[automerger skipped] DO NOT MERGE Add genrule to build *.latest.version build target. am: 8a7dad00c2 -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/build/soong/+/23638331
Change-Id: I5d87e013957af1cb79476a1e4cf291e3281485bc
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.gitignore b/.gitignore
index a09c56d..5d2bc0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
/.idea
+*.iml
+*.ipr
+*.iws
+
diff --git a/OWNERS b/OWNERS
index 0662016..964e27a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,7 +4,6 @@
# AMER
agespino@google.com
alexmarquez@google.com
-asmundak@google.com
ccross@android.com
colefaust@google.com
cparsons@google.com
@@ -14,6 +13,7 @@
eakammer@google.com
jobredeaux@google.com
joeo@google.com
+juu@google.com
lamontjones@google.com
spandandas@google.com
tradical@google.com
@@ -26,6 +26,4 @@
jingwen@google.com
# EMEA
-hansson@google.com
lberki@google.com
-paulduffin@google.com
diff --git a/README.md b/README.md
index caffd3d..70311cb 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,39 @@
# Soong
-Soong is the replacement for the old Android make-based build system. It
-replaces Android.mk files with Android.bp files, which are JSON-like simple
-declarative descriptions of modules to build.
+Soong is one of the build systems used in Android. There are altogether three:
+* The legacy Make-based build system that is controlled by files called
+ `Android.mk`.
+* Soong, which is controlled by files called `Android.bp`.
+* The upcoming Bazel-based build system that is controlled by files called
+ `BUILD.bazel`.
+
+`Android.bp` file are JSON-like declarative descriptions of "modules" to build;
+a "module" is the basic unit of building that Soong understands, similarly to
+how "target" is the basic unit of building for Bazel (and Make, although the
+two kinds of "targets" are very different)
See [Simple Build
Configuration](https://source.android.com/compatibility/tests/development/blueprints)
on source.android.com to read how Soong is configured for testing.
+### Contributing
+
+Code reviews are handled through the usual code review system of Android,
+available [here](https://android-review.googlesource.com/dashboard/self).
+
+For simple changes (fixing typos, obvious optimizations, etc.), sending a code
+review request is enough. For more substantial changes, file a bug in our
+[bug tracker](https://issuetracker.google.com/issues/new?component=381517) or
+or write us at android-building@googlegroups.com .
+
+For Googlers, see our [internal documentation](http://go/soong).
+
## Android.bp file format
By design, Android.bp files are very simple. There are no conditionals or
control flow statements - any complexity is handled in build logic written in
Go. The syntax and semantics of Android.bp files are intentionally similar
-to [Bazel BUILD files](https://www.bazel.io/versions/master/docs/be/overview.html)
-when possible.
+to [Bazel BUILD files](https://bazel.build/concepts/build-files) when possible.
### Modules
@@ -86,31 +105,35 @@
### Types
-Variables and properties are strongly typed, variables dynamically based on the
-first assignment, and properties statically by the module type. The supported
-types are:
+Variables and properties are strongly typed. Variables are dynamically typed
+based on the first assignment, and properties are statically typed by the
+module type. The supported types are:
* Bool (`true` or `false`)
* Integers (`int`)
* Strings (`"string"`)
* Lists of strings (`["string1", "string2"]`)
* Maps (`{key1: "value1", key2: ["value2"]}`)
-Maps may values of any type, including nested maps. Lists and maps may have
-trailing commas after the last value.
+Maps may contain values of any type, including nested maps. Lists and maps may
+have trailing commas after the last value.
Strings can contain double quotes using `\"`, for example `"cat \"a b\""`.
### Operators
-Strings, lists of strings, and maps can be appended using the `+` operator.
-Integers can be summed up using the `+` operator. Appending a map produces the
-union of keys in both maps, appending the values of any keys that are present
-in both maps.
+The `+` operator:
+* Sums integers.
+* Concatenates strings and lists.
+* Produces the union of maps.
+
+Concatenating maps produces a map whose keys are the union of the given maps'
+keys, and whose mapped values are the union of the given maps' corresponding
+mapped values.
### Defaults modules
-A defaults module can be used to repeat the same properties in multiple modules.
-For example:
+A `defaults` module can be used to repeat the same properties in multiple
+modules. For example:
```
cc_defaults {
@@ -165,7 +188,7 @@
```
package {
- default_visibility: [":__subpackages"]
+ default_visibility: [":__subpackages__"]
}
```
@@ -590,27 +613,44 @@
Content Root, then add the `build/blueprint` directory.
* Optional: also add the `external/golang-protobuf` directory. In practice,
IntelliJ seems to work well enough without this, too.
+
### Running Soong in a debugger
+Both the Android build driver (`soong_ui`) and Soong proper (`soong_build`) are
+Go applications and can be debugged with the help of the standard Go debugger
+called Delve. A client (e.g., IntelliJ IDEA) communicates with Delve via IP port
+that Delve listens to (the port number is passed to it on invocation).
+
+#### Debugging Android Build Driver ####
+To make `soong_ui` wait for a debugger connection, use the `SOONG_UI_DELVE`
+variable:
+
+```
+SOONG_UI_DELVE=5006 m nothing
+```
+
+#### Debugging Soong Proper ####
+
To make `soong_build` wait for a debugger connection, install `dlv` and then
start the build with `SOONG_DELVE=<listen addr>` in the environment.
For example:
```bash
-SOONG_DELVE=:5006 m nothing
+SOONG_DELVE=5006 m nothing
```
-
-To make `soong_ui` wait for a debugger connection, use the `SOONG_UI_DELVE`
-variable:
-
+Android build driver invokes `soong_build` multiple times, and by default each
+invocation is run in the debugger. Setting `SOONG_DELVE_STEPS` controls which
+invocations are run in the debugger, e.g., running
+```bash
+SOONG_DELVE=2345 SOONG_DELVE_STEPS='build,modulegraph' m
```
-SOONG_UI_DELVE=:5006 m nothing
-```
+results in only `build` (main build step) and `modulegraph` being run in the debugger.
+The allowed step names are `api_bp2build`, `bp2build_files`, `bp2build_workspace`,
+`build`, `modulegraph`, `queryview`, `soong_docs`.
-
-setting or unsetting `SOONG_DELVE` causes a recompilation of `soong_build`. This
+Note setting or unsetting `SOONG_DELVE` causes a recompilation of `soong_build`. This
is because in order to debug the binary, it needs to be built with debug
symbols.
-
+#### Delve Troubleshooting ####
To test the debugger connection, run this command:
```
@@ -629,15 +669,23 @@
sudo sysctl -w kernel.yama.ptrace_scope=0
```
+#### IntelliJ Setup ####
To connect to the process using IntelliJ:
* Run -> Edit Configurations...
* Choose "Go Remote" on the left
* Click on the "+" buttion on the top-left
-* Give it a nice name and set "Host" to localhost and "Port" to the port in the
- environment variable
+* Give it a nice _name_ and set "Host" to `localhost` and "Port" to the port in the
+ environment variable (`SOONG_UI_DELVE` for `soong_ui`, `SOONG_DELVE` for
+ `soong_build`)
+* Set the breakpoints where you want application to stop
+* Run the build from the command line
+* In IntelliJ, click Run -> Debug _name_
+* Observe _Connecting..._ message in the debugger pane. It changes to
+ _Connected_ once the communication with the debugger has been established; the
+ terminal window where the build started will display
+ `API server listening at ...` message
-Debugging works far worse than debugging Java, but is sometimes useful.
Sometimes the `dlv` process hangs on connection. A symptom of this is `dlv`
spinning a core or two. In that case, `kill -9` `dlv` and try again.
diff --git a/android/Android.bp b/android/Android.bp
index c0a35ac..641c438 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -30,6 +30,7 @@
srcs: [
"androidmk.go",
"apex.go",
+ "api_domain.go",
"api_levels.go",
"arch.go",
"arch_list.go",
@@ -38,7 +39,9 @@
"bazel_paths.go",
"buildinfo_prop.go",
"config.go",
+ "test_config.go",
"config_bp2build.go",
+ "configured_jars.go",
"csuite_config.go",
"deapexer.go",
"defaults.go",
@@ -88,6 +91,7 @@
"test_asserts.go",
"test_suites.go",
"testing.go",
+ "updatable_modules.go",
"util.go",
"variable.go",
"visibility.go",
@@ -98,6 +102,7 @@
"apex_test.go",
"arch_test.go",
"bazel_handler_test.go",
+ "bazel_paths_test.go",
"bazel_test.go",
"config_test.go",
"config_bp2build_test.go",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 4fd4f13..63d4e11 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -33,114 +33,254 @@
// all modules in this package (not recursively) default to bp2build_available: false.
// allows modules to opt-in.
Bp2BuildDefaultFalse
+
+ // all modules in this package and subpackages default to bp2build_available: false.
+ // allows modules to opt-in.
+ Bp2BuildDefaultFalseRecursively
+
+ DEFAULT_NINJA_WEIGHT = 1000
)
var (
Bp2buildDefaultConfig = Bp2BuildConfig{
+ "art": Bp2BuildDefaultTrue,
+ "art/libartbase": Bp2BuildDefaultTrueRecursively,
"art/libartpalette": Bp2BuildDefaultTrueRecursively,
"art/libdexfile": Bp2BuildDefaultTrueRecursively,
+ "art/libnativebridge": Bp2BuildDefaultTrueRecursively,
"art/runtime": Bp2BuildDefaultTrueRecursively,
"art/tools": Bp2BuildDefaultTrue,
"bionic": Bp2BuildDefaultTrueRecursively,
+ "bootable/recovery/applypatch": Bp2BuildDefaultTrue,
+ "bootable/recovery/minadbd": Bp2BuildDefaultTrue,
+ "bootable/recovery/minui": Bp2BuildDefaultTrue,
+ "bootable/recovery/recovery_utils": Bp2BuildDefaultTrue,
"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/make/target/product/security": 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/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/error_prone": Bp2BuildDefaultTrueRecursively,
- "external/fmtlib": Bp2BuildDefaultTrueRecursively,
- "external/google-benchmark": Bp2BuildDefaultTrueRecursively,
- "external/googletest": Bp2BuildDefaultTrueRecursively,
- "external/gwp_asan": Bp2BuildDefaultTrueRecursively,
- "external/icu": Bp2BuildDefaultTrueRecursively,
- "external/icu/android_icu4j": Bp2BuildDefaultFalse, // java rules incomplete
- "external/icu/icu4j": Bp2BuildDefaultFalse, // java rules incomplete
- "external/javapoet": 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,
+
+ "build/bazel": Bp2BuildDefaultTrueRecursively,
+ "build/make/target/product/security": Bp2BuildDefaultTrue,
+ "build/make/tools/protos": Bp2BuildDefaultTrue,
+ "build/make/tools/releasetools": Bp2BuildDefaultTrue,
+ "build/make/tools/sbom": 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/licenses": Bp2BuildDefaultTrue,
+ "build/soong/linkerconfig": Bp2BuildDefaultTrueRecursively,
+ "build/soong/scripts": Bp2BuildDefaultTrueRecursively,
+
+ "cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively,
+
+ "dalvik/tools/dexdeps": 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": Bp2BuildDefaultTrue,
+ "external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively,
+ "external/auto/common": Bp2BuildDefaultTrueRecursively,
+ "external/auto/service": Bp2BuildDefaultTrueRecursively,
+ "external/boringssl": Bp2BuildDefaultTrueRecursively,
+ "external/bouncycastle": Bp2BuildDefaultTrue,
+ "external/brotli": Bp2BuildDefaultTrue,
+ "external/bsdiff": Bp2BuildDefaultTrueRecursively,
+ "external/bzip2": Bp2BuildDefaultTrueRecursively,
+ "external/conscrypt": Bp2BuildDefaultTrue,
+ "external/e2fsprogs": Bp2BuildDefaultTrueRecursively,
+ "external/eigen": Bp2BuildDefaultTrueRecursively,
+ "external/erofs-utils": Bp2BuildDefaultTrueRecursively,
+ "external/error_prone": Bp2BuildDefaultTrueRecursively,
+ "external/escapevelocity": Bp2BuildDefaultTrueRecursively,
+ "external/expat": Bp2BuildDefaultTrueRecursively,
+ "external/f2fs-tools": Bp2BuildDefaultTrue,
+ "external/flac": Bp2BuildDefaultTrueRecursively,
+ "external/fmtlib": Bp2BuildDefaultTrueRecursively,
+ "external/guava": 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/jacoco": Bp2BuildDefaultTrueRecursively,
+ "external/jarjar": Bp2BuildDefaultTrueRecursively,
+ "external/javaparser": Bp2BuildDefaultTrueRecursively,
+ "external/javapoet": Bp2BuildDefaultTrueRecursively,
+ "external/javassist": Bp2BuildDefaultTrueRecursively,
+ "external/jemalloc_new": Bp2BuildDefaultTrueRecursively,
+ "external/jsoncpp": Bp2BuildDefaultTrueRecursively,
+ "external/jsr305": Bp2BuildDefaultTrueRecursively,
+ "external/jsr330": Bp2BuildDefaultTrueRecursively,
+ "external/junit": Bp2BuildDefaultTrueRecursively,
+ "external/kotlinc": Bp2BuildDefaultTrueRecursively,
+ "external/libaom": Bp2BuildDefaultTrueRecursively,
+ "external/libavc": Bp2BuildDefaultTrueRecursively,
+ "external/libcap": Bp2BuildDefaultTrueRecursively,
+ "external/libcxx": Bp2BuildDefaultTrueRecursively,
+ "external/libcxxabi": Bp2BuildDefaultTrueRecursively,
+ "external/libdivsufsort": Bp2BuildDefaultTrueRecursively,
+ "external/libdrm": Bp2BuildDefaultTrue,
+ "external/libevent": Bp2BuildDefaultTrueRecursively,
+ "external/libgav1": Bp2BuildDefaultTrueRecursively,
+ "external/libhevc": Bp2BuildDefaultTrueRecursively,
+ "external/libjpeg-turbo": Bp2BuildDefaultTrueRecursively,
+ "external/libmpeg2": Bp2BuildDefaultTrueRecursively,
+ "external/libpng": Bp2BuildDefaultTrueRecursively,
+ "external/libvpx": Bp2BuildDefaultTrueRecursively,
+ "external/libyuv": Bp2BuildDefaultTrueRecursively,
+ "external/lz4/lib": Bp2BuildDefaultTrue,
+ "external/lz4/programs": Bp2BuildDefaultTrue,
+ "external/lzma/C": Bp2BuildDefaultTrueRecursively,
+ "external/mdnsresponder": Bp2BuildDefaultTrueRecursively,
+ "external/minijail": Bp2BuildDefaultTrueRecursively,
+ "external/musl": Bp2BuildDefaultTrueRecursively,
+ "external/objenesis": Bp2BuildDefaultTrueRecursively,
+ "external/openscreen": Bp2BuildDefaultTrueRecursively,
+ "external/ow2-asm": 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/speex": Bp2BuildDefaultTrueRecursively,
+ "external/tinyalsa": Bp2BuildDefaultTrueRecursively,
+ "external/tinyalsa_new": Bp2BuildDefaultTrueRecursively,
+ "external/toybox": Bp2BuildDefaultTrueRecursively,
+ "external/zlib": Bp2BuildDefaultTrueRecursively,
+ "external/zopfli": Bp2BuildDefaultTrueRecursively,
+ "external/zstd": Bp2BuildDefaultTrueRecursively,
+
+ "frameworks/av": Bp2BuildDefaultTrue,
+ "frameworks/av/media/codec2/components/aom": Bp2BuildDefaultTrueRecursively,
+ "frameworks/av/media/codecs": Bp2BuildDefaultTrueRecursively,
+ "frameworks/av/media/liberror": Bp2BuildDefaultTrueRecursively,
+ "frameworks/av/media/libshmem": Bp2BuildDefaultTrueRecursively,
+ "frameworks/av/media/audioaidlconversion": Bp2BuildDefaultTrueRecursively,
+ "frameworks/av/media/module/minijail": Bp2BuildDefaultTrueRecursively,
+ "frameworks/av/services/minijail": Bp2BuildDefaultTrueRecursively,
+ "frameworks/base/libs/androidfw": Bp2BuildDefaultTrue,
"frameworks/base/media/tests/MediaDump": Bp2BuildDefaultTrue,
+ "frameworks/base/services/tests/servicestests/aidl": Bp2BuildDefaultTrue,
+ "frameworks/base/proto": Bp2BuildDefaultTrue,
"frameworks/base/startop/apps/test": Bp2BuildDefaultTrue,
"frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
+ "frameworks/base/tools/aapt2": Bp2BuildDefaultTrue,
+ "frameworks/base/tools/codegen": Bp2BuildDefaultTrueRecursively,
+ "frameworks/base/tools/streaming_proto": Bp2BuildDefaultTrueRecursively,
+ "frameworks/hardware/interfaces/stats/aidl": Bp2BuildDefaultTrue,
"frameworks/native/libs/adbd_auth": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/libs/arect": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/libs/gui": Bp2BuildDefaultTrue,
+ "frameworks/native/libs/math": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/libs/nativebase": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/libs/vr": 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,
+ "frameworks/native/libs/permission": Bp2BuildDefaultTrue,
+ "frameworks/native/services/batteryservice": Bp2BuildDefaultTrue,
+ "frameworks/proto_logging/stats": Bp2BuildDefaultTrueRecursively,
+
+ "hardware/interfaces": Bp2BuildDefaultTrue,
+ "hardware/interfaces/audio/aidl": Bp2BuildDefaultTrue,
+ "hardware/interfaces/audio/aidl/common": Bp2BuildDefaultTrue,
+ "hardware/interfaces/audio/aidl/default": Bp2BuildDefaultTrue,
+ "hardware/interfaces/audio/aidl/sounddose": Bp2BuildDefaultTrue,
+ "hardware/interfaces/common/aidl": Bp2BuildDefaultTrue,
+ "hardware/interfaces/common/fmq/aidl": Bp2BuildDefaultTrue,
+ "hardware/interfaces/common/support": Bp2BuildDefaultTrue,
+ "hardware/interfaces/configstore/1.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/configstore/1.1": Bp2BuildDefaultTrue,
+ "hardware/interfaces/configstore/utils": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/allocator/2.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/allocator/3.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/allocator/4.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/allocator/aidl": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/bufferqueue/1.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/bufferqueue/2.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/common/1.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/common/1.1": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/common/1.2": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/common/aidl": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/mapper/2.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/mapper/2.1": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/mapper/3.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/graphics/mapper/4.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/health/1.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/health/1.0/default": Bp2BuildDefaultTrue,
+ "hardware/interfaces/health/2.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/health/2.0/default": Bp2BuildDefaultTrue,
+ "hardware/interfaces/health/2.0/utils": Bp2BuildDefaultTrueRecursively,
+ "hardware/interfaces/health/2.1": Bp2BuildDefaultTrue,
+ "hardware/interfaces/health/aidl": Bp2BuildDefaultTrue,
+ "hardware/interfaces/health/utils": Bp2BuildDefaultTrueRecursively,
+ "hardware/interfaces/media/1.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/media/bufferpool": Bp2BuildDefaultTrueRecursively,
+ "hardware/interfaces/media/bufferpool/aidl/default/tests": Bp2BuildDefaultFalseRecursively,
+ "hardware/interfaces/media/c2/1.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/media/c2/1.1": Bp2BuildDefaultTrue,
+ "hardware/interfaces/media/c2/1.2": Bp2BuildDefaultTrue,
+ "hardware/interfaces/media/omx/1.0": Bp2BuildDefaultTrue,
+ "hardware/interfaces/neuralnetworks": Bp2BuildDefaultTrueRecursively,
+ "hardware/interfaces/neuralnetworks/aidl/vts": Bp2BuildDefaultFalseRecursively,
+ "hardware/interfaces/neuralnetworks/1.0/vts": Bp2BuildDefaultFalseRecursively,
+ "hardware/interfaces/neuralnetworks/1.1/vts": Bp2BuildDefaultFalseRecursively,
+ "hardware/interfaces/neuralnetworks/1.2/vts": Bp2BuildDefaultFalseRecursively,
+ "hardware/interfaces/neuralnetworks/1.3/vts": Bp2BuildDefaultFalseRecursively,
+ "hardware/interfaces/neuralnetworks/1.4/vts": Bp2BuildDefaultFalseRecursively,
+
+ "libnativehelper": Bp2BuildDefaultTrueRecursively,
+
"packages/apps/DevCamera": Bp2BuildDefaultTrue,
"packages/apps/HTMLViewer": Bp2BuildDefaultTrue,
"packages/apps/Protips": Bp2BuildDefaultTrue,
+ "packages/apps/SafetyRegulatoryInfo": Bp2BuildDefaultTrue,
+ "packages/apps/WallpaperPicker": Bp2BuildDefaultTrue,
+ "packages/modules/NeuralNetworks/driver/cache": Bp2BuildDefaultTrueRecursively,
"packages/modules/StatsD/lib/libstatssocket": Bp2BuildDefaultTrueRecursively,
"packages/modules/adb": Bp2BuildDefaultTrue,
"packages/modules/adb/apex": Bp2BuildDefaultTrue,
@@ -150,93 +290,272 @@
"packages/modules/adb/pairing_connection": Bp2BuildDefaultTrueRecursively,
"packages/modules/adb/proto": Bp2BuildDefaultTrueRecursively,
"packages/modules/adb/tls": Bp2BuildDefaultTrueRecursively,
- "packages/providers/MediaProvider/tools/dialogs": Bp2BuildDefaultTrue,
+ "packages/modules/Gki/libkver": Bp2BuildDefaultTrue,
+ "packages/modules/NetworkStack/common/captiveportal": Bp2BuildDefaultTrue,
+ "packages/modules/NeuralNetworks/apex": Bp2BuildDefaultTrue,
+ "packages/providers/MediaProvider/tools/dialogs": Bp2BuildDefaultFalse, // TODO(b/242834374)
"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,
+ "packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultFalse, // TODO(b/242834321)
+
+ "platform_testing/tests/example": Bp2BuildDefaultTrueRecursively,
+
+ "prebuilts/clang/host/linux-x86": Bp2BuildDefaultTrueRecursively,
+ "prebuilts/gradle-plugin": Bp2BuildDefaultTrueRecursively,
+ "prebuilts/runtime/mainline/platform/sdk": Bp2BuildDefaultTrueRecursively,
+ "prebuilts/sdk/current/androidx": Bp2BuildDefaultTrue,
+ "prebuilts/sdk/current/androidx-legacy": Bp2BuildDefaultTrue,
+ "prebuilts/sdk/current/extras/constraint-layout-x": Bp2BuildDefaultTrue,
+ "prebuilts/sdk/current/extras/material-design-x": Bp2BuildDefaultTrue,
+ "prebuilts/sdk/current/extras/app-toolkit": Bp2BuildDefaultTrue,
+ "prebuilts/sdk/current/support": Bp2BuildDefaultTrue,
+ "prebuilts/tools": Bp2BuildDefaultTrue,
+ "prebuilts/tools/common/m2": Bp2BuildDefaultTrue,
+
+ "sdk/dumpeventlog": Bp2BuildDefaultTrue,
+ "sdk/eventanalyzer": 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/healthd": Bp2BuildDefaultTrue,
+ "system/core/healthd/testdata": Bp2BuildDefaultTrue,
+ "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/libsuspend": Bp2BuildDefaultTrue,
+ "system/core/libsystem": Bp2BuildDefaultTrueRecursively,
+ "system/core/libsysutils": Bp2BuildDefaultTrueRecursively,
+ "system/core/libutils": Bp2BuildDefaultTrueRecursively,
+ "system/core/libvndksupport": Bp2BuildDefaultTrueRecursively,
+ "system/core/mkbootfs": Bp2BuildDefaultTrueRecursively,
+ "system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
+ "system/core/property_service/libpropertyinfoserializer": Bp2BuildDefaultTrueRecursively,
+ "system/extras/toolchain-extras": Bp2BuildDefaultTrue,
+ "system/hardware/interfaces/media": Bp2BuildDefaultTrueRecursively,
+ "system/incremental_delivery/incfs": Bp2BuildDefaultTrue,
+ "system/libartpalette": Bp2BuildDefaultTrueRecursively,
+ "system/libbase": Bp2BuildDefaultTrueRecursively,
+ "system/libfmq": Bp2BuildDefaultTrue,
+ "system/libhidl/libhidlmemory": Bp2BuildDefaultTrue,
+ "system/libhidl/transport": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/allocator/1.0": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/base/1.0": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/manager/1.0": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/manager/1.1": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/manager/1.2": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/memory/1.0": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/memory/token/1.0": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/safe_union/1.0": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/token/1.0": Bp2BuildDefaultTrue,
+ "system/libhidl/transport/token/1.0/utils": Bp2BuildDefaultTrue,
+ "system/libhwbinder": Bp2BuildDefaultTrueRecursively,
+ "system/libprocinfo": Bp2BuildDefaultTrue,
+ "system/libvintf": Bp2BuildDefaultTrue,
+ "system/libziparchive": Bp2BuildDefaultTrueRecursively,
+ "system/logging": Bp2BuildDefaultTrueRecursively,
+ "system/media": Bp2BuildDefaultTrue,
+ "system/media/audio": Bp2BuildDefaultTrueRecursively,
+ "system/media/alsa_utils": Bp2BuildDefaultTrueRecursively,
+ "system/media/audio_utils": Bp2BuildDefaultTrueRecursively,
+ "system/memory/libion": Bp2BuildDefaultTrueRecursively,
+ "system/memory/libmemunreachable": Bp2BuildDefaultTrueRecursively,
+ "system/sepolicy/apex": Bp2BuildDefaultTrueRecursively,
+ "system/testing/gtest_extras": Bp2BuildDefaultTrueRecursively,
+ "system/timezone/apex": Bp2BuildDefaultTrueRecursively,
+ "system/timezone/output_data": Bp2BuildDefaultTrueRecursively,
+ "system/timezone/testdata": Bp2BuildDefaultTrueRecursively,
+ "system/timezone/testing": Bp2BuildDefaultTrueRecursively,
+ "system/tools/aidl/build/tests_bp2build": Bp2BuildDefaultTrue,
+ "system/tools/aidl/metadata": Bp2BuildDefaultTrue,
+ "system/tools/hidl/metadata": Bp2BuildDefaultTrue,
+ "system/tools/mkbootimg": Bp2BuildDefaultTrueRecursively,
+ "system/tools/sysprop": Bp2BuildDefaultTrue,
+ "system/tools/xsdc/utils": Bp2BuildDefaultTrueRecursively,
+ "system/unwinding/libunwindstack": Bp2BuildDefaultTrueRecursively,
+
+ "tools/apifinder": Bp2BuildDefaultTrue,
+ "tools/apksig": Bp2BuildDefaultTrue,
+ "tools/external_updater": Bp2BuildDefaultTrueRecursively,
+ "tools/metalava": Bp2BuildDefaultTrue,
+ "tools/platform-compat/java/android/compat": Bp2BuildDefaultTrueRecursively,
+ "tools/tradefederation/prebuilts/filegroups": 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":/* recursive = */ true,
+ "build/make/core":/* recursive = */ false,
"build/bazel_common_rules":/* recursive = */ true,
+ "build/make/target/product/security":/* recursive = */ false,
// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
"build/make/tools":/* recursive = */ false,
"build/pesto":/* recursive = */ true,
+ "build/soong":/* 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/bazelbuild-rules_license":/* recursive = */ true,
+ "external/bazelbuild-kotlin-rules":/* recursive = */ true,
"external/bazel-skylib":/* recursive = */ true,
- "external/guava":/* recursive = */ true,
- "external/jsr305":/* recursive = */ true,
- "frameworks/ex/common":/* recursive = */ true,
+ "external/protobuf":/* recursive = */ false,
+ "external/python/absl-py":/* recursive = */ true,
+ "external/compiler-rt/lib/cfi":/* recursive = */ false,
+
+ // this BUILD file is globbed by //external/icu/icu4c/source:icu4c_test_data's "data/**/*".
+ "external/icu/icu4c/source/data/unidata/norm2":/* recursive = */ false,
+
+ // Building manually due to b/179889880: resource files cross package boundary
"packages/apps/Music":/* recursive = */ true,
- "packages/apps/QuickSearchBox":/* recursive = */ true,
- "packages/apps/WallpaperPicker":/* recursive = */ false,
+ "prebuilts/abi-dumps/platform":/* recursive = */ true,
+ "prebuilts/abi-dumps/ndk":/* recursive = */ true,
+ "prebuilts/bazel":/* recursive = */ true,
"prebuilts/bundletool":/* recursive = */ true,
+ "prebuilts/clang/host/linux-x86":/* recursive = */ false,
+ "prebuilts/clang-tools":/* recursive = */ true,
"prebuilts/gcc":/* recursive = */ true,
- "prebuilts/build-tools":/* recursive = */ false,
- "prebuilts/jdk/jdk11":/* recursive = */ false,
+ "prebuilts/build-tools":/* recursive = */ true,
+ "prebuilts/jdk/jdk17":/* recursive = */ true,
+ "prebuilts/misc":/* recursive = */ false, // not recursive because we need bp2build converted build files in prebuilts/misc/common/asm
"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,
+ "prebuilts/runtime":/* recursive = */ false,
+
+ // not recursive due to conflicting workspace paths in tools/atest/bazel/rules
+ "tools/asuite/atest":/* recursive = */ false,
+ "tools/asuite/atest/bazel/reporter":/* recursive = */ true,
+
+ // TODO(b/266459895): remove this and the placeholder BUILD file after re-enabling libunwindstack
+ "external/rust/crates/rustc-demangle-capi":/* recursive = */ false,
}
Bp2buildModuleAlwaysConvertList = []string{
+ "libidmap2_policies",
+ "libSurfaceFlingerProp",
+ // cc mainline modules
+ "code_coverage.policy",
+ "code_coverage.policy.other",
+ "codec2_soft_exports",
+ "codecs_g711dec",
+ "com.android.media.swcodec",
+ "com.android.media.swcodec-androidManifest",
+ "com.android.media.swcodec-ld.config.txt",
+ "com.android.media.swcodec-mediaswcodec.32rc",
+ "com.android.media.swcodec-mediaswcodec.rc",
+ "com.android.media.swcodec.certificate",
+ "com.android.media.swcodec.key",
+ "flatbuffer_headers",
+ "framework-connectivity-protos",
+ "gemmlowp_headers",
+ "gl_headers",
+ "libandroid_runtime_lazy",
+ "libandroid_runtime_vm_headers",
+ "libaudioclient_aidl_conversion_util",
+ "libbinder",
+ "libbinder_device_interface_sources",
+ "libbinder_aidl",
+ "libbinder_headers",
+ "libbinder_headers_platform_shared",
+ "libbinderthreadstateutils",
+ "libbluetooth-types-header",
+ "libcodec2",
+ "libcodec2_headers",
+ "libcodec2_internal",
+ "libdmabufheap",
+ "libgsm",
+ "libgrallocusage",
+ "libgralloctypes",
+ "libnativewindow",
+ "libneuralnetworks",
+ "libneuralnetworks_static",
+ "libgraphicsenv",
+ "libhardware",
+ "libhardware_headers",
+ "libnativeloader-headers",
+ "libnativewindow_headers",
+ "libneuralnetworks_headers",
+ "libneuralnetworks_packageinfo",
+ "libopus",
+ "libprocpartition",
+ "libruy_static",
+ "libandroidio",
+ "libandroidio_srcs",
+ "libserviceutils",
+ "libstagefright_amrnbenc",
+ "libstagefright_amrnbdec",
+ "libstagefright_amrwbdec",
+ "libstagefright_amrwbenc",
+ "libstagefright_amrnb_common",
+ "libstagefright_enc_common",
+ "libstagefright_flacdec",
+ "libstagefright_foundation",
+ "libstagefright_foundation_headers",
+ "libstagefright_headers",
+ "libstagefright_m4vh263dec",
+ "libstagefright_m4vh263enc",
+ "libstagefright_mp3dec",
+ "libstagefright_mp3dec_headers",
+ "libsurfaceflinger_headers",
+ "libsync",
+ "libtextclassifier_hash_headers",
+ "libtextclassifier_hash_static",
+ "libtflite_kernel_utils",
+ "libtinyxml2",
+ "libui",
+ "libui-types",
+ "libui_headers",
+ "libvorbisidec",
+ "media_ndk_headers",
+ "media_plugin_headers",
+ "mediaswcodec.policy",
+ "mediaswcodec.xml",
+ "neuralnetworks_types",
+ "libneuralnetworks_common",
+ // packagemanager_aidl_interface is created implicitly in packagemanager_aidl module
+ "packagemanager_aidl_interface",
+ "philox_random",
+ "philox_random_headers",
+ "server_configurable_flags",
+ "service-permission-streaming-proto-sources",
+ "statslog_neuralnetworks.cpp",
+ "statslog_neuralnetworks.h",
+ "tensorflow_headers",
+
+ "libstagefright_bufferpool@2.0",
+ "libstagefright_bufferpool@2.0.1",
+ "libSurfaceFlingerProp",
+
+ // prebuilts
+ "prebuilt_stats-log-api-gen",
+
+ // fastboot
+ "fastboot",
+ "libfastboot",
+ "liblp",
+ "libstorage_literals_headers",
+
//external/avb
"avbtool",
"libavb",
"avb_headers",
+ //external/libxml2
+ "xmllint",
+ "libxml2",
+
//external/fec
"libfec_rs",
@@ -245,6 +564,7 @@
//system/extras/ext4_utils
"libext4_utils",
+ "mke2fs_conf",
//system/extras/libfec
"libfec",
@@ -254,6 +574,7 @@
//system/extras/verity/fec
"fec",
+ "boot_signer",
//packages/apps/Car/libs/car-ui-lib/car-ui-androidx
// genrule dependencies for java_imports
@@ -262,120 +583,860 @@
"car-ui-androidx-core-common-nodeps",
"car-ui-androidx-lifecycle-common-nodeps",
"car-ui-androidx-constraintlayout-solver-nodeps",
+
+ //system/libhidl
+ "libhidlbase", // needed by cc_hidl_library
+ "libhidl_gtest_helper",
+
+ //frameworks/native
+ "framework_native_aidl_binder",
+ "framework_native_aidl_gui",
+
+ //frameworks/native/libs/input
+ "inputconstants_aidl",
+
+ // needed for aidl_interface's ndk backend
+ "libbinder_ndk",
+
+ "libusb",
+
+ // needed by liblogd
+ "ILogcatManagerService_aidl",
+ "libincremental_aidl-cpp",
+ "incremental_aidl",
+
+ //frameworks/native/cmds/cmd
+ "libcmd",
+
+ //system/core/fs_mgr/libdm
+ "libdm",
+
+ //system/core/fs_mgr/libfiemap
+ "libfiemap_headers",
+ "libfiemap_passthrough_srcs",
+ "libfiemap_srcs",
+
+ //system/gsid
+ "libgsi",
+ "libgsi_headers",
+
+ //system/core/libkeyutils
+ "libkeyutils",
+
+ //bootable/recovery/otautil
+ "libotautil",
+
+ //system/vold
+ "libvold_headers",
+
+ //system/extras/libfscrypt
+ "libfscrypt",
+
+ //system/core/fs_mgr
+ "libfstab",
+
+ //bootable/recovery/fuse_sideload
+ "libfusesideload",
+
+ //system/core/fs_mgr/libfs_avb
+ "libfs_avb",
+
+ //system/core/fs_mgr
+ "libfs_mgr",
+
+ "libcodec2_hidl@1.0",
+ "libcodec2_hidl@1.1",
+ "libcodec2_hidl@1.2",
+ "libcodec2_hidl_plugin_stub",
+ "libcodec2_hidl_plugin",
+ "libstagefright_bufferqueue_helper_novndk",
+ "libGLESv2",
+ "libEGL",
+ "libcodec2_vndk",
+ "libnativeloader_lazy",
+ "libnativeloader",
+ "libEGL_getProcAddress",
+ "libEGL_blobCache",
+
+ "mediaswcodec",
+ "libmedia_headers",
+ "libmedia_codecserviceregistrant",
+ "libsfplugin_ccodec_utils",
+ "libcodec2_soft_aacenc",
+ "libcodec2_soft_amrnbdec",
+ "libcodec2_soft_amrnbenc",
+ "libcodec2_soft_amrwbdec",
+ "libcodec2_soft_amrwbenc",
+ "libcodec2_soft_hevcdec",
+ "libcodec2_soft_hevcenc",
+ "libcodec2_soft_g711alawdec",
+ "libcodec2_soft_g711mlawdec",
+ "libcodec2_soft_mpeg2dec",
+ "libcodec2_soft_h263dec",
+ "libcodec2_soft_h263enc",
+ "libcodec2_soft_mpeg4dec",
+ "libcodec2_soft_mpeg4enc",
+ "libcodec2_soft_mp3dec",
+ "libcodec2_soft_vorbisdec",
+ "libcodec2_soft_opusdec",
+ "libcodec2_soft_opusenc",
+ "libcodec2_soft_vp8dec",
+ "libcodec2_soft_vp9dec",
+ "libcodec2_soft_av1dec_gav1",
+ "libcodec2_soft_vp8enc",
+ "libcodec2_soft_vp9enc",
+ "libcodec2_soft_rawdec",
+ "libcodec2_soft_flacdec",
+ "libcodec2_soft_flacenc",
+ "libcodec2_soft_gsmdec",
+ "libcodec2_soft_avcdec",
+ "libcodec2_soft_avcenc",
+ "libcodec2_soft_aacdec",
+ "libcodec2_soft_common",
+
+ // kotlin srcs in java libs
+ "kotlinx_atomicfu",
+
+ // kotlin srcs in java binary
+ "AnalyzerKt",
+ "trebuchet-core",
+
+ // kotlin srcs in android_library
+ "renderscript_toolkit",
+
+ //kotlin srcs in android_binary
+ "MusicKotlin",
+
+ // java_library with prebuilt sdk_version
+ "android-common",
+
+ // checked in current.txt for merged_txts
+ "non-updatable-current.txt",
+ "non-updatable-system-current.txt",
+ "non-updatable-module-lib-current.txt",
+ "non-updatable-system-server-current.txt",
+
+ // for api_fingerprint.txt generation
+ "api_fingerprint",
+
+ // allowlisting for kotlinx_coroutines
+ "kotlinx_coroutines",
+ "kotlinx_coroutines-device",
+ "kotlinx_coroutines-host",
+ "annotations",
+ "kotlinx-coroutines-android-annotation-stubs",
+
+ // for building com.android.neuralnetworks
+ "libimapper_stablec",
+ "libimapper_providerutils",
+
+ // min_sdk_version in android_app
+ "CtsShimUpgrade",
+
+ // Mainline Module Apps
+ "CaptivePortalLogin",
}
Bp2buildModuleTypeAlwaysConvertList = []string{
+ "aidl_interface_headers",
+ "bpf",
+ "combined_apis",
+ "license",
+ "linker_config",
"java_import",
"java_import_host",
+ "java_sdk_library",
+ "sysprop_library",
}
+ // Add the names of modules that bp2build should never convert, if it is
+ // in the package allowlist. An error will be thrown if a module must
+ // not be here and in the alwaysConvert lists.
+ //
+ // For prebuilt modules (e.g. android_library_import), remember to add
+ // the "prebuilt_" prefix to the name, so that it's differentiable from
+ // the source versions within Soong's module graph.
Bp2buildModuleDoNotConvertList = []string{
+ // Depends on unconverted libandroid, libgui
+ "dvr_buffer_queue-test",
+ "dvr_display-test",
+ // Depends on unconverted libchrome
+ "pdx_benchmarks",
+ "buffer_hub_queue-test",
+ "buffer_hub_queue_producer-test",
+
// cc bugs
- "libsepol", // TODO(b/207408632): Unsupported case of .l sources in cc library rules
- "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
+ "libactivitymanager_aidl", // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library
+
+ // TODO(b/198619163) module has same name as source
+ "logtagd.rc",
+
"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
+
+ // TODO(b/202876379): has arch-variant static_executable
+ "linkerconfig",
+ "mdnsd",
+ "libcutils_test_static",
+ "KernelLibcutilsTest",
+
+ "linker", // TODO(b/228316882): cc_binary uses link_crt
+ "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
+ "art_libartbase_headers", // TODO(b/236268577): Header libraries do not support export_shared_libs_headers
+ "apexer_test", // Requires aapt2
+ "apexer_test_host_tools",
+ "host_apex_verifier",
+ "tjbench", // TODO(b/240563612): Stem property
// java bugs
- "libbase_ndk", // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
+ "libbase_ndk", // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
+ "bouncycastle", // TODO(b/274474005): Need support for custom system_modules.
// python protos
- "libprotobuf-python", // TODO(b/196084681): contains .proto sources
- "apex_build_info_proto", "apex_manifest_proto", // TODO(b/196084681): a python lib with proto sources
- "linker_config_proto", // TODO(b/196084681): contains .proto sources
+ "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
+ "prebuilt_car-ui-androidx-core-common", // TODO(b/224773339), genrule dependency creates an .aar, not a .jar
+ // ERROR: The dependencies for the following 1 jar(s) are not complete.
+ // 1.bazel-out/android_target-fastbuild/bin/prebuilts/tools/common/m2/_aar/robolectric-monitor-1.0.2-alpha1/classes_and_libs_merged.jar
+ "prebuilt_robolectric-monitor-1.0.2-alpha1",
// 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_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
+ "analyze_bcpf", // depends on bpmodify a blueprint_go_binary.
+ "analyze_bcpf_test", // depends on bpmodify a blueprint_go_binary.
+ "host_bionic_linker_asm", // depends on extract_linker, a go binary.
+ "host_bionic_linker_script", // depends on extract_linker, a go binary.
+
+ // in cmd attribute of genrule rule //system/timezone/output_data:robolectric_tzdata: label '//system/timezone/output_data:iana/tzdata' in $(location) expression is not a declared prerequisite of this rule
+ "robolectric_tzdata",
// 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
- "chkcon", "sefcontext_compile", // depends on unconverted modules: libsepol
+ "apexer_with_DCLA_preprocessing_test", // depends on unconverted modules: apexer_test_host_tools, com.android.example.apex
+ "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_compression_test", // depends on unconverted modules: soong_zip, com.android.example.apex
+ "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
+ "CarHTMLViewer", // depends on unconverted modules android.car-stubs, car-ui-lib
"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
+ "generated_android_icu4j_resources", // depends on unconverted modules: android_icu4j_srcgen_binary
+ "generated_android_icu4j_test_resources", // depends on unconverted modules: android_icu4j_srcgen_binary
"host-libprotobuf-java-nano", // b/220869005, depends on libprotobuf-java-nano
- "libadb_host", // depends on unconverted modules: AdbWinApi, libopenscreen-discovery, libopenscreen-platform-impl, libusb
+ "jacoco-stubs", // b/245767077, depends on droidstubs
+ "libapexutil", // depends on unconverted modules: apex-info-list-tinyxml
"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
+ "libartbase-art-gtest", // depends on unconverted modules: libgtest_isolated, libart, libart-compiler, libdexfile, libprofile
+ "libartbased-art-gtest", // depends on unconverted modules: libgtest_isolated, libartd, libartd-compiler, libdexfiled, libprofiled
"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", // depends on unconverted module: libdexfile
"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", // depends on unconverted modules: libstatspull, statsd-aidl-ndk
"libstatslog_art", // depends on unconverted modules: statslog_art.cpp, statslog_art.h
"linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_*
+ "malloc-rss-benchmark", // depends on unconverted modules: libmeminfo
"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
+ "releasetools_test", // depends on unconverted modules: com.android.apex.compressed.v1
+ "robolectric-sqlite4java-0.282", // depends on unconverted modules: robolectric-sqlite4java-import, robolectric-sqlite4java-native
+ "static_crasher", // depends on unconverted modules: libdebuggerd_handler
+ "test_fips", // depends on unconverted modules: adb
+ "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",
+ // '//bionic/libc:libc_bp2build_cc_library_static' is duplicated in the 'deps' attribute of rule
+ "toybox-static",
+
+ // aidl files not created
+ "overlayable_policy_aidl_interface",
+
+ //prebuilts/tools/common/m2
+ // depends on //external/okio:okio-lib, which uses kotlin
+ "wire-runtime",
+
+ // depends on adbd_system_api_recovery, which is a unconverted `phony` module type
+ "minadbd",
+
+ // depends on android.hardware.health-V2.0-java
+ "android.hardware.health-translate-java",
+
+ //system/libvintf
+ // depends on apex-info-list-tinyxml, unconverted xsd_config Soong module type.
+ "libvintf",
+ "vintf",
+ "libassemblevintf",
+ "assemble_vintf",
+ "libvintffm",
+ "vintffm",
+ "checkvintf",
+
+ // depends on audio_policy_configuration_aidl_default, xsd_config module.
+ "libaudioserviceexampleimpl",
+ "android.hardware.audio.service-aidl.example",
+
+ // depends on //system/tools/aidl/build:aidl_metadata_json, which is an aidl_interfaces_metadata custom Soong type.
+ "aidl_metadata_in_cpp",
+ "libaidlmetadata",
+ "libaidlmetadata_test",
+
+ // depends on //system/tools/hidl/build:hidl_metadata_json, which is an hidl_interfaces_metadata custom Soong type.
+ "hidl_metadata_in_cpp",
+ "libhidlmetadata",
+ "hidl_metadata_test",
+
+ // cc_test related.
+ // b/274164834 "Could not open Configuration file test.cfg"
+ "svcenc", "svcdec",
+
+ // Failing host cc_tests
+ "memunreachable_unit_test",
+ "libprocinfo_test",
+ "ziparchive-tests",
+ "gtest_isolated_tests",
+ "libunwindstack_unit_test",
+ "task_profiles_test",
+ "power_tests", // failing test on server, but not on host
+
+ // reflect: call of reflect.Value.NumField on interface Value
+ // affects all cc_tests that depend on art_defaults
+ "libnativebridge-tests",
+ "libnativeloader_test",
+ "art_libnativebridge_cts_tests",
+ "art_standalone_libdexfile_external_tests",
+ "art_standalone_libdexfile_support_tests",
+ "libnativebridge-lazy-tests",
+ "libnativebridge-test-case",
+ "libnativebridge2-test-case",
+ "libnativebridge3-test-case",
+ "libnativebridge6-test-case",
+ "libnativebridge6prezygotefork",
+
+ "libandroidfw_tests", "aapt2_tests", // failing due to data path issues
+
+ // cc_test with unconverted deps, or are device-only (and not verified to pass yet)
+ "AMRWBEncTest",
+ "AmrnbDecoderTest", // depends on unconverted modules: libaudioutils, libsndfile
+ "AmrnbEncoderTest", // depends on unconverted modules: libaudioutils, libsndfile
+ "AmrwbDecoderTest", // depends on unconverted modules: libsndfile, libaudioutils
+ "AmrwbEncoderTest", // depends on unconverted modules: libaudioutils, libsndfile
+ "Mp3DecoderTest", // depends on unconverted modules: libsndfile, libaudioutils
+ "Mpeg4H263DecoderTest", // depends on unconverted modules: libstagefright_foundation
+ "Mpeg4H263EncoderTest",
+ "avcdec",
+ "avcenc",
+ "bionic-benchmarks-tests",
+ "bionic-fortify-runtime-asan-test",
+ "bionic-stress-tests",
+ "bionic-unit-tests",
+ "bionic-unit-tests-glibc",
+ "bionic-unit-tests-static",
+ "boringssl_crypto_test",
+ "boringssl_ssl_test",
+ "cfi_test_helper",
+ "cfi_test_helper2",
+ "cintltst32",
+ "cintltst64",
+ "compare",
+ "cpuid",
+ "debuggerd_test", // depends on unconverted modules: libdebuggerd
+ "elftls_dlopen_ie_error_helper",
+ "exec_linker_helper",
+ "fastdeploy_test", // depends on unconverted modules: AdbWinApi, libadb_host, libandroidfw, libfastdeploy_host, libopenscreen-discovery, libopenscreen-platform-impl, libusb
+ "fdtrack_test",
+ "google-benchmark-test",
+ "googletest-param-test-test_ndk", // depends on unconverted modules: libgtest_ndk_c++
+ "gtest-typed-test_test",
+ "gtest-typed-test_test_ndk", // depends on unconverted modules: libgtest_ndk_c++, libgtest_main_ndk_c++
+ "gtest_ndk_tests", // depends on unconverted modules: libgtest_ndk_c++, libgtest_main_ndk_c++
+ "gtest_ndk_tests_no_main", // depends on unconverted modules: libgtest_ndk_c++
+ "gtest_prod_test_ndk", // depends on unconverted modules: libgtest_ndk_c++, libgtest_main_ndk_c++
+ "gtest_tests",
+ "gtest_tests_no_main",
+ "gwp_asan_unittest",
+ "half_test",
+ "hashcombine_test",
+ "hevcdec",
+ "hevcenc",
+ "hwbinderThroughputTest", // depends on unconverted modules: android.hardware.tests.libhwbinder@1.0-impl.test, android.hardware.tests.libhwbinder@1.0
+ "i444tonv12_eg",
+ "icu4c_sample_break",
+ "intltest32",
+ "intltest64",
+ "ion-unit-tests",
+ "jemalloc5_integrationtests",
+ "jemalloc5_unittests",
+ "ld_config_test_helper",
+ "ld_preload_test_helper",
+ "libBionicCtsGtestMain", // depends on unconverted modules: libgtest_isolated
+ "libBionicLoaderTests", // depends on unconverted modules: libmeminfo
+ "libapexutil_tests", // depends on unconverted modules: apex-info-list-tinyxml, libapexutil
+ "libcutils_sockets_test",
+ "libexpectedutils_test",
+ "libhwbinder_latency",
+ "liblog-host-test", // failing tests
+ "libminijail_test",
+ "libminijail_unittest_gtest",
+ "libpackagelistparser_test",
+ "libprotobuf_vendor_suffix_test",
+ "libstagefright_amrnbdec_test", // depends on unconverted modules: libsndfile, libaudioutils
+ "libstagefright_amrnbenc_test",
+ "libstagefright_amrwbdec_test", // depends on unconverted modules: libsndfile, libaudioutils
+ "libstagefright_m4vh263enc_test",
+ "libstagefright_mp3dec_test", // depends on unconverted modules: libsndfile, libaudioutils
+ "libstatssocket_test",
+ "libvndksupport-tests",
+ "libyuv_unittest",
+ "linker-unit-tests",
+ "malloc_debug_system_tests",
+ "malloc_debug_unit_tests",
+ "malloc_hooks_system_tests",
+ "mat_test",
+ "mathtest",
+ "memunreachable_binder_test", // depends on unconverted modules: libbinder
+ "memunreachable_test",
+ "metadata_tests",
+ "minijail0_cli_unittest_gtest",
+ "mpeg2dec",
+ "mvcdec",
+ "ns_hidden_child_helper",
+ "pngtest",
+ "preinit_getauxval_test_helper",
+ "preinit_syscall_test_helper",
+ "psnr",
+ "quat_test",
+ "rappor-tests", // depends on unconverted modules: jsr305, guava
+ "scudo_unit_tests",
+ "stats-log-api-gen-test", // depends on unconverted modules: libstats_proto_host
+ "syscall_filter_unittest_gtest",
+ "sysprop_test", // depends on unconverted modules: libcom.android.sysprop.tests
+ "thread_exit_cb_helper",
+ "tls_properties_helper",
+ "ulp",
+ "vec_test",
+ "yuvconstants",
+ "yuvconvert",
+ "zipalign_tests",
+
+ // cc_test_library
+ "clang_diagnostic_tests",
+ "exec_linker_helper_lib",
+ "fortify_disabled_for_tidy",
+ "ld_config_test_helper_lib1",
+ "ld_config_test_helper_lib2",
+ "ld_config_test_helper_lib3",
+ "ld_preload_test_helper_lib1",
+ "ld_preload_test_helper_lib2",
+ "libBionicElfTlsLoaderTests",
+ "libBionicElfTlsTests",
+ "libBionicElfTlsTests",
+ "libBionicFramePointerTests",
+ "libBionicFramePointerTests",
+ "libBionicStandardTests",
+ "libBionicStandardTests",
+ "libBionicTests",
+ "libart-broken",
+ "libatest_simple_zip",
+ "libcfi-test",
+ "libcfi-test-bad",
+ "libcrash_test",
+ // "libcrypto_fuzz_unsafe",
+ "libdl_preempt_test_1",
+ "libdl_preempt_test_2",
+ "libdl_test_df_1_global",
+ "libdlext_test",
+ "libdlext_test_different_soname",
+ "libdlext_test_fd",
+ "libdlext_test_norelro",
+ "libdlext_test_recursive",
+ "libdlext_test_zip",
+ "libdvrcommon_test",
+ "libfortify1-new-tests-clang",
+ "libfortify1-new-tests-clang",
+ "libfortify1-tests-clang",
+ "libfortify1-tests-clang",
+ "libfortify2-new-tests-clang",
+ "libfortify2-new-tests-clang",
+ "libfortify2-tests-clang",
+ "libfortify2-tests-clang",
+ "libgnu-hash-table-library",
+ "libicutest_static",
+ "liblinker_reloc_bench_000",
+ "liblinker_reloc_bench_001",
+ "liblinker_reloc_bench_002",
+ "liblinker_reloc_bench_003",
+ "liblinker_reloc_bench_004",
+ "liblinker_reloc_bench_005",
+ "liblinker_reloc_bench_006",
+ "liblinker_reloc_bench_007",
+ "liblinker_reloc_bench_008",
+ "liblinker_reloc_bench_009",
+ "liblinker_reloc_bench_010",
+ "liblinker_reloc_bench_011",
+ "liblinker_reloc_bench_012",
+ "liblinker_reloc_bench_013",
+ "liblinker_reloc_bench_014",
+ "liblinker_reloc_bench_015",
+ "liblinker_reloc_bench_016",
+ "liblinker_reloc_bench_017",
+ "liblinker_reloc_bench_018",
+ "liblinker_reloc_bench_019",
+ "liblinker_reloc_bench_020",
+ "liblinker_reloc_bench_021",
+ "liblinker_reloc_bench_022",
+ "liblinker_reloc_bench_023",
+ "liblinker_reloc_bench_024",
+ "liblinker_reloc_bench_025",
+ "liblinker_reloc_bench_026",
+ "liblinker_reloc_bench_027",
+ "liblinker_reloc_bench_028",
+ "liblinker_reloc_bench_029",
+ "liblinker_reloc_bench_030",
+ "liblinker_reloc_bench_031",
+ "liblinker_reloc_bench_032",
+ "liblinker_reloc_bench_033",
+ "liblinker_reloc_bench_034",
+ "liblinker_reloc_bench_035",
+ "liblinker_reloc_bench_036",
+ "liblinker_reloc_bench_037",
+ "liblinker_reloc_bench_038",
+ "liblinker_reloc_bench_039",
+ "liblinker_reloc_bench_040",
+ "liblinker_reloc_bench_041",
+ "liblinker_reloc_bench_042",
+ "liblinker_reloc_bench_043",
+ "liblinker_reloc_bench_044",
+ "liblinker_reloc_bench_045",
+ "liblinker_reloc_bench_046",
+ "liblinker_reloc_bench_047",
+ "liblinker_reloc_bench_048",
+ "liblinker_reloc_bench_049",
+ "liblinker_reloc_bench_050",
+ "liblinker_reloc_bench_051",
+ "liblinker_reloc_bench_052",
+ "liblinker_reloc_bench_053",
+ "liblinker_reloc_bench_054",
+ "liblinker_reloc_bench_055",
+ "liblinker_reloc_bench_056",
+ "liblinker_reloc_bench_057",
+ "liblinker_reloc_bench_058",
+ "liblinker_reloc_bench_059",
+ "liblinker_reloc_bench_060",
+ "liblinker_reloc_bench_061",
+ "liblinker_reloc_bench_062",
+ "liblinker_reloc_bench_063",
+ "liblinker_reloc_bench_064",
+ "liblinker_reloc_bench_065",
+ "liblinker_reloc_bench_066",
+ "liblinker_reloc_bench_067",
+ "liblinker_reloc_bench_068",
+ "liblinker_reloc_bench_069",
+ "liblinker_reloc_bench_070",
+ "liblinker_reloc_bench_071",
+ "liblinker_reloc_bench_072",
+ "liblinker_reloc_bench_073",
+ "liblinker_reloc_bench_074",
+ "liblinker_reloc_bench_075",
+ "liblinker_reloc_bench_076",
+ "liblinker_reloc_bench_077",
+ "liblinker_reloc_bench_078",
+ "liblinker_reloc_bench_079",
+ "liblinker_reloc_bench_080",
+ "liblinker_reloc_bench_081",
+ "liblinker_reloc_bench_082",
+ "liblinker_reloc_bench_083",
+ "liblinker_reloc_bench_084",
+ "liblinker_reloc_bench_085",
+ "liblinker_reloc_bench_086",
+ "liblinker_reloc_bench_087",
+ "liblinker_reloc_bench_088",
+ "liblinker_reloc_bench_089",
+ "liblinker_reloc_bench_090",
+ "liblinker_reloc_bench_091",
+ "liblinker_reloc_bench_092",
+ "liblinker_reloc_bench_093",
+ "liblinker_reloc_bench_094",
+ "liblinker_reloc_bench_095",
+ "liblinker_reloc_bench_096",
+ "liblinker_reloc_bench_097",
+ "liblinker_reloc_bench_098",
+ "liblinker_reloc_bench_099",
+ "liblinker_reloc_bench_100",
+ "liblinker_reloc_bench_101",
+ "liblinker_reloc_bench_102",
+ "liblinker_reloc_bench_103",
+ "liblinker_reloc_bench_104",
+ "liblinker_reloc_bench_105",
+ "liblinker_reloc_bench_106",
+ "liblinker_reloc_bench_107",
+ "liblinker_reloc_bench_108",
+ "liblinker_reloc_bench_109",
+ "liblinker_reloc_bench_110",
+ "liblinker_reloc_bench_111",
+ "liblinker_reloc_bench_112",
+ "liblinker_reloc_bench_113",
+ "liblinker_reloc_bench_114",
+ "liblinker_reloc_bench_115",
+ "liblinker_reloc_bench_116",
+ "liblinker_reloc_bench_117",
+ "liblinker_reloc_bench_118",
+ "liblinker_reloc_bench_119",
+ "liblinker_reloc_bench_120",
+ "liblinker_reloc_bench_121",
+ "liblinker_reloc_bench_122",
+ "liblinker_reloc_bench_123",
+ "liblinker_reloc_bench_124",
+ "liblinker_reloc_bench_125",
+ "liblinker_reloc_bench_126",
+ "liblinker_reloc_bench_127",
+ "liblinker_reloc_bench_128",
+ "liblinker_reloc_bench_129",
+ "liblinker_reloc_bench_130",
+ "liblinker_reloc_bench_131",
+ "liblinker_reloc_bench_132",
+ "liblinker_reloc_bench_133",
+ "liblinker_reloc_bench_134",
+ "liblinker_reloc_bench_135",
+ "liblinker_reloc_bench_136",
+ "liblinker_reloc_bench_137",
+ "liblinker_reloc_bench_138",
+ "liblinker_reloc_bench_139",
+ "liblinker_reloc_bench_140",
+ "liblinker_reloc_bench_141",
+ "liblinker_reloc_bench_142",
+ "liblinker_reloc_bench_143",
+ "liblinker_reloc_bench_144",
+ "liblinker_reloc_bench_145",
+ "liblinker_reloc_bench_146",
+ "liblinker_reloc_bench_147",
+ "liblinker_reloc_bench_148",
+ "liblinker_reloc_bench_149",
+ "liblinker_reloc_bench_150",
+ "liblinker_reloc_bench_151",
+ "liblinker_reloc_bench_152",
+ "liblinker_reloc_bench_153",
+ "liblinker_reloc_bench_154",
+ "liblinker_reloc_bench_155",
+ "liblinker_reloc_bench_156",
+ "liblinker_reloc_bench_157",
+ "liblinker_reloc_bench_158",
+ "liblinker_reloc_bench_159",
+ "liblinker_reloc_bench_160",
+ "liblinker_reloc_bench_161",
+ "liblinker_reloc_bench_162",
+ "liblinker_reloc_bench_163",
+ "liblinker_reloc_bench_164",
+ "liblinker_reloc_bench_165",
+ "liblinker_reloc_bench_166",
+ "liblinker_reloc_bench_167",
+ "liblinker_reloc_bench_168",
+ "libns_hidden_child_app",
+ "libns_hidden_child_global",
+ "libns_hidden_child_internal",
+ "libns_hidden_child_public",
+ "libnstest_dlopened",
+ "libnstest_ns_a_public1",
+ "libnstest_ns_a_public1_internal",
+ "libnstest_ns_b_public2",
+ "libnstest_ns_b_public3",
+ "libnstest_private",
+ "libnstest_private_external",
+ "libnstest_public",
+ "libnstest_public_internal",
+ "libnstest_root",
+ "libnstest_root_not_isolated",
+ "librelocations-ANDROID_REL",
+ "librelocations-ANDROID_RELR",
+ "librelocations-RELR",
+ "librelocations-fat",
+ "libsegment_gap_inner",
+ "libsegment_gap_outer",
+ // "libssl_fuzz_unsafe",
+ "libstatssocket_private",
+ "libsysv-hash-table-library",
+ "libtest_atexit",
+ "libtest_check_order_dlsym",
+ "libtest_check_order_dlsym_1_left",
+ "libtest_check_order_dlsym_2_right",
+ "libtest_check_order_dlsym_3_c",
+ "libtest_check_order_dlsym_a",
+ "libtest_check_order_dlsym_b",
+ "libtest_check_order_dlsym_d",
+ "libtest_check_order_reloc_root",
+ "libtest_check_order_reloc_root_1",
+ "libtest_check_order_reloc_root_2",
+ "libtest_check_order_reloc_siblings",
+ "libtest_check_order_reloc_siblings_1",
+ "libtest_check_order_reloc_siblings_2",
+ "libtest_check_order_reloc_siblings_3",
+ "libtest_check_order_reloc_siblings_a",
+ "libtest_check_order_reloc_siblings_b",
+ "libtest_check_order_reloc_siblings_c",
+ "libtest_check_order_reloc_siblings_c_1",
+ "libtest_check_order_reloc_siblings_c_2",
+ "libtest_check_order_reloc_siblings_d",
+ "libtest_check_order_reloc_siblings_e",
+ "libtest_check_order_reloc_siblings_f",
+ "libtest_check_rtld_next_from_library",
+ "libtest_dlopen_df_1_global",
+ "libtest_dlopen_from_ctor",
+ "libtest_dlopen_from_ctor_main",
+ "libtest_dlopen_weak_undefined_func",
+ "libtest_dlsym_df_1_global",
+ "libtest_dlsym_from_this",
+ "libtest_dlsym_from_this_child",
+ "libtest_dlsym_from_this_grandchild",
+ "libtest_dlsym_weak_func",
+ "libtest_dt_runpath_a",
+ "libtest_dt_runpath_b",
+ "libtest_dt_runpath_c",
+ "libtest_dt_runpath_d",
+ "libtest_dt_runpath_d_zip",
+ "libtest_dt_runpath_x",
+ "libtest_dt_runpath_y",
+ "libtest_elftls_dynamic",
+ "libtest_elftls_dynamic_filler_1",
+ "libtest_elftls_dynamic_filler_2",
+ "libtest_elftls_dynamic_filler_3",
+ "libtest_elftls_shared_var",
+ "libtest_elftls_shared_var_ie",
+ "libtest_elftls_tprel",
+ "libtest_empty",
+ "libtest_ifunc",
+ "libtest_ifunc_variable",
+ "libtest_ifunc_variable_impl",
+ "libtest_indirect_thread_local_dtor",
+ "libtest_init_fini_order_child",
+ "libtest_init_fini_order_grand_child",
+ "libtest_init_fini_order_root",
+ "libtest_init_fini_order_root2",
+ "libtest_missing_symbol",
+ "libtest_missing_symbol_child_private",
+ "libtest_missing_symbol_child_public",
+ "libtest_missing_symbol_root",
+ "libtest_nodelete_1",
+ "libtest_nodelete_2",
+ "libtest_nodelete_dt_flags_1",
+ "libtest_pthread_atfork",
+ "libtest_relo_check_dt_needed_order",
+ "libtest_relo_check_dt_needed_order_1",
+ "libtest_relo_check_dt_needed_order_2",
+ "libtest_simple",
+ "libtest_thread_local_dtor",
+ "libtest_thread_local_dtor2",
+ "libtest_two_parents_child",
+ "libtest_two_parents_parent1",
+ "libtest_two_parents_parent2",
+ "libtest_versioned_lib",
+ "libtest_versioned_libv1",
+ "libtest_versioned_libv2",
+ "libtest_versioned_otherlib",
+ "libtest_versioned_otherlib_empty",
+ "libtest_versioned_uselibv1",
+ "libtest_versioned_uselibv2",
+ "libtest_versioned_uselibv2_other",
+ "libtest_versioned_uselibv3_other",
+ "libtest_with_dependency",
+ "libtest_with_dependency_loop",
+ "libtest_with_dependency_loop_a",
+ "libtest_with_dependency_loop_b",
+ "libtest_with_dependency_loop_b_tmp",
+ "libtest_with_dependency_loop_c",
+ "libtestshared",
+
+ // depends on unconverted libprotobuf-java-nano
+ "dnsresolverprotosnano",
+ "launcherprotosnano",
+ "datastallprotosnano",
+ "devicepolicyprotosnano",
+ "ota_metadata_proto_java",
+ "merge_ota",
+
+ // releasetools
+ "releasetools_fsverity_metadata_generator",
+ "verity_utils",
+ "check_ota_package_signature",
+ "check_target_files_vintf",
+ "releasetools_check_target_files_vintf",
+ "releasetools_verity_utils",
+ "build_image",
+ "ota_from_target_files",
+ "releasetools_ota_from_target_files",
+ "releasetools_build_image",
+ "add_img_to_target_files",
+ "releasetools_add_img_to_target_files",
+ "fsverity_metadata_generator",
+ "sign_target_files_apks",
+
+ // depends on the support of yacc file
+ "libapplypatch",
+ "libapplypatch_modes",
+ "applypatch",
+
+ // TODO(b/254476335): disable the following due to this bug
+ "libapexinfo",
+ "libapexinfo_tests",
+
+ // uses glob in $(locations)
+ "libc_musl_sysroot",
+
+ // TODO(b/266459895): depends on libunwindstack
+ "libutils_test",
+
+ // Has dependencies on other tools like ziptool, bp2build'd data properties don't work with these tests atm
+ "ziparchive_tests_large",
+ "mkbootimg_test",
+ "certify_bootimg_test",
+
+ // Despite being _host module types, these require devices to run
+ "logd_integration_test",
+ "mobly-hello-world-test",
+ "mobly-multidevice-test",
+
+ // TODO(b/274805756): Support core_platform and current java APIs
+ "fake-framework",
+
+ // TODO(b/277616982): These modules depend on private java APIs, but maybe they don't need to.
+ "StreamingProtoTest",
+ "textclassifierprotoslite",
+ "styleprotoslite",
+ "CtsPkgInstallerConstants",
+ "guava-android-testlib",
+
+ // python_test_host with test data
+ "sbom_writers_test",
}
- Bp2buildCcLibraryStaticOnlyList = []string{}
-
MixedBuildsDisabledList = []string{
+ "libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source
+
"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
@@ -390,29 +1451,366 @@
"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",
+ // TODO(b/240563612) Needing `stem` selection support for cc_binary
"crasher",
- "libcxx_test_template",
- "linker",
- "memory_replay",
- "native_bridge_guest_linker",
- "native_bridge_stub_library_defaults",
- "noop",
- "simpleperf_ndk",
- "toybox-static",
- "zlib_bench",
+
+ // java_import[_host] issues
+ // tradefed prebuilts depend on libprotobuf
+ "prebuilt_tradefed",
+ "prebuilt_tradefed-test-framework",
+ // handcrafted BUILD.bazel files in //prebuilts/...
+ "prebuilt_r8lib-prebuilt",
+ "prebuilt_sdk-core-lambda-stubs",
+ "prebuilt_android-support-collections-nodeps",
+ "prebuilt_android-arch-core-common-nodeps",
+ "prebuilt_android-arch-lifecycle-common-java8-nodeps",
+ "prebuilt_android-arch-lifecycle-common-nodeps",
+ "prebuilt_android-support-annotations-nodeps",
+ "prebuilt_android-arch-paging-common-nodeps",
+ "prebuilt_android-arch-room-common-nodeps",
+ // TODO(b/217750501) exclude_dirs property not supported
+ "prebuilt_kotlin-reflect",
+ "prebuilt_kotlin-stdlib",
+ "prebuilt_kotlin-stdlib-jdk7",
+ "prebuilt_kotlin-stdlib-jdk8",
+ "prebuilt_kotlin-test",
+ // TODO(b/217750501) exclude_files property not supported
+ "prebuilt_currysrc_org.eclipse",
+
+ // TODO(b/266459895): re-enable libunwindstack
+ "libunwindstack",
+ "libunwindstack_stdout_log",
+ "libunwindstack_no_dex",
+ "libunwindstack_utils",
+ "unwind_reg_info",
+ "libunwindstack_local",
+ "unwind_for_offline",
+ "unwind",
+ "unwind_info",
+ "unwind_symbols",
+ "libEGL",
+ "libGLESv2",
+ "libc_malloc_debug",
+ "libcodec2_hidl@1.0",
+ "libcodec2_hidl@1.1",
+ "libcodec2_hidl@1.2",
+ "libfdtrack",
+ "libgui",
+ "libgui_bufferqueue_static",
+ "libmedia_codecserviceregistrant",
+ "libstagefright_bufferqueue_helper_novndk",
+ "libutils_test",
+ "libutilscallstack",
+ "mediaswcodec",
+ }
+
+ // Bazel prod-mode allowlist. Modules in this list are built by Bazel
+ // in either prod mode or staging mode.
+ ProdMixedBuildsEnabledList = []string{
+ // M5: tzdata launch
+ "com.android.tzdata",
+ "test1_com.android.tzdata",
+ "test3_com.android.tzdata",
+ // M7: adbd launch
+ "com.android.adbd",
+ "test_com.android.adbd",
+ "adbd_test",
+ "adb_crypto_test",
+ "adb_pairing_auth_test",
+ "adb_pairing_connection_test",
+ "adb_tls_connection_test",
+ // M9: mixed builds for mainline trains launch
+ "api_fingerprint",
+ }
+
+ // Staging-mode allowlist. Modules in this list are only built
+ // by Bazel with --bazel-mode-staging. This list should contain modules
+ // which will soon be added to the prod allowlist.
+ // It is implicit that all modules in ProdMixedBuildsEnabledList will
+ // also be built - do not add them to this list.
+ StagingMixedBuildsEnabledList = []string{
+ "com.android.neuralnetworks",
+ "libneuralnetworks",
+ "libneuralnetworks_static",
+ }
+
+ // These should be the libs that are included by the apexes in the ProdMixedBuildsEnabledList
+ ProdDclaMixedBuildsEnabledList = []string{
+ "libbase",
+ "libc++",
+ "libcrypto",
+ "libcutils",
+ }
+
+ // These should be the libs that are included by the apexes in the StagingMixedBuildsEnabledList
+ StagingDclaMixedBuildsEnabledList = []string{}
+
+ // TODO(b/269342245): Enable the rest of the DCLA libs
+ // "libssl",
+ // "libstagefright_flacdec",
+ // "libutils",
+
+ // TODO(b/273282046): Make this list customizable to support various targets.
+ // The list of modules which are expected to spend lots of build time.
+ // With `--ninja_weight_source=soong`, ninja builds these modules and deps first.
+ HugeModulesMap = map[string]int{
+ "AccountManagementApp": DEFAULT_NINJA_WEIGHT,
+ "ActivityManagerPerfTestsStubApp1": DEFAULT_NINJA_WEIGHT,
+ "ActivityManagerPerfTestsStubApp2": DEFAULT_NINJA_WEIGHT,
+ "ActivityManagerPerfTestsStubApp3": DEFAULT_NINJA_WEIGHT,
+ "api-stubs-docs-non-updatable": DEFAULT_NINJA_WEIGHT,
+ "AppCompatibilityTest": DEFAULT_NINJA_WEIGHT,
+ "AppTransitionTests": DEFAULT_NINJA_WEIGHT,
+ "art_compiler_tests": DEFAULT_NINJA_WEIGHT,
+ "art.module.intra.core.api.stubs.source": DEFAULT_NINJA_WEIGHT,
+ "art.module.public.api.stubs.source": DEFAULT_NINJA_WEIGHT,
+ "AttestationVerificationTest": DEFAULT_NINJA_WEIGHT,
+ "BatteryUsageStatsProtoTests": DEFAULT_NINJA_WEIGHT,
+ "bluetooth_test_gd_unit": DEFAULT_NINJA_WEIGHT,
+ "Bluetooth": DEFAULT_NINJA_WEIGHT,
+ "BluetoothInstrumentationTests": DEFAULT_NINJA_WEIGHT,
+ "Calendar": DEFAULT_NINJA_WEIGHT,
+ "CalendarProvider": DEFAULT_NINJA_WEIGHT,
+ "Camera2": DEFAULT_NINJA_WEIGHT,
+ "CarRotaryControllerUnitTests": DEFAULT_NINJA_WEIGHT,
+ "CarSettingsForUnitTesting": DEFAULT_NINJA_WEIGHT,
+ "CarSettingsUnitTests": DEFAULT_NINJA_WEIGHT,
+ "CarSystemUI-tests": DEFAULT_NINJA_WEIGHT,
+ "CellBroadcastApp": DEFAULT_NINJA_WEIGHT,
+ "CellBroadcastLegacyApp": DEFAULT_NINJA_WEIGHT,
+ "CellBroadcastReceiverOemUnitTests": DEFAULT_NINJA_WEIGHT,
+ "CellBroadcastServiceModule": DEFAULT_NINJA_WEIGHT,
+ "CompanionDeviceManager": DEFAULT_NINJA_WEIGHT,
+ "ConnectivityChecker": DEFAULT_NINJA_WEIGHT,
+ "Contacts": DEFAULT_NINJA_WEIGHT,
+ "ContactsProvider": DEFAULT_NINJA_WEIGHT,
+ "ContentCapturePerfTests": DEFAULT_NINJA_WEIGHT,
+ "CorePerfTests": DEFAULT_NINJA_WEIGHT,
+ "crosvm": DEFAULT_NINJA_WEIGHT,
+ "CtsDomainVerificationDeviceMultiUserTestCases": DEFAULT_NINJA_WEIGHT,
+ "CtsLogdTestCases": DEFAULT_NINJA_WEIGHT,
+ "CtsMediaProviderTranscodeTests": DEFAULT_NINJA_WEIGHT,
+ "CtsRollbackManagerHostTestHelperApp": DEFAULT_NINJA_WEIGHT,
+ "CtsRollbackManagerHostTestHelperApp2": DEFAULT_NINJA_WEIGHT,
+ "CtsRootPackageInstallerTestCases": DEFAULT_NINJA_WEIGHT,
+ "CtsRootRollbackManagerHostTestHelperApp": DEFAULT_NINJA_WEIGHT,
+ "CtsTranscodeTestAppSupportsHevc": DEFAULT_NINJA_WEIGHT,
+ "CtsTranscodeTestAppSupportsSlowMotion": DEFAULT_NINJA_WEIGHT,
+ "CuttlefishDisplayHotplugHelperApp": DEFAULT_NINJA_WEIGHT,
+ "cvd-host_package": DEFAULT_NINJA_WEIGHT,
+ "DelegateTestApp": DEFAULT_NINJA_WEIGHT,
+ "DeskClock": DEFAULT_NINJA_WEIGHT,
+ "Development": DEFAULT_NINJA_WEIGHT,
+ "DeviceAdminTestApp": DEFAULT_NINJA_WEIGHT,
+ "DevicePolicyManagementRoleHolderTestApp": DEFAULT_NINJA_WEIGHT,
+ "dex2oatd": DEFAULT_NINJA_WEIGHT,
+ "DocumentsUI": DEFAULT_NINJA_WEIGHT,
+ "EasterEgg": DEFAULT_NINJA_WEIGHT,
+ "EffectProxyTest": DEFAULT_NINJA_WEIGHT,
+ "EmergencyInfo": DEFAULT_NINJA_WEIGHT,
+ "EmptyTestApp": DEFAULT_NINJA_WEIGHT,
+ "ExtServices": DEFAULT_NINJA_WEIGHT,
+ "FacebookAppsScenarioTests": DEFAULT_NINJA_WEIGHT,
+ "flickerlib-core": DEFAULT_NINJA_WEIGHT,
+ "flickerlib": DEFAULT_NINJA_WEIGHT,
+ "FlickerLibTest": DEFAULT_NINJA_WEIGHT,
+ "FlickerTests": DEFAULT_NINJA_WEIGHT,
+ "framework-minus-apex": DEFAULT_NINJA_WEIGHT,
+ "framework-res": DEFAULT_NINJA_WEIGHT,
+ "FrameworksCoreTests": DEFAULT_NINJA_WEIGHT,
+ "FrameworksMockingCoreTests": DEFAULT_NINJA_WEIGHT,
+ "FrameworksMockingServicesTests": DEFAULT_NINJA_WEIGHT,
+ "FrameworksNetSmokeTests": DEFAULT_NINJA_WEIGHT,
+ "FrameworksNetTests": DEFAULT_NINJA_WEIGHT,
+ "FrameworksServicesTests": DEFAULT_NINJA_WEIGHT,
+ "FrameworksTelephonyTests": DEFAULT_NINJA_WEIGHT,
+ "FrameworksUiServicesTests": DEFAULT_NINJA_WEIGHT,
+ "FrameworksVcnTests": DEFAULT_NINJA_WEIGHT,
+ "Gallery2": DEFAULT_NINJA_WEIGHT,
+ "GameCoreDevice": DEFAULT_NINJA_WEIGHT,
+ "GoogleBluetoothInstrumentationTests": DEFAULT_NINJA_WEIGHT,
+ "guice_munged_srcs": DEFAULT_NINJA_WEIGHT,
+ "HalfSheetUX": DEFAULT_NINJA_WEIGHT,
+ "ImePerfTests": DEFAULT_NINJA_WEIGHT,
+ "imgdiag": DEFAULT_NINJA_WEIGHT,
+ "ImsServiceEntitlement": DEFAULT_NINJA_WEIGHT,
+ "ImsServiceEntitlementUnitTests": DEFAULT_NINJA_WEIGHT,
+ "InputTests": DEFAULT_NINJA_WEIGHT,
+ "InstallTest": DEFAULT_NINJA_WEIGHT,
+ "IntentResolver": DEFAULT_NINJA_WEIGHT,
+ "JankBench": DEFAULT_NINJA_WEIGHT,
+ "jsilver": DEFAULT_NINJA_WEIGHT,
+ "KeyChain": DEFAULT_NINJA_WEIGHT,
+ "KeyChainTests": DEFAULT_NINJA_WEIGHT,
+ "keystore2": DEFAULT_NINJA_WEIGHT,
+ "LargeResourcesCompressed": DEFAULT_NINJA_WEIGHT,
+ "LatinIME": DEFAULT_NINJA_WEIGHT,
+ "Launcher3QuickStepLib": DEFAULT_NINJA_WEIGHT,
+ "libaom": DEFAULT_NINJA_WEIGHT,
+ "libart-broken": DEFAULT_NINJA_WEIGHT,
+ "libart-compiler": DEFAULT_NINJA_WEIGHT,
+ "libart-disassembler": DEFAULT_NINJA_WEIGHT,
+ "libart": DEFAULT_NINJA_WEIGHT,
+ "libartd": DEFAULT_NINJA_WEIGHT,
+ "libaudiohal@7.1": DEFAULT_NINJA_WEIGHT,
+ "libbluetooth_core_rs": DEFAULT_NINJA_WEIGHT,
+ "libbluetooth_gd_unit_tests": DEFAULT_NINJA_WEIGHT,
+ "libbluetooth_gd": DEFAULT_NINJA_WEIGHT,
+ "libbluetooth_rs": DEFAULT_NINJA_WEIGHT,
+ "libbluetooth-for-tests": DEFAULT_NINJA_WEIGHT,
+ "libbt_common": DEFAULT_NINJA_WEIGHT,
+ "libbt_packets_nonapex": DEFAULT_NINJA_WEIGHT,
+ "libbt_packets": DEFAULT_NINJA_WEIGHT,
+ "libbt_shim_ffi": DEFAULT_NINJA_WEIGHT,
+ "libbt_shim": DEFAULT_NINJA_WEIGHT,
+ "libbt-audio-hal-interface": DEFAULT_NINJA_WEIGHT,
+ "libbt-bta-core": DEFAULT_NINJA_WEIGHT,
+ "libbt-bta": DEFAULT_NINJA_WEIGHT,
+ "libbt-common": DEFAULT_NINJA_WEIGHT,
+ "libbt-hci": DEFAULT_NINJA_WEIGHT,
+ "libbt-platform-protos-lite": DEFAULT_NINJA_WEIGHT,
+ "libbt-protos-lite": DEFAULT_NINJA_WEIGHT,
+ "libbt-sbc-decoder": DEFAULT_NINJA_WEIGHT,
+ "libc": DEFAULT_NINJA_WEIGHT,
+ "libclap": DEFAULT_NINJA_WEIGHT,
+ "libcodec2_soft_av1dec_gav1": DEFAULT_NINJA_WEIGHT,
+ "libcompositionengine_test": DEFAULT_NINJA_WEIGHT,
+ "libdevices": DEFAULT_NINJA_WEIGHT,
+ "libfrontend_proto": DEFAULT_NINJA_WEIGHT,
+ "libhwtrust": DEFAULT_NINJA_WEIGHT,
+ "libjni": DEFAULT_NINJA_WEIGHT,
+ "libkeystore2": DEFAULT_NINJA_WEIGHT,
+ "libkmr_ta": DEFAULT_NINJA_WEIGHT,
+ "liblmp": DEFAULT_NINJA_WEIGHT,
+ "libopenjdkjvmtid": DEFAULT_NINJA_WEIGHT,
+ "libprotobuf_deprecated": DEFAULT_NINJA_WEIGHT,
+ "libprotobuf": DEFAULT_NINJA_WEIGHT,
+ "libregex": DEFAULT_NINJA_WEIGHT,
+ "LibStatsPullTests": DEFAULT_NINJA_WEIGHT,
+ "libstd": DEFAULT_NINJA_WEIGHT,
+ "libsurfaceflinger_unittest": DEFAULT_NINJA_WEIGHT,
+ "libsyn": DEFAULT_NINJA_WEIGHT,
+ "libtokio": DEFAULT_NINJA_WEIGHT,
+ "libuwb_core": DEFAULT_NINJA_WEIGHT,
+ "libuwb_uci_jni_rust": DEFAULT_NINJA_WEIGHT,
+ "libuwb_uci_packets": DEFAULT_NINJA_WEIGHT,
+ "libvpx": DEFAULT_NINJA_WEIGHT,
+ "libvulkan_enc": DEFAULT_NINJA_WEIGHT,
+ "libwebrtc": DEFAULT_NINJA_WEIGHT,
+ "LiveWallpapersPicker": DEFAULT_NINJA_WEIGHT,
+ "LockTaskApp": DEFAULT_NINJA_WEIGHT,
+ "LongevityPlatformLibTests": DEFAULT_NINJA_WEIGHT,
+ "ManagedProvisioning": DEFAULT_NINJA_WEIGHT,
+ "ManagedProvisioningTests": DEFAULT_NINJA_WEIGHT,
+ "MediaProvider": DEFAULT_NINJA_WEIGHT,
+ "MediaProviderClientTests": DEFAULT_NINJA_WEIGHT,
+ "MediaProviderLegacy": DEFAULT_NINJA_WEIGHT,
+ "messaging": DEFAULT_NINJA_WEIGHT,
+ "metalava": DEFAULT_NINJA_WEIGHT,
+ "MicrobenchmarkRunnerTests": DEFAULT_NINJA_WEIGHT,
+ "microdroid_manager": DEFAULT_NINJA_WEIGHT,
+ "minikin_tests": DEFAULT_NINJA_WEIGHT,
+ "MLCTestApp": DEFAULT_NINJA_WEIGHT,
+ "MmsService": DEFAULT_NINJA_WEIGHT,
+ "MmsServiceTests": DEFAULT_NINJA_WEIGHT,
+ "module-lib-api-stubs-docs-non-updatable": DEFAULT_NINJA_WEIGHT,
+ "motion_tool_lib_tests": DEFAULT_NINJA_WEIGHT,
+ "MtpService": DEFAULT_NINJA_WEIGHT,
+ "MultiUserTests": DEFAULT_NINJA_WEIGHT,
+ "NearbyIntegrationUiTests": DEFAULT_NINJA_WEIGHT,
+ "net_test_bluetooth": DEFAULT_NINJA_WEIGHT,
+ "net_test_btif": DEFAULT_NINJA_WEIGHT,
+ "net_test_main_shim": DEFAULT_NINJA_WEIGHT,
+ "net_test_stack": DEFAULT_NINJA_WEIGHT,
+ "net-tests-utils": DEFAULT_NINJA_WEIGHT,
+ "NetworkStackCoverageTests": DEFAULT_NINJA_WEIGHT,
+ "NetworkStackIntegrationTests": DEFAULT_NINJA_WEIGHT,
+ "NetworkStackNext": DEFAULT_NINJA_WEIGHT,
+ "NfcNci": DEFAULT_NINJA_WEIGHT,
+ "NfcNciUnitTests": DEFAULT_NINJA_WEIGHT,
+ "NotEmptyTestApp": DEFAULT_NINJA_WEIGHT,
+ "NotificationFunctionalTests": DEFAULT_NINJA_WEIGHT,
+ "oatdumpd": DEFAULT_NINJA_WEIGHT,
+ "OsuLogin": DEFAULT_NINJA_WEIGHT,
+ "PackageInstaller": DEFAULT_NINJA_WEIGHT,
+ "PackageManagerComponentOverrideTests": DEFAULT_NINJA_WEIGHT,
+ "PackageManagerPerfTests": DEFAULT_NINJA_WEIGHT,
+ "PackageManagerServiceServerTests": DEFAULT_NINJA_WEIGHT,
+ "PackageManagerServiceUnitTests": DEFAULT_NINJA_WEIGHT,
+ "PackageWatchdogTest": DEFAULT_NINJA_WEIGHT,
+ "PandoraServerLib": DEFAULT_NINJA_WEIGHT,
+ "pdl": DEFAULT_NINJA_WEIGHT,
+ "perfetto_trace_java_protos": DEFAULT_NINJA_WEIGHT,
+ "perfetto_trace-full": DEFAULT_NINJA_WEIGHT,
+ "PermissionController": DEFAULT_NINJA_WEIGHT,
+ "PermissionControllerMockingTests": DEFAULT_NINJA_WEIGHT,
+ "PixelAppCompTests": DEFAULT_NINJA_WEIGHT,
+ "platform-bootclasspath": DEFAULT_NINJA_WEIGHT,
+ "PlatformCommonScenarioTests": DEFAULT_NINJA_WEIGHT,
+ "PlatformComposeCoreTests": DEFAULT_NINJA_WEIGHT,
+ "platformprotoslite": DEFAULT_NINJA_WEIGHT,
+ "PlatformRuleTests": DEFAULT_NINJA_WEIGHT,
+ "precompiled_sepolicy-without_apex": DEFAULT_NINJA_WEIGHT,
+ "PresencePolling": DEFAULT_NINJA_WEIGHT,
+ "PrintSpooler": DEFAULT_NINJA_WEIGHT,
+ "QuickSearchBox": DEFAULT_NINJA_WEIGHT,
+ "RemoteDPCTestApp": DEFAULT_NINJA_WEIGHT,
+ "RemoteProvisioningServiceTests": DEFAULT_NINJA_WEIGHT,
+ "RkpdAppUnitTests": DEFAULT_NINJA_WEIGHT,
+ "Robolectric_shadows_framework": DEFAULT_NINJA_WEIGHT,
+ "RoleHolderApp": DEFAULT_NINJA_WEIGHT,
+ "SdkSandbox": DEFAULT_NINJA_WEIGHT,
+ "service-appsearch": DEFAULT_NINJA_WEIGHT,
+ "service-connectivity": DEFAULT_NINJA_WEIGHT,
+ "service-uwb": DEFAULT_NINJA_WEIGHT,
+ "service-wifi": DEFAULT_NINJA_WEIGHT,
+ "services-non-updatable-stubs": DEFAULT_NINJA_WEIGHT,
+ "services": DEFAULT_NINJA_WEIGHT,
+ "Settings-core": DEFAULT_NINJA_WEIGHT,
+ "Settings": DEFAULT_NINJA_WEIGHT,
+ "SettingsIntelligence": DEFAULT_NINJA_WEIGHT,
+ "SettingsLibTests": DEFAULT_NINJA_WEIGHT,
+ "SettingsProvider": DEFAULT_NINJA_WEIGHT,
+ "Shell": DEFAULT_NINJA_WEIGHT,
+ "SimAppDialog": DEFAULT_NINJA_WEIGHT,
+ "sl4a": DEFAULT_NINJA_WEIGHT,
+ "SmsApp": DEFAULT_NINJA_WEIGHT,
+ "SoundPicker": DEFAULT_NINJA_WEIGHT,
+ "StagedInstallTest": DEFAULT_NINJA_WEIGHT,
+ "StatementService": DEFAULT_NINJA_WEIGHT,
+ "StatsdFrameworkTestApp": DEFAULT_NINJA_WEIGHT,
+ "StatsdFrameworkTestAppNoPermission": DEFAULT_NINJA_WEIGHT,
+ "statsdprotolite": DEFAULT_NINJA_WEIGHT,
+ "Stk": DEFAULT_NINJA_WEIGHT,
+ "StorageManager": DEFAULT_NINJA_WEIGHT,
+ "system-api-stubs-docs-non-updatable": DEFAULT_NINJA_WEIGHT,
+ "SystemUI-core": DEFAULT_NINJA_WEIGHT,
+ "SystemUI-tests-base": DEFAULT_NINJA_WEIGHT,
+ "SystemUI-tests": DEFAULT_NINJA_WEIGHT,
+ "SystemUI": DEFAULT_NINJA_WEIGHT,
+ "SystemUIComposeFeatures": DEFAULT_NINJA_WEIGHT,
+ "SystemUIComposeFeaturesTests": DEFAULT_NINJA_WEIGHT,
+ "SystemUITests": DEFAULT_NINJA_WEIGHT,
+ "Tag": DEFAULT_NINJA_WEIGHT,
+ "Telecom": DEFAULT_NINJA_WEIGHT,
+ "TelecomUnitTests": DEFAULT_NINJA_WEIGHT,
+ "telephony-common": DEFAULT_NINJA_WEIGHT,
+ "TelephonyProvider": DEFAULT_NINJA_WEIGHT,
+ "TeleService": DEFAULT_NINJA_WEIGHT,
+ "test-api-stubs-docs-non-updatable": DEFAULT_NINJA_WEIGHT,
+ "TetheringIntegrationTests": DEFAULT_NINJA_WEIGHT,
+ "TetheringNext": DEFAULT_NINJA_WEIGHT,
+ "ThemePickerTests": DEFAULT_NINJA_WEIGHT,
+ "Traceur": DEFAULT_NINJA_WEIGHT,
+ "UsbManagerTests": DEFAULT_NINJA_WEIGHT,
+ "UsbTests": DEFAULT_NINJA_WEIGHT,
+ "virtmgr": DEFAULT_NINJA_WEIGHT,
+ "WallpaperPicker2TestLib": DEFAULT_NINJA_WEIGHT,
+ "WallpaperPicker2Tests": DEFAULT_NINJA_WEIGHT,
+ "WifiDialog": DEFAULT_NINJA_WEIGHT,
+ "wm-proto-parsers": DEFAULT_NINJA_WEIGHT,
+ "WMShellFlickerTests": DEFAULT_NINJA_WEIGHT,
+ "WmTests": DEFAULT_NINJA_WEIGHT,
+ "wpa_supplicant": DEFAULT_NINJA_WEIGHT,
}
)
diff --git a/android/android_test.go b/android/android_test.go
index fb82e37..64ceedc 100644
--- a/android/android_test.go
+++ b/android/android_test.go
@@ -15,10 +15,32 @@
package android
import (
+ "io/ioutil"
"os"
"testing"
)
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "android_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
func TestMain(m *testing.M) {
- os.Exit(m.Run())
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
}
diff --git a/android/androidmk.go b/android/androidmk.go
index 69df358..aa411d1 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -489,11 +489,11 @@
// Write the license variables to Make for AndroidMkData.Custom(..) methods that do not call WriteAndroidMkData(..)
// It's required to propagate the license metadata even for module types that have non-standard interfaces to Make.
func (a *AndroidMkEntries) WriteLicenseVariables(w io.Writer) {
- fmt.Fprintln(w, "LOCAL_LICENSE_KINDS :=", strings.Join(a.EntryMap["LOCAL_LICENSE_KINDS"], " "))
- fmt.Fprintln(w, "LOCAL_LICENSE_CONDITIONS :=", strings.Join(a.EntryMap["LOCAL_LICENSE_CONDITIONS"], " "))
- fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(a.EntryMap["LOCAL_NOTICE_FILE"], " "))
+ AndroidMkEmitAssignList(w, "LOCAL_LICENSE_KINDS", a.EntryMap["LOCAL_LICENSE_KINDS"])
+ AndroidMkEmitAssignList(w, "LOCAL_LICENSE_CONDITIONS", a.EntryMap["LOCAL_LICENSE_CONDITIONS"])
+ AndroidMkEmitAssignList(w, "LOCAL_NOTICE_FILE", a.EntryMap["LOCAL_NOTICE_FILE"])
if pn, ok := a.EntryMap["LOCAL_LICENSE_PACKAGE_NAME"]; ok {
- fmt.Fprintln(w, "LOCAL_LICENSE_PACKAGE_NAME :=", strings.Join(pn, " "))
+ AndroidMkEmitAssignList(w, "LOCAL_LICENSE_PACKAGE_NAME", pn)
}
}
@@ -501,9 +501,11 @@
// generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file.
type fillInEntriesContext interface {
ModuleDir(module blueprint.Module) string
+ ModuleSubDir(module blueprint.Module) string
Config() Config
ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
+ ModuleType(module blueprint.Module) string
}
func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
@@ -527,7 +529,7 @@
fmt.Fprintf(&a.header, distString)
}
- fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintf(&a.header, "\ninclude $(CLEAR_VARS) # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod))
// Collect make variable assignment entries.
a.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
@@ -546,6 +548,7 @@
a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
a.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
a.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
+ a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
// If the install rule was generated by Soong tell Make about it.
if len(base.katiInstalls) > 0 {
@@ -608,10 +611,6 @@
}
}
- if len(base.noticeFiles) > 0 {
- a.AddStrings("LOCAL_NOTICE_FILE", strings.Join(base.noticeFiles.Strings(), " "))
- }
-
if host {
makeOs := base.Os().String()
if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
@@ -675,7 +674,7 @@
w.Write(a.header.Bytes())
for _, name := range a.entryOrder {
- fmt.Fprintln(w, name+" := "+strings.Join(a.EntryMap[name], " "))
+ AndroidMkEmitAssignList(w, name, a.EntryMap[name])
}
w.Write(a.footer.Bytes())
}
@@ -949,7 +948,10 @@
return !module.Enabled() ||
module.commonProperties.HideFromMake ||
// Make does not understand LinuxBionic
- module.Os() == LinuxBionic
+ module.Os() == LinuxBionic ||
+ // Make does not understand LinuxMusl, except when we are building with USE_HOST_MUSL=true
+ // and all host binaries are LinuxMusl
+ (module.Os() == LinuxMusl && module.Target().HostCross)
}
// A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how
@@ -972,3 +974,28 @@
}
return testFiles
}
+
+// AndroidMkEmitAssignList emits the line
+//
+// VAR := ITEM ...
+//
+// Items are the elements to the given set of lists
+// If all the passed lists are empty, no line will be emitted
+func AndroidMkEmitAssignList(w io.Writer, varName string, lists ...[]string) {
+ doPrint := false
+ for _, l := range lists {
+ if doPrint = len(l) > 0; doPrint {
+ break
+ }
+ }
+ if !doPrint {
+ return
+ }
+ fmt.Fprint(w, varName, " :=")
+ for _, l := range lists {
+ for _, item := range l {
+ fmt.Fprint(w, " ", item)
+ }
+ }
+ fmt.Fprintln(w)
+}
diff --git a/android/apex.go b/android/apex.go
index 883bf14..c9b4a0b 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -356,9 +356,18 @@
return m
}
+var (
+ availableToPlatformList = []string{AvailableToPlatform}
+)
+
// Implements ApexModule
func (m *ApexModuleBase) ApexAvailable() []string {
- return m.ApexProperties.Apex_available
+ aa := m.ApexProperties.Apex_available
+ if len(aa) > 0 {
+ return aa
+ }
+ // Default is availability to platform
+ return CopyOf(availableToPlatformList)
}
// Implements ApexModule
@@ -454,8 +463,6 @@
}
return InList(what, apex_available) ||
(what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) ||
- (what == "com.android.btservices" && InList("com.android.bluetooth", apex_available)) || // TODO b/243054261
- (what == "com.android.bluetooth" && InList("com.android.btservices", apex_available)) || // TODO b/243054261
(strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available)) ||
(what == "com.google.mainline.primary.libs") || // TODO b/248601389
(what == "com.google.mainline.go.primary.libs") // TODO b/248601389
@@ -821,7 +828,7 @@
var flatContent strings.Builder
fmt.Fprintf(&fullContent, "%s(minSdkVersion:%s):\n", ctx.ModuleName(), minSdkVersion)
- for _, key := range FirstUniqueStrings(SortedStringKeys(depInfos)) {
+ for _, key := range FirstUniqueStrings(SortedKeys(depInfos)) {
info := depInfos[key]
toName := fmt.Sprintf("%s(minSdkVersion:%s)", info.To, info.MinSdkVersion)
if info.IsExternal {
@@ -840,64 +847,6 @@
ctx.Phony(fmt.Sprintf("%s-depsinfo", ctx.ModuleName()), d.fullListPath, d.flatListPath)
}
-// TODO(b/158059172): remove minSdkVersion allowlist
-var minSdkVersionAllowlist = func(apiMap map[string]int) map[string]ApiLevel {
- list := make(map[string]ApiLevel, len(apiMap))
- for name, finalApiInt := range apiMap {
- list[name] = uncheckedFinalApiLevel(finalApiInt)
- }
- 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,
-})
-
// Function called while walking an APEX's payload dependencies.
//
// Return true if the `to` module should be visited, false otherwise.
@@ -907,7 +856,7 @@
// ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks
type ModuleWithMinSdkVersionCheck interface {
Module
- MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+ MinSdkVersion(ctx EarlyModuleContext) ApiLevel
CheckMinSdkVersion(ctx ModuleContext)
}
@@ -949,15 +898,13 @@
}
if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
toName := ctx.OtherModuleName(to)
- if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
- ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+
- "\n\nDependency path: %s\n\n"+
- "Consider adding 'min_sdk_version: %q' to %q",
- minSdkVersion, ctx.ModuleName(), err.Error(),
- ctx.GetPathString(false),
- minSdkVersion, toName)
- return false
- }
+ ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+
+ "\n\nDependency path: %s\n\n"+
+ "Consider adding 'min_sdk_version: %q' to %q",
+ minSdkVersion, ctx.ModuleName(), err.Error(),
+ ctx.GetPathString(false),
+ minSdkVersion, toName)
+ return false
}
return true
})
diff --git a/android/api_domain.go b/android/api_domain.go
new file mode 100644
index 0000000..587ceae
--- /dev/null
+++ b/android/api_domain.go
@@ -0,0 +1,130 @@
+// 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 (
+ "github.com/google/blueprint"
+
+ "android/soong/bazel"
+)
+
+func init() {
+ RegisterApiDomainBuildComponents(InitRegistrationContext)
+}
+
+func RegisterApiDomainBuildComponents(ctx RegistrationContext) {
+ ctx.RegisterModuleType("api_domain", ApiDomainFactory)
+}
+
+type ApiSurface int
+
+// TODO(b/246656800): Reconcile with android.SdkKind
+const (
+ // API surface provided by platform and mainline modules to other mainline modules
+ ModuleLibApi ApiSurface = iota
+ PublicApi // Aka NDK
+ VendorApi // Aka LLNDK
+)
+
+func (a ApiSurface) String() string {
+ switch a {
+ case ModuleLibApi:
+ return "module-libapi"
+ case PublicApi:
+ return "publicapi"
+ case VendorApi:
+ return "vendorapi"
+ default:
+ return "invalid"
+ }
+}
+
+type apiDomain struct {
+ ModuleBase
+ BazelModuleBase
+
+ properties apiDomainProperties
+}
+
+type apiDomainProperties struct {
+ // cc library contributions (.h files/.map.txt) of this API domain
+ // This dependency is a no-op in Soong, but the corresponding Bazel target in the api_bp2build workspace
+ // will provide a `CcApiContributionInfo` provider
+ Cc_api_contributions []string
+
+ // java library contributions (as .txt) of this API domain
+ // This dependency is a no-op in Soong, but the corresponding Bazel target in the api_bp2build workspace
+ // will provide a `JavaApiContributionInfo` provider
+ Java_api_contributions []string
+}
+
+func ApiDomainFactory() Module {
+ m := &apiDomain{}
+ m.AddProperties(&m.properties)
+ InitAndroidArchModule(m, DeviceSupported, MultilibBoth)
+ return m
+}
+
+// Do not create any dependency edges in Soong for now to skip visibility checks for some systemapi libraries.
+// Currently, all api_domain modules reside in build/orchestrator/apis/Android.bp
+// However, cc libraries like libsigchain (com.android.art) restrict their visibility to art/*
+// When the api_domain module types are collocated with their contributions, this dependency edge can be restored
+func (a *apiDomain) DepsMutator(ctx BottomUpMutatorContext) {
+}
+
+// API domain does not have any builld actions yet
+func (a *apiDomain) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+const (
+ apiContributionSuffix = ".contribution"
+)
+
+// ApiContributionTargetName returns the name of the bp2build target (e.g. cc_api_contribution) of contribution modules (e.g. ndk_library)
+// A suffix is necessary to prevent a name collision with the base target in the same bp2build bazel package
+func ApiContributionTargetName(moduleName string) string {
+ return moduleName + apiContributionSuffix
+}
+
+// For each contributing cc_library, format the name to its corresponding contribution bazel target in the bp2build workspace
+func contributionBazelAttributes(ctx TopDownMutatorContext, contributions []string) bazel.LabelListAttribute {
+ addSuffix := func(ctx BazelConversionPathContext, module blueprint.Module) string {
+ baseLabel := BazelModuleLabel(ctx, module)
+ return ApiContributionTargetName(baseLabel)
+ }
+ bazelLabels := BazelLabelForModuleDepsWithFn(ctx, contributions, addSuffix)
+ return bazel.MakeLabelListAttribute(bazelLabels)
+}
+
+type bazelApiDomainAttributes struct {
+ Cc_api_contributions bazel.LabelListAttribute
+ Java_api_contributions bazel.LabelListAttribute
+}
+
+var _ ApiProvider = (*apiDomain)(nil)
+
+func (a *apiDomain) ConvertWithApiBp2build(ctx TopDownMutatorContext) {
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "api_domain",
+ Bzl_load_location: "//build/bazel/rules/apis:api_domain.bzl",
+ }
+ attrs := &bazelApiDomainAttributes{
+ Cc_api_contributions: contributionBazelAttributes(ctx, a.properties.Cc_api_contributions),
+ Java_api_contributions: contributionBazelAttributes(ctx, a.properties.Java_api_contributions),
+ }
+ ctx.CreateBazelTargetModule(props, CommonAttributes{
+ Name: ctx.ModuleName(),
+ }, attrs)
+}
diff --git a/android/api_levels.go b/android/api_levels.go
index 2013730..137fd9d 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -54,7 +54,21 @@
isPreview bool
}
+func (this ApiLevel) FinalInt() int {
+ if this.IsInvalid() {
+ panic(fmt.Errorf("%v is not a recognized api_level\n", this))
+ }
+ if this.IsPreview() {
+ panic("Requested a final int from a non-final ApiLevel")
+ } else {
+ return this.number
+ }
+}
+
func (this ApiLevel) FinalOrFutureInt() int {
+ if this.IsInvalid() {
+ panic(fmt.Errorf("%v is not a recognized api_level\n", this))
+ }
if this.IsPreview() {
return FutureApiLevelInt
} else {
@@ -68,6 +82,9 @@
// - preview codenames -> preview base (9000) + index
// - otherwise -> cast to int
func (this ApiLevel) FinalOrPreviewInt() int {
+ if this.IsInvalid() {
+ panic(fmt.Errorf("%v is not a recognized api_level\n", this))
+ }
if this.IsCurrent() {
return this.number
}
@@ -89,6 +106,11 @@
return this.isPreview
}
+// Returns true if the raw api level string is invalid
+func (this ApiLevel) IsInvalid() bool {
+ return this.EqualTo(InvalidApiLevel)
+}
+
// Returns true if this is the unfinalized "current" API level. This means
// different things across Java and native. Java APIs do not use explicit
// codenames, so all non-final codenames are grouped into "current". For native
@@ -105,6 +127,72 @@
return this.number == -1
}
+// Returns true if an app is compiling against private apis.
+// e.g. if sdk_version = "" in Android.bp, then the ApiLevel of that "sdk" is at PrivateApiLevel.
+func (this ApiLevel) IsPrivate() bool {
+ return this.number == PrivateApiLevel.number
+}
+
+// EffectiveVersion converts an ApiLevel into the concrete ApiLevel that the module should use. For
+// modules targeting an unreleased SDK (meaning it does not yet have a number) it returns
+// FutureApiLevel(10000).
+func (l ApiLevel) EffectiveVersion(ctx EarlyModuleContext) (ApiLevel, error) {
+ if l.EqualTo(InvalidApiLevel) {
+ return l, fmt.Errorf("invalid version in sdk_version %q", l.value)
+ }
+ if !l.IsPreview() {
+ return l, nil
+ }
+ ret := ctx.Config().DefaultAppTargetSdk(ctx)
+ if ret.IsPreview() {
+ return FutureApiLevel, nil
+ }
+ return ret, nil
+}
+
+// EffectiveVersionString converts an SdkSpec into the concrete version string that the module
+// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
+// it returns the codename (P, Q, R, etc.)
+func (l ApiLevel) EffectiveVersionString(ctx EarlyModuleContext) (string, error) {
+ if l.EqualTo(InvalidApiLevel) {
+ return l.value, fmt.Errorf("invalid version in sdk_version %q", l.value)
+ }
+ if !l.IsPreview() {
+ return l.String(), nil
+ }
+ // Determine the default sdk
+ ret := ctx.Config().DefaultAppTargetSdk(ctx)
+ if !ret.IsPreview() {
+ // If the default sdk has been finalized, return that
+ return ret.String(), nil
+ }
+ // There can be more than one active in-development sdks
+ // If an app is targeting an active sdk, but not the default one, return the requested active sdk.
+ // e.g.
+ // SETUP
+ // In-development: UpsideDownCake, VanillaIceCream
+ // Default: VanillaIceCream
+ // Android.bp
+ // min_sdk_version: `UpsideDownCake`
+ // RETURN
+ // UpsideDownCake and not VanillaIceCream
+ for _, preview := range ctx.Config().PreviewApiLevels() {
+ if l.String() == preview.String() {
+ return preview.String(), nil
+ }
+ }
+ // Otherwise return the default one
+ return ret.String(), nil
+}
+
+// Specified returns true if the module is targeting a recognzized api_level.
+// It returns false if either
+// 1. min_sdk_version is not an int or a recognized codename
+// 2. both min_sdk_version and sdk_version are empty. In this case, MinSdkVersion() defaults to SdkSpecPrivate.ApiLevel
+func (this ApiLevel) Specified() bool {
+ return !this.IsInvalid() && !this.IsPrivate()
+}
+
// Returns -1 if the current API level is less than the argument, 0 if they
// are equal, and 1 if it is greater than the argument.
func (this ApiLevel) CompareTo(other ApiLevel) int {
@@ -158,6 +246,19 @@
isPreview: true,
}
+// Sentinel ApiLevel to validate that an apiLevel is either an int or a recognized codename.
+var InvalidApiLevel = NewInvalidApiLevel("invalid")
+
+// Returns an apiLevel object at the same level as InvalidApiLevel.
+// The object contains the raw string provied in bp file, and can be used for error handling.
+func NewInvalidApiLevel(raw string) ApiLevel {
+ return ApiLevel{
+ value: raw,
+ number: -2, // One less than NoneApiLevel
+ isPreview: true,
+ }
+}
+
// The first version that introduced 64-bit ABIs.
var FirstLp64Version = uncheckedFinalApiLevel(21)
@@ -184,17 +285,9 @@
// a core-for-system-modules.jar for the module-lib API scope.
var LastWithoutModuleLibCoreSystemModules = uncheckedFinalApiLevel(31)
-// If the `raw` input is the codename of an API level has been finalized, this
-// function returns the API level number associated with that API level. If the
-// input is *not* a finalized codename, the input is returned unmodified.
-//
-// For example, at the time of writing, R has been finalized as API level 30,
-// but S is in development so it has no number assigned. For the following
-// inputs:
-//
-// * "30" -> "30"
-// * "R" -> "30"
-// * "S" -> "S"
+// ReplaceFinalizedCodenames returns the API level number associated with that API level
+// if the `raw` input is the codename of an API level has been finalized.
+// If the input is *not* a finalized codename, the input is returned unmodified.
func ReplaceFinalizedCodenames(config Config, raw string) string {
num, ok := getFinalCodenamesMap(config)[raw]
if !ok {
@@ -204,6 +297,16 @@
return strconv.Itoa(num)
}
+// ApiLevelFrom converts the given string `raw` to an ApiLevel.
+// If `raw` is invalid (empty string, unrecognized codename etc.) it returns an invalid ApiLevel
+func ApiLevelFrom(ctx PathContext, raw string) ApiLevel {
+ ret, err := ApiLevelFromUser(ctx, raw)
+ if err != nil {
+ return NewInvalidApiLevel(raw)
+ }
+ return ret
+}
+
// ApiLevelFromUser converts the given string `raw` to an ApiLevel, possibly returning an error.
//
// `raw` must be non-empty. Passing an empty string results in a panic.
@@ -225,6 +328,8 @@
// ApiLevelFromUserWithConfig implements ApiLevelFromUser, see comments for
// ApiLevelFromUser for more details.
func ApiLevelFromUserWithConfig(config Config, raw string) (ApiLevel, error) {
+ // This logic is replicated in starlark, if changing logic here update starlark code too
+ // https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=42;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
if raw == "" {
panic("API level string must be non-empty")
}
@@ -239,14 +344,17 @@
}
}
- canonical := ReplaceFinalizedCodenames(config, raw)
- asInt, err := strconv.Atoi(canonical)
- if err != nil {
- return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", canonical)
+ canonical, ok := getApiLevelsMapReleasedVersions()[raw]
+ if !ok {
+ asInt, err := strconv.Atoi(raw)
+ if err != nil {
+ return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", raw)
+ }
+ return uncheckedFinalApiLevel(asInt), nil
}
- apiLevel := uncheckedFinalApiLevel(asInt)
- return apiLevel, nil
+ return uncheckedFinalApiLevel(canonical), nil
+
}
// ApiLevelForTest returns an ApiLevel constructed from the supplied raw string.
@@ -302,31 +410,38 @@
return PathForOutput(ctx, "api_levels.json")
}
+func getApiLevelsMapReleasedVersions() map[string]int {
+ return map[string]int{
+ "G": 9,
+ "I": 14,
+ "J": 16,
+ "J-MR1": 17,
+ "J-MR2": 18,
+ "K": 19,
+ "L": 21,
+ "L-MR1": 22,
+ "M": 23,
+ "N": 24,
+ "N-MR1": 25,
+ "O": 26,
+ "O-MR1": 27,
+ "P": 28,
+ "Q": 29,
+ "R": 30,
+ "S": 31,
+ "S-V2": 32,
+ "Tiramisu": 33,
+ "UpsideDownCake": 34,
+ }
+}
+
var finalCodenamesMapKey = NewOnceKey("FinalCodenamesMap")
func getFinalCodenamesMap(config Config) map[string]int {
+ // This logic is replicated in starlark, if changing logic here update starlark code too
+ // https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=30;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
return config.Once(finalCodenamesMapKey, func() interface{} {
- apiLevelsMap := map[string]int{
- "G": 9,
- "I": 14,
- "J": 16,
- "J-MR1": 17,
- "J-MR2": 18,
- "K": 19,
- "L": 21,
- "L-MR1": 22,
- "M": 23,
- "N": 24,
- "N-MR1": 25,
- "O": 26,
- "O-MR1": 27,
- "P": 28,
- "Q": 29,
- "R": 30,
- "S": 31,
- "S-V2": 32,
- "Tiramisu": 33,
- }
+ apiLevelsMap := getApiLevelsMapReleasedVersions()
// TODO: Differentiate "current" and "future".
// The code base calls it FutureApiLevel, but the spelling is "current",
@@ -349,30 +464,13 @@
var apiLevelsMapKey = NewOnceKey("ApiLevelsMap")
+// ApiLevelsMap has entries for preview API levels
func GetApiLevelsMap(config Config) map[string]int {
+ // This logic is replicated in starlark, if changing logic here update starlark code too
+ // https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=23;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
return config.Once(apiLevelsMapKey, func() interface{} {
- apiLevelsMap := map[string]int{
- "G": 9,
- "I": 14,
- "J": 16,
- "J-MR1": 17,
- "J-MR2": 18,
- "K": 19,
- "L": 21,
- "L-MR1": 22,
- "M": 23,
- "N": 24,
- "N-MR1": 25,
- "O": 26,
- "O-MR1": 27,
- "P": 28,
- "Q": 29,
- "R": 30,
- "S": 31,
- "S-V2": 32,
- "Tiramisu": 33,
- }
- for i, codename := range config.PlatformVersionActiveCodenames() {
+ apiLevelsMap := getApiLevelsMapReleasedVersions()
+ for i, codename := range config.PlatformVersionAllPreviewCodenames() {
apiLevelsMap[codename] = previewAPILevelBase + i
}
@@ -386,20 +484,11 @@
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_released_versions = %s
-api_levels = _api_levels
-`, printApiLevelsStarlarkDict(config),
+api_levels_released_versions = _api_levels_released_versions
+`, starlark_fmt.PrintStringIntDict(getApiLevelsMapReleasedVersions(), 0),
)
}
diff --git a/android/arch.go b/android/arch.go
index 655b008..4b4691b 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -16,9 +16,11 @@
import (
"encoding"
+ "encoding/json"
"fmt"
"reflect"
"runtime"
+ "sort"
"strings"
"android/soong/bazel"
@@ -308,7 +310,7 @@
// Linux is the OS for the Linux kernel plus the glibc runtime.
Linux = newOsType("linux_glibc", Host, false, X86, X86_64)
// LinuxMusl is the OS for the Linux kernel plus the musl runtime.
- LinuxMusl = newOsType("linux_musl", Host, false, X86, X86_64)
+ LinuxMusl = newOsType("linux_musl", Host, false, X86, X86_64, Arm64, Arm)
// Darwin is the OS for MacOS/Darwin host machines.
Darwin = newOsType("darwin", Host, false, Arm64, X86_64)
// LinuxBionic is the OS for the Linux kernel plus the Bionic libc runtime, but without the
@@ -394,54 +396,6 @@
}
}
-func registerBp2buildArchPathDepsMutator(ctx RegisterMutatorsContext) {
- ctx.BottomUp("bp2build-arch-pathdeps", bp2buildArchPathDepsMutator).Parallel()
-}
-
-// add dependencies for architecture specific properties tagged with `android:"path"`
-func bp2buildArchPathDepsMutator(ctx BottomUpMutatorContext) {
- var module Module
- module = ctx.Module()
-
- m := module.base()
- if !m.ArchSpecific() {
- return
- }
-
- // addPathDepsForProps does not descend into sub structs, so we need to descend into the
- // arch-specific properties ourselves
- var properties []interface{}
- for _, archProperties := range m.archProperties {
- for _, archProps := range archProperties {
- archPropValues := reflect.ValueOf(archProps).Elem()
- // there are three "arch" variations, descend into each
- for _, variant := range []string{"Arch", "Multilib", "Target"} {
- // The properties are an interface, get the value (a pointer) that it points to
- archProps := archPropValues.FieldByName(variant).Elem()
- if archProps.IsNil() {
- continue
- }
- // And then a pointer to a struct
- archProps = archProps.Elem()
- for i := 0; i < archProps.NumField(); i += 1 {
- f := archProps.Field(i)
- // If the value of the field is a struct (as opposed to a pointer to a struct) then step
- // into the BlueprintEmbed field.
- if f.Kind() == reflect.Struct {
- f = f.FieldByName("BlueprintEmbed")
- }
- if f.IsZero() {
- continue
- }
- props := f.Interface().(interface{})
- properties = append(properties, props)
- }
- }
- }
- }
- addPathDepsForProps(ctx, properties)
-}
-
// osMutator splits an arch-specific module into a variant for each OS that is enabled for the
// module. It uses the HostOrDevice value passed to InitAndroidArchModule and the
// device_supported and host_supported properties to determine which OsTypes are enabled for this
@@ -659,7 +613,8 @@
prefer32 := os == Windows
// Determine the multilib selection for this module.
- multilib, extraMultilib := decodeMultilib(base, os)
+ ignorePrefer32OnDevice := mctx.Config().IgnorePrefer32OnDevice()
+ multilib, extraMultilib := decodeMultilib(base, os, ignorePrefer32OnDevice)
// Convert the multilib selection into a list of Targets.
targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
@@ -667,6 +622,12 @@
mctx.ModuleErrorf("%s", err.Error())
}
+ // If there are no supported targets disable the module.
+ if len(targets) == 0 {
+ base.Disable()
+ return
+ }
+
// If the module is using extraMultilib, decode the extraMultilib selection into
// a separate list of Targets.
var multiTargets []Target
@@ -675,6 +636,7 @@
if err != nil {
mctx.ModuleErrorf("%s", err.Error())
}
+ multiTargets = filterHostCross(multiTargets, targets[0].HostCross)
}
// Recovery is always the primary architecture, filter out any other architectures.
@@ -734,7 +696,7 @@
// multilib from the factory's call to InitAndroidArchModule if none was set. For modules that
// called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
// the actual multilib in extraMultilib.
-func decodeMultilib(base *ModuleBase, os OsType) (multilib, extraMultilib string) {
+func decodeMultilib(base *ModuleBase, os OsType, ignorePrefer32OnDevice bool) (multilib, extraMultilib string) {
// First check the "android.compile_multilib" or "host.compile_multilib" properties.
switch os.Class {
case Device:
@@ -753,6 +715,13 @@
multilib = base.commonProperties.Default_multilib
}
+ // If a device is configured with multiple targets, this option
+ // force all device targets that prefer32 to be compiled only as
+ // the first target.
+ if ignorePrefer32OnDevice && os.Class == Device && (multilib == "prefer32" || multilib == "first_prefer32") {
+ multilib = "first"
+ }
+
if base.commonProperties.UseTargetVariants {
// Darwin has the concept of "universal binaries" which is implemented in Soong by
// building both x86_64 and arm64 variants, and having select module types know how to
@@ -804,6 +773,18 @@
return targets
}
+// filterHostCross takes a list of Targets and a hostCross value, and returns a modified list
+// that contains only Targets that have the specified HostCross.
+func filterHostCross(targets []Target, hostCross bool) []Target {
+ for i := 0; i < len(targets); i++ {
+ if targets[i].HostCross != hostCross {
+ targets = append(targets[:i], targets[i+1:]...)
+ i--
+ }
+ }
+ return targets
+}
+
// archPropRoot is a struct type used as the top level of the arch-specific properties. It
// contains the "arch", "multilib", and "target" property structs. It is used to split up the
// property structs to limit how much is allocated when a single arch-specific property group is
@@ -994,19 +975,13 @@
if string(field.Tag) != `android:"`+strings.Join(values, ",")+`"` {
panic(fmt.Errorf("unexpected tag format %q", field.Tag))
}
- // don't delete path tag as it is needed for bp2build
// these tags don't need to be present in the runtime generated struct type.
- values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend"})
- if len(values) > 0 && values[0] != "path" {
+ values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend", "path"})
+ if len(values) > 0 {
panic(fmt.Errorf("unknown tags %q in field %q", values, prefix+field.Name))
- } else if len(values) == 1 {
- // FIXME(b/200678898): This assumes that the only tag type when there's
- // `android:"arch_variant"` is `android` itself and thus clobbers others
- field.Tag = reflect.StructTag(`android:"` + strings.Join(values, ",") + `"`)
- } else {
- field.Tag = ``
}
+ field.Tag = ``
return true, field
}
return false, field
@@ -1707,20 +1682,19 @@
// archConfig describes a built-in configuration.
type archConfig struct {
- arch string
- archVariant string
- cpuVariant string
- abi []string
+ Arch string `json:"arch"`
+ ArchVariant string `json:"arch_variant"`
+ CpuVariant string `json:"cpu_variant"`
+ Abi []string `json:"abis"`
}
-// getNdkAbisConfig returns the list of archConfigs that are used for bulding
-// the API stubs and static libraries that are included in the NDK. These are
-// built *without Neon*, because non-Neon is still supported and building these
-// with Neon will break those users.
+// getNdkAbisConfig returns the list of archConfigs that are used for building
+// the API stubs and static libraries that are included in the NDK.
func getNdkAbisConfig() []archConfig {
return []archConfig{
{"arm64", "armv8-a-branchprot", "", []string{"arm64-v8a"}},
- {"arm", "armv7-a", "", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
+ {"riscv64", "", "", []string{"riscv64"}},
{"x86_64", "", "", []string{"x86_64"}},
{"x86", "", "", []string{"x86"}},
}
@@ -1741,8 +1715,8 @@
var ret []Target
for _, config := range archConfigs {
- arch, err := decodeArch(Android, config.arch, &config.archVariant,
- &config.cpuVariant, config.abi)
+ arch, err := decodeArch(Android, config.Arch, &config.ArchVariant,
+ &config.CpuVariant, config.Abi)
if err != nil {
return nil, err
}
@@ -1832,7 +1806,9 @@
for _, t := range targets {
if _, found := set[t.Os.String()]; !found {
set[t.Os.String()] = true
- ret = append(ret, commonTargetMap[t.Os.String()])
+ common := commonTargetMap[t.Os.String()]
+ common.HostCross = t.HostCross
+ ret = append(ret, common)
}
}
@@ -1840,20 +1816,23 @@
}
// FirstTarget takes a list of Targets and a list of multilib values and returns a list of Targets
-// that contains zero or one Target for each OsType, selecting the one that matches the earliest
-// filter.
+// that contains zero or one Target for each OsType and HostCross, selecting the one that matches
+// the earliest filter.
func FirstTarget(targets []Target, filters ...string) []Target {
// find the first target from each OS
var ret []Target
- hasHost := false
- set := make(map[OsType]bool)
+ type osHostCross struct {
+ os OsType
+ hostCross bool
+ }
+ set := make(map[osHostCross]bool)
for _, filter := range filters {
buildTargets := filterMultilibTargets(targets, filter)
for _, t := range buildTargets {
- if _, found := set[t.Os]; !found {
- hasHost = hasHost || (t.Os.Class == Host)
- set[t.Os] = true
+ key := osHostCross{t.Os, t.HostCross}
+ if _, found := set[key]; !found {
+ set[key] = true
ret = append(ret, t)
}
}
@@ -2107,13 +2086,22 @@
// For each arch type (x86, arm64, etc.)
for _, arch := range ArchTypeList() {
// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
- // Iterate over ever shard and extract a struct with the same type as the
+ // Iterate over every shard and extract a struct with the same type as the
// input one that contains the data specific to that arch.
propertyStructs := make([]reflect.Value, 0)
+ archFeaturePropertyStructs := make(map[string][]reflect.Value, 0)
for _, archProperty := range archProperties {
archTypeStruct, ok := getArchTypeStruct(ctx, archProperty, arch)
if ok {
propertyStructs = append(propertyStructs, archTypeStruct)
+
+ // For each feature this arch supports (arm: neon, x86: ssse3, sse4, ...)
+ for _, feature := range archFeatures[arch] {
+ prefix := "arch." + arch.Name + "." + feature
+ if featureProperties, ok := getChildPropertyStruct(ctx, archTypeStruct, feature, prefix); ok {
+ archFeaturePropertyStructs[feature] = append(archFeaturePropertyStructs[feature], featureProperties)
+ }
+ }
}
multilibStruct, ok := getMultilibStruct(ctx, archProperty, arch)
if ok {
@@ -2121,10 +2109,31 @@
}
}
- // Create a new instance of the requested property set
- value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+ archToProp[arch.Name] = mergeStructs(ctx, propertyStructs, propertySet)
- archToProp[arch.Name] = mergeStructs(ctx, propertyStructs, value)
+ // In soong, if multiple features match the current configuration, they're
+ // all used. In bazel, we have to have unambiguous select() statements, so
+ // we can't have two features that are both active in the same select().
+ // One alternative is to split out each feature into a separate select(),
+ // but then it's difficult to support exclude_srcs, which may need to
+ // exclude things from the regular arch select() statement if a certain
+ // feature is active. Instead, keep the features in the same select
+ // statement as the arches, but emit the power set of all possible
+ // combinations of features, so that bazel can match the most precise one.
+ allFeatures := make([]string, 0, len(archFeaturePropertyStructs))
+ for feature := range archFeaturePropertyStructs {
+ allFeatures = append(allFeatures, feature)
+ }
+ for _, features := range bazel.PowerSetWithoutEmptySet(allFeatures) {
+ sort.Strings(features)
+ propsForCurrentFeatureSet := make([]reflect.Value, 0)
+ propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, propertyStructs...)
+ for _, feature := range features {
+ propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, archFeaturePropertyStructs[feature]...)
+ }
+ archToProp[arch.Name+"-"+strings.Join(features, "-")] =
+ mergeStructs(ctx, propsForCurrentFeatureSet, propertySet)
+ }
}
axisToProps[bazel.ArchConfigurationAxis] = archToProp
@@ -2277,6 +2286,14 @@
return starlark_fmt.PrintDict(valDict, 0)
}
+func printArchConfigList(arches []archConfig) string {
+ jsonOut, err := json.MarshalIndent(arches, "", starlark_fmt.Indention(1))
+ if err != nil {
+ panic(fmt.Errorf("Error converting arch configs %#v to json: %q", arches, err))
+ }
+ return fmt.Sprintf("json.decode('''%s''')", string(jsonOut))
+}
+
func StarlarkArchConfigurations() string {
return fmt.Sprintf(`
_arch_to_variants = %s
@@ -2287,13 +2304,21 @@
_android_arch_feature_for_arch_variant = %s
+_aml_arches = %s
+
+_ndk_arches = %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
+aml_arches = _aml_arches
+ndk_arches = _ndk_arches
`, printArchTypeStarlarkDict(archVariants),
printArchTypeStarlarkDict(cpuVariants),
printArchTypeStarlarkDict(archFeatures),
printArchTypeNestedStarlarkDict(androidArchFeatureMap),
+ printArchConfigList(getAmlAbisConfig()),
+ printArchConfigList(getNdkAbisConfig()),
)
}
diff --git a/android/arch_list.go b/android/arch_list.go
index cbf8e7a..ab644a4 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -26,11 +26,14 @@
"armv8-a-branchprot",
"armv8-2a",
"armv8-2a-dotprod",
+ "armv9-a",
},
X86: {
"amberlake",
"atom",
"broadwell",
+ "goldmont",
+ "goldmont-plus",
"haswell",
"icelake",
"ivybridge",
@@ -40,12 +43,15 @@
"skylake",
"stoneyridge",
"tigerlake",
+ "tremont",
"whiskeylake",
"x86_64",
},
X86_64: {
"amberlake",
"broadwell",
+ "goldmont",
+ "goldmont-plus",
"haswell",
"icelake",
"ivybridge",
@@ -55,6 +61,7 @@
"skylake",
"stoneyridge",
"tigerlake",
+ "tremont",
"whiskeylake",
},
}
@@ -65,6 +72,7 @@
"cortex-a8",
"cortex-a9",
"cortex-a15",
+ "cortex-a32",
"cortex-a53",
"cortex-a53.a57",
"cortex-a55",
@@ -142,6 +150,9 @@
"armv8-2a-dotprod": {
"dotprod",
},
+ "armv9-a": {
+ "dotprod",
+ },
},
X86: {
"amberlake": {
@@ -168,6 +179,24 @@
"aes_ni",
"popcnt",
},
+ "goldmont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ "movbe",
+ },
+ "goldmont-plus": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ "movbe",
+ },
"haswell": {
"ssse3",
"sse4",
@@ -257,6 +286,15 @@
"aes_ni",
"popcnt",
},
+ "tremont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ "movbe",
+ },
"whiskeylake": {
"ssse3",
"sse4",
@@ -304,6 +342,22 @@
"aes_ni",
"popcnt",
},
+ "goldmont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ },
+ "goldmont-plus": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ },
"haswell": {
"ssse3",
"sse4",
@@ -390,6 +444,14 @@
"aes_ni",
"popcnt",
},
+ "tremont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ },
"whiskeylake": {
"ssse3",
"sse4",
diff --git a/android/arch_test.go b/android/arch_test.go
index 8b328b6..5021a67 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -66,9 +66,9 @@
}{},
out: &struct {
A *string
- B *string `android:"path"`
- C *string `android:"path"`
- D *string `android:"path"`
+ B *string
+ C *string
+ D *string
}{},
filtered: true,
},
@@ -259,6 +259,27 @@
}
}
+func (m *archTestMultiTargetsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *archTestMultiTargetsModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+}
+
+func archTestMultiTargetsModuleFactory() Module {
+ m := &archTestMultiTargetsModule{}
+ m.AddProperties(&m.props)
+ InitAndroidMultiTargetsArchModule(m, HostAndDeviceSupported, MultilibCommon)
+ return m
+}
+
+type archTestMultiTargetsModule struct {
+ ModuleBase
+ props struct {
+ Deps []string
+ }
+}
+
func (m *archTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
}
@@ -277,19 +298,27 @@
PrepareForTestWithArchMutator,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("module", archTestModuleFactory)
+ ctx.RegisterModuleType("multi_targets_module", archTestMultiTargetsModuleFactory)
}),
)
func TestArchMutator(t *testing.T) {
var buildOSVariants []string
+ var buildOS64Variants []string
var buildOS32Variants []string
+ var buildOSCommonVariant string
+
switch runtime.GOOS {
case "linux":
buildOSVariants = []string{"linux_glibc_x86_64", "linux_glibc_x86"}
+ buildOS64Variants = []string{"linux_glibc_x86_64"}
buildOS32Variants = []string{"linux_glibc_x86"}
+ buildOSCommonVariant = "linux_glibc_common"
case "darwin":
buildOSVariants = []string{"darwin_x86_64"}
+ buildOS64Variants = []string{"darwin_x86_64"}
buildOS32Variants = nil
+ buildOSCommonVariant = "darwin_common"
}
bp := `
@@ -312,24 +341,46 @@
host_supported: true,
compile_multilib: "32",
}
+
+ module {
+ name: "first",
+ host_supported: true,
+ compile_multilib: "first",
+ }
+
+ multi_targets_module {
+ name: "multi_targets",
+ host_supported: true,
+ }
`
testCases := []struct {
- name string
- preparer FixturePreparer
- fooVariants []string
- barVariants []string
- bazVariants []string
- quxVariants []string
+ name string
+ preparer FixturePreparer
+ fooVariants []string
+ barVariants []string
+ bazVariants []string
+ quxVariants []string
+ firstVariants []string
+
+ multiTargetVariants []string
+ multiTargetVariantsMap map[string][]string
+
+ goOS string
}{
{
- name: "normal",
- preparer: nil,
- fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
- barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
- bazVariants: nil,
- quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"),
- },
+ name: "normal",
+ preparer: nil,
+ fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+ barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
+ bazVariants: nil,
+ quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"),
+ firstVariants: append(buildOS64Variants, "android_arm64_armv8-a"),
+ multiTargetVariants: []string{buildOSCommonVariant, "android_common"},
+ multiTargetVariantsMap: map[string][]string{
+ buildOSCommonVariant: buildOS64Variants,
+ "android_common": {"android_arm64_armv8-a"},
+ }},
{
name: "host-only",
preparer: FixtureModifyConfig(func(config Config) {
@@ -337,10 +388,33 @@
config.BuildOSCommonTarget = Target{}
config.Targets[Android] = nil
}),
- fooVariants: nil,
- barVariants: buildOSVariants,
- bazVariants: nil,
- quxVariants: buildOS32Variants,
+ fooVariants: nil,
+ barVariants: buildOSVariants,
+ bazVariants: nil,
+ quxVariants: buildOS32Variants,
+ firstVariants: buildOS64Variants,
+ multiTargetVariants: []string{buildOSCommonVariant},
+ multiTargetVariantsMap: map[string][]string{
+ buildOSCommonVariant: buildOS64Variants,
+ },
+ },
+ {
+ name: "same arch host and host cross",
+ preparer: FixtureModifyConfig(func(config Config) {
+ ModifyTestConfigForMusl(config)
+ modifyTestConfigForMuslArm64HostCross(config)
+ }),
+ fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+ barVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+ bazVariants: nil,
+ quxVariants: []string{"linux_musl_x86", "android_arm_armv7-a-neon"},
+ firstVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "android_arm64_armv8-a"},
+ multiTargetVariants: []string{"linux_musl_common", "android_common"},
+ multiTargetVariantsMap: map[string][]string{
+ "linux_musl_common": {"linux_musl_x86_64"},
+ "android_common": {"android_arm64_armv8-a"},
+ },
+ goOS: "linux",
},
}
@@ -356,8 +430,21 @@
return ret
}
+ moduleMultiTargets := func(ctx *TestContext, name string, variant string) []string {
+ var ret []string
+ targets := ctx.ModuleForTests(name, variant).Module().MultiTargets()
+ for _, t := range targets {
+ ret = append(ret, t.String())
+ }
+ return ret
+ }
+
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
+ if tt.goOS != runtime.GOOS {
+ t.Skipf("requries runtime.GOOS %s", tt.goOS)
+ }
+
result := GroupFixturePreparers(
prepareForArchTest,
// Test specific preparer
@@ -381,6 +468,20 @@
if g, w := enabledVariants(ctx, "qux"), tt.quxVariants; !reflect.DeepEqual(w, g) {
t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g)
}
+ if g, w := enabledVariants(ctx, "first"), tt.firstVariants; !reflect.DeepEqual(w, g) {
+ t.Errorf("want first variants:\n%q\ngot:\n%q\n", w, g)
+ }
+
+ if g, w := enabledVariants(ctx, "multi_targets"), tt.multiTargetVariants; !reflect.DeepEqual(w, g) {
+ t.Fatalf("want multi_target variants:\n%q\ngot:\n%q\n", w, g)
+ }
+
+ for _, variant := range tt.multiTargetVariants {
+ targets := moduleMultiTargets(ctx, "multi_targets", variant)
+ if g, w := targets, tt.multiTargetVariantsMap[variant]; !reflect.DeepEqual(w, g) {
+ t.Errorf("want ctx.MultiTarget() for %q:\n%q\ngot:\n%q\n", variant, w, g)
+ }
+ }
})
}
}
@@ -483,6 +584,8 @@
func (testArchPropertiesModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+// Module property "a" does not have "variant_prepend" tag.
+// Expected variant property orders are based on this fact.
func TestArchProperties(t *testing.T) {
bp := `
module {
@@ -602,7 +705,7 @@
{
name: "linux_musl",
goOS: "linux",
- preparer: FixtureModifyConfig(modifyTestConfigForMusl),
+ preparer: FixtureModifyConfig(ModifyTestConfigForMusl),
results: []result{
{
module: "foo",
diff --git a/android/bazel.go b/android/bazel.go
index 4ef8d78..1646883 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -17,9 +17,6 @@
import (
"bufio"
"errors"
- "fmt"
- "io/ioutil"
- "path/filepath"
"strings"
"github.com/google/blueprint"
@@ -35,6 +32,29 @@
Bp2BuildTopLevel = "."
)
+// FileGroupAsLibrary describes a filegroup module that is converted to some library
+// such as aidl_library or proto_library.
+type FileGroupAsLibrary interface {
+ ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool
+ ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool
+ GetAidlLibraryLabel(ctx BazelConversionPathContext) string
+ GetProtoLibraryLabel(ctx BazelConversionPathContext) string
+}
+
+type BazelConversionStatus struct {
+ // Information about _all_ bp2build targets generated by this module. Multiple targets are
+ // supported as Soong handles some things within a single target that we may choose to split into
+ // multiple targets, e.g. renderscript, protos, yacc within a cc module.
+ Bp2buildInfo []bp2buildInfo `blueprint:"mutated"`
+
+ // UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to
+ // Bazel
+ UnconvertedDeps []string `blueprint:"mutated"`
+
+ // MissingBp2buildDep stores the module names of direct dependency that were not found
+ MissingDeps []string `blueprint:"mutated"`
+}
+
type bazelModuleProperties struct {
// The label of the Bazel target replacing this Soong module. When run in conversion mode, this
// will import the handcrafted build target into the autogenerated file. Note: this may result in
@@ -46,8 +66,8 @@
//
// This is a bool pointer to support tristates: true, false, not set.
//
- // To opt-in a module, set bazel_module: { bp2build_available: true }
- // To opt-out a module, set bazel_module: { bp2build_available: false }
+ // To opt in a module, set bazel_module: { bp2build_available: true }
+ // To opt out a module, set bazel_module: { bp2build_available: false }
// To defer the default setting for the directory, do not set the value.
Bp2build_available *bool
@@ -58,7 +78,7 @@
// Properties contains common module properties for Bazel migration purposes.
type properties struct {
- // In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
+ // In "Bazel mixed build" mode, this represents the Bazel target replacing
// this Soong module.
Bazel_module bazelModuleProperties
}
@@ -97,7 +117,6 @@
GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
ShouldConvertWithBp2build(ctx BazelConversionContext) bool
shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool
- GetBazelBuildFileContents(c Config, path, name string) (string, error)
ConvertWithBp2build(ctx TopDownMutatorContext)
// namespacedVariableProps is a map from a soong config variable namespace
@@ -107,7 +126,7 @@
// one with the single member called Soong_config_variables, which itself is
// a struct containing fields for each supported feature in that namespace.
//
- // The reason for using an slice of interface{} is to support defaults
+ // The reason for using a slice of interface{} is to support defaults
// propagation of the struct pointers.
namespacedVariableProps() namespacedVariableProperties
setNamespacedVariableProps(props namespacedVariableProperties)
@@ -115,6 +134,32 @@
SetBaseModuleType(baseModuleType string)
}
+// ApiProvider is implemented by modules that contribute to an API surface
+type ApiProvider interface {
+ ConvertWithApiBp2build(ctx TopDownMutatorContext)
+}
+
+// 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
@@ -170,7 +215,7 @@
return "" // no label for unconverted module
}
-type bp2BuildConversionAllowlist struct {
+type Bp2BuildConversionAllowlist struct {
// Configure modules in these directories to enable bp2build_available: true or false by default.
defaultConfig allowlists.Bp2BuildConfig
@@ -178,46 +223,36 @@
// in the synthetic Bazel workspace.
keepExistingBuildFile map[string]bool
- // 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.
+ // Per-module allowlist to always opt modules into both bp2build and Bazel Dev Mode 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
- // Per-module-type allowlist to always opt modules in to both bp2build and mixed builds
- // when they have the same type as one listed.
+ // Per-module-type allowlist to always opt modules in to both bp2build and
+ // Bazel Dev Mode 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.
+ // Per-module denylist to always opt modules out of bp2build conversion.
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.
- ccLibraryStaticOnly map[string]bool
-
- // Per-module denylist to opt modules out of mixed builds. Such modules will
- // still be generated via bp2build.
- mixedBuildsDisabled map[string]bool
}
-// NewBp2BuildAllowlist creates a new, empty bp2BuildConversionAllowlist
+// NewBp2BuildAllowlist creates a new, empty Bp2BuildConversionAllowlist
// which can be populated using builder pattern Set* methods
-func NewBp2BuildAllowlist() bp2BuildConversionAllowlist {
- return bp2BuildConversionAllowlist{
+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 {
+func (a Bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) Bp2BuildConversionAllowlist {
if a.defaultConfig == nil {
a.defaultConfig = allowlists.Bp2BuildConfig{}
}
@@ -229,7 +264,7 @@
}
// SetKeepExistingBuildFile copies the entries from keepExistingBuildFile into the allowlist
-func (a bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) Bp2BuildConversionAllowlist {
if a.keepExistingBuildFile == nil {
a.keepExistingBuildFile = map[string]bool{}
}
@@ -241,7 +276,7 @@
}
// SetModuleAlwaysConvertList copies the entries from moduleAlwaysConvert into the allowlist
-func (a bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) Bp2BuildConversionAllowlist {
if a.moduleAlwaysConvert == nil {
a.moduleAlwaysConvert = map[string]bool{}
}
@@ -253,7 +288,7 @@
}
// SetModuleTypeAlwaysConvertList copies the entries from moduleTypeAlwaysConvert into the allowlist
-func (a bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) Bp2BuildConversionAllowlist {
if a.moduleTypeAlwaysConvert == nil {
a.moduleTypeAlwaysConvert = map[string]bool{}
}
@@ -265,7 +300,7 @@
}
// SetModuleDoNotConvertList copies the entries from moduleDoNotConvert into the allowlist
-func (a bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) Bp2BuildConversionAllowlist {
if a.moduleDoNotConvert == nil {
a.moduleDoNotConvert = map[string]bool{}
}
@@ -276,92 +311,59 @@
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 bp2buildAllowlist = NewBp2BuildAllowlist().
- SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
- SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
- SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
- SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
- SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
- SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList).
- SetMixedBuildsDisabledList(allowlists.MixedBuildsDisabledList)
-
-// 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 bp2buildAllowlist.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 {
- return shouldKeepExistingBuildFileForDir(bp2buildAllowlist, dir)
-}
-
-func shouldKeepExistingBuildFileForDir(allowlist bp2BuildConversionAllowlist, dir string) bool {
- if _, ok := allowlist.keepExistingBuildFile[dir]; ok {
+func (a Bp2BuildConversionAllowlist) ShouldKeepExistingBuildFileForDir(dir string) bool {
+ if _, ok := a.keepExistingBuildFile[dir]; ok {
// Exact dir match
return true
}
+ var i int
// Check if subtree match
- for prefix, recursive := range allowlist.keepExistingBuildFile {
- if recursive {
- if strings.HasPrefix(dir, prefix+"/") {
- return true
- }
+ for {
+ j := strings.Index(dir[i:], "/")
+ if j == -1 {
+ return false //default
+ }
+ prefix := dir[0 : i+j]
+ i = i + j + 1 // skip the "/"
+ if recursive, ok := a.keepExistingBuildFile[prefix]; ok && recursive {
+ return true
}
}
- // Default
- return false
}
-// MixedBuildsEnabled checks that a module is ready to be replaced by a
-// converted or handcrafted Bazel target.
-func (b *BazelModuleBase) MixedBuildsEnabled(ctx ModuleContext) bool {
- if ctx.Os() == Windows {
- // Windows toolchains are not currently supported.
- return false
- }
- if !ctx.Module().Enabled() {
- return false
- }
- if !ctx.Config().BazelContext.BazelEnabled() {
- return false
- }
- if !convertedToBazel(ctx, ctx.Module()) {
- return false
- }
+var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist")
+var bp2buildAllowlist OncePer
- if GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
- // Don't use partially-converted cc_library targets in mixed builds,
- // since mixed builds would generally rely on both static and shared
- // variants of a cc_library.
- return false
- }
- return !bp2buildAllowlist.mixedBuildsDisabled[ctx.Module().Name()]
+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)
+ }).(Bp2BuildConversionAllowlist)
+}
+
+// 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 {
+ module := ctx.Module()
+ apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
+ withinApex := !apexInfo.IsForPlatform()
+ mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
+ ctx.Os() != Windows && // Windows toolchains are not currently supported.
+ ctx.Os() != LinuxBionic && // Linux Bionic toolchains are not currently supported.
+ ctx.Os() != LinuxMusl && // Linux musl toolchains are not currently supported (b/259266326).
+ ctx.Arch().ArchType != Riscv64 && // TODO(b/262192655) Riscv64 toolchains are not currently supported.
+ module.Enabled() &&
+ convertedToBazel(ctx, module) &&
+ ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex)
+ ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
+ return mixedBuildEnabled
}
// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
@@ -391,6 +393,13 @@
return false
}
+ // In api_bp2build mode, all soong modules that can provide API contributions should be converted
+ // This is irrespective of its presence/absence in bp2build allowlists
+ if ctx.Config().BuildMode == ApiBp2build {
+ _, providesApis := module.(ApiProvider)
+ return providesApis
+ }
+
propValue := b.bazelProperties.Bazel_module.Bp2build_available
packagePath := ctx.OtherModuleDir(module)
@@ -402,7 +411,7 @@
}
moduleName := module.Name()
- allowlist := ctx.Config().bp2buildPackageConfig
+ allowlist := ctx.Config().Bp2buildPackageConfig
moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName]
moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[ctx.OtherModuleType(module)]
allowlistConvert := moduleNameAllowed || moduleTypeAllowed
@@ -418,20 +427,12 @@
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.
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)
+ " or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: '%s'"+
+ " Module: '%s'", directoryPath, moduleName)
return false
}
@@ -461,48 +462,32 @@
// Check if the package path has an exact match in the config.
if config[packagePath] == allowlists.Bp2BuildDefaultTrue || config[packagePath] == allowlists.Bp2BuildDefaultTrueRecursively {
return true, packagePath
- } else if config[packagePath] == allowlists.Bp2BuildDefaultFalse {
+ } else if config[packagePath] == allowlists.Bp2BuildDefaultFalse || config[packagePath] == allowlists.Bp2BuildDefaultFalseRecursively {
return false, packagePath
}
// If not, check for the config recursively.
- packagePrefix := ""
- // 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] == allowlists.Bp2BuildDefaultTrueRecursively {
+ packagePrefix := packagePath
+
+ // e.g. for x/y/z, iterate over x/y, then x, taking the most-specific value from the allowlist.
+ for strings.Contains(packagePrefix, "/") {
+ dirIndex := strings.LastIndex(packagePrefix, "/")
+ packagePrefix = packagePrefix[:dirIndex]
+ switch value := config[packagePrefix]; value {
+ case allowlists.Bp2BuildDefaultTrueRecursively:
// package contains this prefix and this prefix should convert all modules
return true, packagePrefix
+ case allowlists.Bp2BuildDefaultFalseRecursively:
+ //package contains this prefix and this prefix should NOT convert any modules
+ return false, packagePrefix
}
// Continue to the next part of the package dir.
- packagePrefix += "/"
+
}
return false, packagePath
}
-// GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or
-// an error if there are errors reading the file.
-// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
-// something more targeted based on the rule type and target.
-func (b *BazelModuleBase) GetBazelBuildFileContents(c Config, path, name string) (string, error) {
- if !strings.Contains(b.HandcraftedLabel(), path) {
- return "", fmt.Errorf("%q not found in bazel_module.label %q", path, b.HandcraftedLabel())
- }
- name = filepath.Join(path, name)
- f, err := c.fs.Open(name)
- if err != nil {
- return "", err
- }
- defer f.Close()
-
- data, err := ioutil.ReadAll(f)
- if err != nil {
- return "", err
- }
- return string(data[:]), nil
-}
-
func registerBp2buildConversionMutator(ctx RegisterMutatorsContext) {
ctx.TopDown("bp2build_conversion", convertWithBp2build).Parallel()
}
@@ -516,6 +501,17 @@
bModule.ConvertWithBp2build(ctx)
}
+func registerApiBp2buildConversionMutator(ctx RegisterMutatorsContext) {
+ ctx.TopDown("apiBp2build_conversion", convertWithApiBp2build).Parallel()
+}
+
+// Generate API contribution targets if the Soong module provides APIs
+func convertWithApiBp2build(ctx TopDownMutatorContext) {
+ if m, ok := ctx.Module().(ApiProvider); ok {
+ m.ConvertWithApiBp2build(ctx)
+ }
+}
+
// GetMainClassInManifest scans the manifest file specified in filepath and returns
// the value of attribute Main-Class in the manifest file if it exists, or returns error.
// WARNING: this is for bp2build converters of java_* modules only.
@@ -535,3 +531,15 @@
return "", errors.New("Main-Class is not found.")
}
+
+func AttachValidationActions(ctx ModuleContext, outputFilePath Path, validations Paths) ModuleOutPath {
+ validatedOutputFilePath := PathForModuleOut(ctx, "validated", outputFilePath.Base())
+ ctx.Build(pctx, BuildParams{
+ Rule: CpNoPreserveSymlink,
+ Description: "run validations " + outputFilePath.Base(),
+ Output: validatedOutputFilePath,
+ Input: outputFilePath,
+ Validations: validations,
+ })
+ return validatedOutputFilePath
+}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index d851a98..9c273d9 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -16,22 +16,87 @@
import (
"bytes"
- "errors"
"fmt"
- "io/ioutil"
"os"
"os/exec"
+ "path"
"path/filepath"
"runtime"
+ "sort"
"strings"
"sync"
+ "android/soong/android/allowlists"
"android/soong/bazel/cquery"
"android/soong/shared"
+ "android/soong/starlark_fmt"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/metrics"
"android/soong/bazel"
)
+var (
+ _ = pctx.HostBinToolVariable("bazelBuildRunfilesTool", "build-runfiles")
+ buildRunfilesRule = pctx.AndroidStaticRule("bazelBuildRunfiles", blueprint.RuleParams{
+ Command: "${bazelBuildRunfilesTool} ${in} ${outDir}",
+ Depfile: "",
+ Description: "",
+ CommandDeps: []string{"${bazelBuildRunfilesTool}"},
+ }, "outDir")
+ allowedBazelEnvironmentVars = []string{
+ // clang-tidy
+ "ALLOW_LOCAL_TIDY_TRUE",
+ "DEFAULT_TIDY_HEADER_DIRS",
+ "TIDY_TIMEOUT",
+ "WITH_TIDY",
+ "WITH_TIDY_FLAGS",
+ "TIDY_EXTERNAL_VENDOR",
+
+ "SKIP_ABI_CHECKS",
+ "UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK",
+ "AUTO_ZERO_INITIALIZE",
+ "AUTO_PATTERN_INITIALIZE",
+ "AUTO_UNINITIALIZE",
+ "USE_CCACHE",
+ "LLVM_NEXT",
+ "ALLOW_UNKNOWN_WARNING_OPTION",
+
+ "UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT",
+
+ // Overrides the version in the apex_manifest.json. The version is unique for
+ // each branch (internal, aosp, mainline releases, dessert releases). This
+ // enables modules built on an older branch to be installed against a newer
+ // device for development purposes.
+ "OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION",
+ }
+)
+
+func init() {
+ RegisterMixedBuildsMutator(InitRegistrationContext)
+}
+
+func RegisterMixedBuildsMutator(ctx RegistrationContext) {
+ ctx.FinalDepsMutators(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 {
+ queueMixedBuild := mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx)
+ if queueMixedBuild {
+ mixedBuildMod.QueueBazelCall(ctx)
+ } else if _, ok := ctx.Config().bazelForceEnabledModules[m.Name()]; ok {
+ // TODO(b/273910287) - remove this once --ensure_allowlist_integrity is added
+ ctx.ModuleErrorf("Attempted to force enable an unready module: %s. Did you forget to Bp2BuildDefaultTrue its directory?\n", m.Name())
+ }
+ }
+ }
+}
+
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.
@@ -42,7 +107,7 @@
// all request-relevant information about a target and returns a string containing
// this information.
// The function should have the following properties:
- // - `target` is the only parameter to this function (a configured target).
+ // - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
// - The return value must be a string.
// - The function body should not be indented outside of its own scope.
StarlarkFunctionBody() string
@@ -50,8 +115,29 @@
// Portion of cquery map key to describe target configuration.
type configKey struct {
- arch string
- osType OsType
+ arch string
+ osType OsType
+ apexKey ApexConfigKey
+}
+
+type ApexConfigKey struct {
+ WithinApex bool
+ ApexSdkVersion string
+}
+
+func (c ApexConfigKey) String() string {
+ return fmt.Sprintf("%s_%s", withinApexToString(c.WithinApex), c.ApexSdkVersion)
+}
+
+func withinApexToString(withinApex bool) string {
+ if withinApex {
+ return "within_apex"
+ }
+ return ""
+}
+
+func (c configKey) String() string {
+ return fmt.Sprintf("%s::%s::%s", c.arch, c.osType, c.apexKey)
}
// Map key to describe bazel cquery requests.
@@ -61,76 +147,129 @@
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
+func makeCqueryKey(label string, cqueryRequest cqueryRequest, cfgKey configKey) cqueryKey {
+ if strings.HasPrefix(label, "//") {
+ // Normalize Bazel labels to specify main repository explicitly.
+ label = "@" + label
+ }
+ return cqueryKey{label, cqueryRequest, cfgKey}
}
+func (c cqueryKey) String() string {
+ return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey)
+}
+
+type invokeBazelContext interface {
+ GetEventHandler() *metrics.EventHandler
+}
+
+// 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 methods below 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
+ // Returns the results of the GetApexInfo query (including output files)
+ GetApexInfo(label string, cfgkey configKey) (cquery.ApexInfo, error)
+
+ // Returns the results of the GetCcUnstrippedInfo query
+ GetCcUnstrippedInfo(label string, cfgkey configKey) (cquery.CcUnstrippedInfo, error)
+
+ // ** end Cquery Results Retrieval Functions
// Issues commands to Bazel to receive results for all cquery requests
- // queued in the BazelContext.
- InvokeBazel() error
+ // queued in the BazelContext. The ctx argument is optional and is only
+ // used for performance data collection
+ InvokeBazel(config Config, ctx invokeBazelContext) error
- // Returns true if bazel is enabled for the given configuration.
- BazelEnabled() bool
+ // Returns true if Bazel handling is enabled for the module with the given name.
+ // Note that this only implies "bazel mixed build" allowlisting. The caller
+ // should independently verify the module is eligible for Bazel handling
+ // (for example, that it is MixedBuildBuildable).
+ IsModuleNameAllowed(moduleName string, withinApex bool) bool
+
+ IsModuleDclaAllowed(moduleName string) bool
// Returns the bazel output base (the root directory for all bazel intermediate outputs).
OutputBase() string
// Returns build statements which should get registered to reflect Bazel's outputs.
- BuildStatementsToRegister() []bazel.BuildStatement
+ BuildStatementsToRegister() []*bazel.BuildStatement
+
+ // Returns the depsets defined in Bazel's aquery response.
+ AqueryDepsets() []bazel.AqueryDepset
}
type bazelRunner interface {
- issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) (string, string, error)
+ createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
+ issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (output string, errorMessage string, error error)
}
type bazelPaths struct {
- homeDir string
- bazelPath string
- outputBase string
- workspaceDir string
- soongOutDir string
- metricsDir string
+ homeDir string
+ bazelPath string
+ outputBase string
+ workspaceDir string
+ soongOutDir string
+ metricsDir string
+ bazelDepsFile string
}
// A context object which tracks queued requests that need to be made to Bazel,
// and their results after the requests have been made.
-type bazelContext struct {
+type mixedBuildBazelContext struct {
bazelRunner
- paths *bazelPaths
- requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
- requestMutex sync.Mutex // requests can be written in parallel
+ paths *bazelPaths
+ // cquery requests that have not yet been issued to Bazel. This list is maintained
+ // in a sorted state, and is guaranteed to have no duplicates.
+ requests []cqueryKey
+ requestMutex sync.Mutex // requests can be written in parallel
results map[cqueryKey]string // Results of cquery requests after Bazel invocations
// Build statements which should get registered to reflect Bazel's outputs.
- buildStatements []bazel.BuildStatement
+ buildStatements []*bazel.BuildStatement
+
+ // Depsets which should be used for Bazel's build statements.
+ depsets []bazel.AqueryDepset
+
+ // Per-module allowlist/denylist functionality to control whether analysis of
+ // modules are handled by Bazel. For modules which do not have a Bazel definition
+ // (or do not sufficiently support bazel handling via MixedBuildBuildable),
+ // this allowlist will have no effect, even if the module is explicitly allowlisted here.
+ // Per-module denylist to opt modules out of bazel handling.
+ bazelDisabledModules map[string]bool
+ // Per-module allowlist to opt modules in to bazel handling.
+ bazelEnabledModules map[string]bool
+ // DCLA modules are enabled when used in apex.
+ bazelDclaEnabledModules map[string]bool
+ // If true, modules are bazel-enabled by default, unless present in bazelDisabledModules.
+ modulesDefaultToBazel bool
+
+ targetProduct string
+ targetBuildVariant string
}
-var _ BazelContext = &bazelContext{}
+var _ BazelContext = &mixedBuildBazelContext{}
// A bazel context to use when Bazel is disabled.
type noopBazelContext struct{}
@@ -144,83 +283,207 @@
LabelToOutputFiles map[string][]string
LabelToCcInfo map[string]cquery.CcInfo
LabelToPythonBinary map[string]string
+ LabelToApexInfo map[string]cquery.ApexInfo
+ LabelToCcBinary map[string]cquery.CcUnstrippedInfo
+
+ BazelRequests map[string]bool
}
-func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
+func (m MockBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+ key := BuildMockBazelContextRequestKey(label, requestType, cfgKey.arch, cfgKey.osType, cfgKey.apexKey)
+ if m.BazelRequests == nil {
+ m.BazelRequests = make(map[string]bool)
+ }
+ m.BazelRequests[key] = true
+}
+
+func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
result, ok := m.LabelToOutputFiles[label]
- return result, ok
+ if !ok {
+ return []string{}, fmt.Errorf("no target with label %q in LabelToOutputFiles", label)
+ }
+ return result, nil
}
-func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
+func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
result, ok := m.LabelToCcInfo[label]
- return result, ok, nil
+ if !ok {
+ key := BuildMockBazelContextResultKey(label, cfgKey.arch, cfgKey.osType, cfgKey.apexKey)
+ result, ok = m.LabelToCcInfo[key]
+ if !ok {
+ return cquery.CcInfo{}, fmt.Errorf("no target with label %q in LabelToCcInfo", label)
+ }
+ }
+ return result, nil
}
-func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
+func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) {
result, ok := m.LabelToPythonBinary[label]
- return result, ok
+ if !ok {
+ return "", fmt.Errorf("no target with label %q in LabelToPythonBinary", label)
+ }
+ return result, nil
}
-func (m MockBazelContext) InvokeBazel() error {
+func (m MockBazelContext) GetApexInfo(label string, _ configKey) (cquery.ApexInfo, error) {
+ result, ok := m.LabelToApexInfo[label]
+ if !ok {
+ return cquery.ApexInfo{}, fmt.Errorf("no target with label %q in LabelToApexInfo", label)
+ }
+ return result, nil
+}
+
+func (m MockBazelContext) GetCcUnstrippedInfo(label string, _ configKey) (cquery.CcUnstrippedInfo, error) {
+ result, ok := m.LabelToCcBinary[label]
+ if !ok {
+ return cquery.CcUnstrippedInfo{}, fmt.Errorf("no target with label %q in LabelToCcBinary", label)
+ }
+ return result, nil
+}
+
+func (m MockBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
panic("unimplemented")
}
-func (m MockBazelContext) BazelEnabled() bool {
+func (m MockBazelContext) IsModuleNameAllowed(_ string, _ bool) bool {
+ return true
+}
+
+func (m MockBazelContext) IsModuleDclaAllowed(_ string) bool {
return true
}
func (m MockBazelContext) OutputBase() string { return m.OutputBaseDir }
-func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
- return []bazel.BuildStatement{}
+func (m MockBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
+ 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 BuildMockBazelContextRequestKey(label string, request cqueryRequest, arch string, osType OsType, apexKey ApexConfigKey) string {
+ cfgKey := configKey{
+ arch: arch,
+ osType: osType,
+ apexKey: apexKey,
+ }
+
+ return strings.Join([]string{label, request.Name(), cfgKey.String()}, "_")
+}
+
+func BuildMockBazelContextResultKey(label string, arch string, osType OsType, apexKey ApexConfigKey) string {
+ cfgKey := configKey{
+ arch: arch,
+ osType: osType,
+ apexKey: apexKey,
+ }
+
+ return strings.Join([]string{label, cfgKey.String()}, "_")
+}
+
+func (bazelCtx *mixedBuildBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+ key := makeCqueryKey(label, requestType, cfgKey)
+ bazelCtx.requestMutex.Lock()
+ defer bazelCtx.requestMutex.Unlock()
+
+ // Insert key into requests, maintaining the sort, and only if it's not duplicate.
+ keyString := key.String()
+ foundEqual := false
+ notLessThanKeyString := func(i int) bool {
+ s := bazelCtx.requests[i].String()
+ v := strings.Compare(s, keyString)
+ if v == 0 {
+ foundEqual = true
+ }
+ return v >= 0
+ }
+ targetIndex := sort.Search(len(bazelCtx.requests), notLessThanKeyString)
+ if foundEqual {
+ return
+ }
+
+ if targetIndex == len(bazelCtx.requests) {
+ bazelCtx.requests = append(bazelCtx.requests, key)
+ } else {
+ bazelCtx.requests = append(bazelCtx.requests[:targetIndex+1], bazelCtx.requests[targetIndex:]...)
+ bazelCtx.requests[targetIndex] = key
+ }
+}
+
+func (bazelCtx *mixedBuildBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
+ key := makeCqueryKey(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 *mixedBuildBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+ key := makeCqueryKey(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 *mixedBuildBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
+ key := makeCqueryKey(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 (bazelCtx *mixedBuildBazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexInfo, error) {
+ key := makeCqueryKey(label, cquery.GetApexInfo, cfgKey)
+ if rawString, ok := bazelCtx.results[key]; ok {
+ return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString))
+ }
+ return cquery.ApexInfo{}, fmt.Errorf("no bazel response found for %v", key)
+}
+
+func (bazelCtx *mixedBuildBazelContext) GetCcUnstrippedInfo(label string, cfgKey configKey) (cquery.CcUnstrippedInfo, error) {
+ key := makeCqueryKey(label, cquery.GetCcUnstrippedInfo, cfgKey)
+ if rawString, ok := bazelCtx.results[key]; ok {
+ return cquery.GetCcUnstrippedInfo.ParseResult(strings.TrimSpace(rawString))
+ }
+ return cquery.CcUnstrippedInfo{}, fmt.Errorf("no bazel response for %s", key)
+}
+
+func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
panic("unimplemented")
}
-func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
+func (n noopBazelContext) GetOutputFiles(_ string, _ configKey) ([]string, error) {
panic("unimplemented")
}
-func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
+func (n noopBazelContext) GetCcInfo(_ string, _ configKey) (cquery.CcInfo, error) {
panic("unimplemented")
}
-func (n noopBazelContext) InvokeBazel() error {
+func (n noopBazelContext) GetPythonBinary(_ string, _ configKey) (string, error) {
+ panic("unimplemented")
+}
+
+func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexInfo, error) {
+ panic("unimplemented")
+}
+
+func (n noopBazelContext) GetCcUnstrippedInfo(_ string, _ configKey) (cquery.CcUnstrippedInfo, error) {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (n noopBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
panic("unimplemented")
}
@@ -228,93 +491,159 @@
return ""
}
-func (n noopBazelContext) BazelEnabled() bool {
+func (n noopBazelContext) IsModuleNameAllowed(_ string, _ bool) bool {
return false
}
-func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
- return []bazel.BuildStatement{}
+func (n noopBazelContext) IsModuleDclaAllowed(_ string) bool {
+ return false
+}
+
+func (m noopBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
+ return []*bazel.BuildStatement{}
+}
+
+func (m noopBazelContext) AqueryDepsets() []bazel.AqueryDepset {
+ return []bazel.AqueryDepset{}
+}
+
+func addToStringSet(set map[string]bool, items []string) {
+ for _, item := range items {
+ set[item] = true
+ }
+}
+
+func GetBazelEnabledAndDisabledModules(buildMode SoongBuildMode, forceEnabled map[string]struct{}) (map[string]bool, map[string]bool) {
+ disabledModules := map[string]bool{}
+ enabledModules := map[string]bool{}
+
+ switch buildMode {
+ case BazelProdMode:
+ addToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
+ for enabledAdHocModule := range forceEnabled {
+ enabledModules[enabledAdHocModule] = true
+ }
+ case BazelStagingMode:
+ // Staging mode includes all prod modules plus all staging modules.
+ addToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
+ addToStringSet(enabledModules, allowlists.StagingMixedBuildsEnabledList)
+ for enabledAdHocModule := range forceEnabled {
+ enabledModules[enabledAdHocModule] = true
+ }
+ case BazelDevMode:
+ addToStringSet(disabledModules, allowlists.MixedBuildsDisabledList)
+ default:
+ panic("Expected BazelProdMode, BazelStagingMode, or BazelDevMode")
+ }
+ return enabledModules, disabledModules
+}
+
+func GetBazelEnabledModules(buildMode SoongBuildMode) []string {
+ enabledModules, disabledModules := GetBazelEnabledAndDisabledModules(buildMode, nil)
+ enabledList := make([]string, 0, len(enabledModules))
+ for module := range enabledModules {
+ if !disabledModules[module] {
+ enabledList = append(enabledList, module)
+ }
+ }
+ sort.Strings(enabledList)
+ return enabledList
}
func NewBazelContext(c *config) (BazelContext, error) {
- // TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
- // are production ready.
- if !c.IsEnvTrue("USE_BAZEL_ANALYSIS") {
+ if c.BuildMode != BazelProdMode && c.BuildMode != BazelStagingMode && c.BuildMode != BazelDevMode {
return noopBazelContext{}, nil
}
- p, err := bazelPathsFromConfig(c)
- if err != nil {
- return nil, err
- }
- return &bazelContext{
- bazelRunner: &builtinBazelRunner{},
- paths: p,
- requests: make(map[cqueryKey]bool),
- }, nil
-}
+ enabledModules, disabledModules := GetBazelEnabledAndDisabledModules(c.BuildMode, c.BazelModulesForceEnabledByFlag())
-func bazelPathsFromConfig(c *config) (*bazelPaths, error) {
- p := bazelPaths{
+ paths := bazelPaths{
soongOutDir: c.soongOutDir,
}
- missingEnvVars := []string{}
- if len(c.Getenv("BAZEL_HOME")) > 1 {
- p.homeDir = c.Getenv("BAZEL_HOME")
- } else {
- missingEnvVars = append(missingEnvVars, "BAZEL_HOME")
+ var missing []string
+ vars := []struct {
+ name string
+ ptr *string
+
+ // True if the environment variable needs to be tracked so that changes to the variable
+ // cause the ninja file to be regenerated, false otherwise. False should only be set for
+ // environment variables that have no effect on the generated ninja file.
+ track bool
+ }{
+ {"BAZEL_HOME", &paths.homeDir, true},
+ {"BAZEL_PATH", &paths.bazelPath, true},
+ {"BAZEL_OUTPUT_BASE", &paths.outputBase, true},
+ {"BAZEL_WORKSPACE", &paths.workspaceDir, true},
+ {"BAZEL_METRICS_DIR", &paths.metricsDir, false},
+ {"BAZEL_DEPS_FILE", &paths.bazelDepsFile, true},
}
- if len(c.Getenv("BAZEL_PATH")) > 1 {
- p.bazelPath = c.Getenv("BAZEL_PATH")
- } else {
- missingEnvVars = append(missingEnvVars, "BAZEL_PATH")
+ for _, v := range vars {
+ if v.track {
+ if s := c.Getenv(v.name); len(s) > 1 {
+ *v.ptr = s
+ continue
+ }
+ } else if s, ok := c.env[v.name]; ok {
+ *v.ptr = s
+ } else {
+ missing = append(missing, v.name)
+ }
}
- if len(c.Getenv("BAZEL_OUTPUT_BASE")) > 1 {
- p.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
- } else {
- missingEnvVars = append(missingEnvVars, "BAZEL_OUTPUT_BASE")
+ if len(missing) > 0 {
+ return nil, fmt.Errorf("missing required env vars to use bazel: %s", missing)
}
- if len(c.Getenv("BAZEL_WORKSPACE")) > 1 {
- p.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
- } else {
- missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
+
+ targetBuildVariant := "user"
+ if c.Eng() {
+ targetBuildVariant = "eng"
+ } else if c.Debuggable() {
+ targetBuildVariant = "userdebug"
}
- if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
- p.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
- } else {
- missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
+ targetProduct := "unknown"
+ if c.HasDeviceProduct() {
+ targetProduct = c.DeviceProduct()
}
- if len(missingEnvVars) > 0 {
- return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
- } else {
- return &p, nil
+ dclaMixedBuildsEnabledList := []string{}
+ if c.BuildMode == BazelProdMode {
+ dclaMixedBuildsEnabledList = allowlists.ProdDclaMixedBuildsEnabledList
+ } else if c.BuildMode == BazelStagingMode {
+ dclaMixedBuildsEnabledList = append(allowlists.ProdDclaMixedBuildsEnabledList,
+ allowlists.StagingDclaMixedBuildsEnabledList...)
}
+ dclaEnabledModules := map[string]bool{}
+ addToStringSet(dclaEnabledModules, dclaMixedBuildsEnabledList)
+ return &mixedBuildBazelContext{
+ bazelRunner: &builtinBazelRunner{c.UseBazelProxy, absolutePath(c.outDir)},
+ paths: &paths,
+ modulesDefaultToBazel: c.BuildMode == BazelDevMode,
+ bazelEnabledModules: enabledModules,
+ bazelDisabledModules: disabledModules,
+ bazelDclaEnabledModules: dclaEnabledModules,
+ targetProduct: targetProduct,
+ targetBuildVariant: targetBuildVariant,
+ }, nil
}
func (p *bazelPaths) BazelMetricsDir() string {
return p.metricsDir
}
-func (context *bazelContext) BazelEnabled() bool {
- return true
+func (context *mixedBuildBazelContext) IsModuleNameAllowed(moduleName string, withinApex bool) bool {
+ if context.bazelDisabledModules[moduleName] {
+ return false
+ }
+ if context.bazelEnabledModules[moduleName] {
+ return true
+ }
+ if withinApex && context.IsModuleDclaAllowed(moduleName) {
+ return true
+ }
+
+ return context.modulesDefaultToBazel
}
-// 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 (context *mixedBuildBazelContext) IsModuleDclaAllowed(moduleName string) bool {
+ return context.bazelDclaEnabledModules[moduleName]
}
func pwdPrefix() string {
@@ -333,102 +662,168 @@
type mockBazelRunner struct {
bazelCommandResults map[bazelCommand]string
- commands []bazelCommand
+ // use *exec.Cmd as a key to get the bazelCommand, the map will be used in issueBazelCommand()
+ // Register createBazelCommand() invocations. Later, an
+ // issueBazelCommand() invocation can be mapped to the *exec.Cmd instance
+ // and then to the expected result via bazelCommandResults
+ tokens map[*exec.Cmd]bazelCommand
+ commands []bazelCommand
+ extraFlags []string
}
-func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
- runName bazel.RunName,
- command bazelCommand,
- extraFlags ...string) (string, string, error) {
+func (r *mockBazelRunner) createBazelCommand(_ Config, _ *bazelPaths, _ bazel.RunName,
+ command bazelCommand, extraFlags ...string) *exec.Cmd {
r.commands = append(r.commands, command)
- if ret, ok := r.bazelCommandResults[command]; ok {
- return ret, "", nil
+ r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
+ cmd := &exec.Cmd{}
+ if r.tokens == nil {
+ r.tokens = make(map[*exec.Cmd]bazelCommand)
+ }
+ r.tokens[cmd] = command
+ return cmd
+}
+
+func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, _ *metrics.EventHandler) (string, string, error) {
+ if command, ok := r.tokens[bazelCmd]; ok {
+ return r.bazelCommandResults[command], "", nil
}
return "", "", nil
}
-type builtinBazelRunner struct{}
+type builtinBazelRunner struct {
+ useBazelProxy bool
+ outDir string
+}
// Issues the given bazel command with given build label and additional flags.
// Returns (stdout, stderr, error). The first and second return values are strings
// containing the stdout and stderr of the run command, and an error is returned if
// the invocation returned an error code.
-func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
- extraFlags ...string) (string, string, error) {
+func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (string, string, error) {
+ if r.useBazelProxy {
+ eventHandler.Begin("client_proxy")
+ defer eventHandler.End("client_proxy")
+ proxyClient := bazel.NewProxyClient(r.outDir)
+ // Omit the arg containing the Bazel binary, as that is handled by the proxy
+ // server.
+ bazelFlags := bazelCmd.Args[1:]
+ // TODO(b/270989498): Refactor these functions to not take exec.Cmd, as its
+ // not actually executed for client proxying.
+ resp, err := proxyClient.IssueCommand(bazel.CmdRequest{bazelFlags, bazelCmd.Env})
+
+ if err != nil {
+ return "", "", err
+ }
+ if len(resp.ErrorString) > 0 {
+ return "", "", fmt.Errorf(resp.ErrorString)
+ }
+ return resp.Stdout, resp.Stderr, nil
+ } else {
+ eventHandler.Begin("bazel command")
+ defer eventHandler.End("bazel command")
+ stderr := &bytes.Buffer{}
+ bazelCmd.Stderr = stderr
+ if output, err := bazelCmd.Output(); err != nil {
+ return "", string(stderr.Bytes()),
+ fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
+ err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
+ } else {
+ return string(output), string(stderr.Bytes()), nil
+ }
+ }
+}
+
+func (r *builtinBazelRunner) createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand,
+ extraFlags ...string) *exec.Cmd {
cmdFlags := []string{
- // --noautodetect_server_javabase has the practical consequence of preventing Bazel from
- // attempting to download rules_java, which is incompatible with
- // --experimental_repository_disable_download set further below.
- // rules_java is also not needed until mixed builds start building java targets.
- // TODO(b/197958133): Once rules_java is pulled into AOSP, remove this flag.
- "--noautodetect_server_javabase",
"--output_base=" + absolutePath(paths.outputBase),
command.command,
+ command.expression,
+ // TODO(asmundak): is it needed in every build?
+ "--profile=" + shared.BazelMetricsFilename(paths, runName),
+
+ // We don't need to set --host_platforms because it's set in bazelrc files
+ // that the bazel shell script wrapper passes
+
+ // Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
+ "--experimental_repository_disable_download",
+
+ // Suppress noise
+ "--ui_event_filters=-INFO",
+ "--noshow_progress",
+ "--norun_validations",
}
- cmdFlags = append(cmdFlags, command.expression)
- cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
-
- // Set default platforms to canonicalized values for mixed builds requests.
- // If these are set in the bazelrc, they will have values that are
- // non-canonicalized to @sourceroot labels, and thus be invalid when
- // referenced from the buildroot.
- //
- // The actual platform values here may be overridden by configuration
- // transitions from the buildroot.
- cmdFlags = append(cmdFlags,
- fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_target"))
- cmdFlags = append(cmdFlags,
- fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"))
- // This should be parameterized on the host OS, but let's restrict to linux
- // to keep things simple for now.
- cmdFlags = append(cmdFlags,
- fmt.Sprintf("--host_platform=%s", "//build/bazel/platforms:linux_x86_64"))
-
- // Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
- cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
cmdFlags = append(cmdFlags, extraFlags...)
bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir())
- bazelCmd.Env = append(os.Environ(),
- "HOME="+paths.homeDir,
+ extraEnv := []string{
+ "HOME=" + paths.homeDir,
pwdPrefix(),
- "BUILD_DIR="+absolutePath(paths.soongOutDir),
- // Make OUT_DIR absolute here so tools/bazel.sh uses the correct
+ "BUILD_DIR=" + absolutePath(paths.soongOutDir),
+ // Make OUT_DIR absolute here so build/bazel/bin/bazel uses the correct
// OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
- "OUT_DIR="+absolutePath(paths.outDir()),
+ "OUT_DIR=" + absolutePath(paths.outDir()),
// Disables local host detection of gcc; toolchain information is defined
// explicitly in BUILD files.
- "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
- stderr := &bytes.Buffer{}
- bazelCmd.Stderr = stderr
-
- if output, err := bazelCmd.Output(); err != nil {
- return "", string(stderr.Bytes()),
- fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
- } else {
- return string(output), string(stderr.Bytes()), nil
+ "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
}
+ for _, envvar := range allowedBazelEnvironmentVars {
+ val := config.Getenv(envvar)
+ if val == "" {
+ continue
+ }
+ extraEnv = append(extraEnv, fmt.Sprintf("%s=%s", envvar, val))
+ }
+ bazelCmd.Env = append(os.Environ(), extraEnv...)
+
+ return bazelCmd
}
-func (context *bazelContext) mainBzlFileContents() []byte {
+func printableCqueryCommand(bazelCmd *exec.Cmd) string {
+ outputString := strings.Join(bazelCmd.Env, " ") + " \"" + strings.Join(bazelCmd.Args, "\" \"") + "\""
+ return outputString
+
+}
+
+func (context *mixedBuildBazelContext) mainBzlFileContents() []byte {
// TODO(cparsons): Define configuration transitions programmatically based
// on available archs.
contents := `
#####################################################
# This file is generated by soong_build. Do not edit.
#####################################################
-
def _config_node_transition_impl(settings, attr):
- return {
- "//command_line_option:platforms": "@//build/bazel/platforms:%s_%s" % (attr.os, attr.arch),
+ if attr.os == "android" and attr.arch == "target":
+ target = "{PRODUCT}-{VARIANT}"
+ else:
+ target = "{PRODUCT}-{VARIANT}_%s_%s" % (attr.os, attr.arch)
+ apex_name = ""
+ if attr.within_apex:
+ # //build/bazel/rules/apex:apex_name has to be set to a non_empty value,
+ # otherwise //build/bazel/rules/apex:non_apex will be true and the
+ # "-D__ANDROID_APEX__" compiler flag will be missing. Apex_name is used
+ # in some validation on bazel side which don't really apply in mixed
+ # build because soong will do the work, so we just set it to a fixed
+ # value here.
+ apex_name = "dcla_apex"
+ outputs = {
+ "//command_line_option:platforms": "@soong_injection//product_config_platforms/products/{PRODUCT}-{VARIANT}:%s" % target,
+ "@//build/bazel/rules/apex:within_apex": attr.within_apex,
+ "@//build/bazel/rules/apex:min_sdk_version": attr.apex_sdk_version,
+ "@//build/bazel/rules/apex:apex_name": apex_name,
}
+ return outputs
+
_config_node_transition = transition(
implementation = _config_node_transition_impl,
inputs = [],
outputs = [
"//command_line_option:platforms",
+ "@//build/bazel/rules/apex:within_apex",
+ "@//build/bazel/rules/apex:min_sdk_version",
+ "@//build/bazel/rules/apex:apex_name",
],
)
@@ -438,9 +833,11 @@
config_node = rule(
implementation = _passthrough_rule_impl,
attrs = {
- "arch" : attr.string(mandatory = True),
- "os" : attr.string(mandatory = True),
- "deps" : attr.label_list(cfg = _config_node_transition, allow_files = True),
+ "arch" : attr.string(mandatory = True),
+ "os" : attr.string(mandatory = True),
+ "within_apex" : attr.bool(default = False),
+ "apex_sdk_version" : attr.string(mandatory = True),
+ "deps" : attr.label_list(cfg = _config_node_transition, allow_files = True),
"_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
},
)
@@ -468,10 +865,15 @@
attrs = {"deps" : attr.label_list()},
)
`
- return []byte(contents)
+
+ productReplacer := strings.NewReplacer(
+ "{PRODUCT}", context.targetProduct,
+ "{VARIANT}", context.targetBuildVariant)
+
+ return []byte(productReplacer.Replace(contents))
}
-func (context *bazelContext) mainBuildFileContents() []byte {
+func (context *mixedBuildBazelContext) mainBuildFileContents() []byte {
// TODO(cparsons): Map label to attribute programmatically; don't use hard-coded
// architecture mapping.
formatString := `
@@ -482,41 +884,68 @@
mixed_build_root(name = "buildroot",
deps = [%s],
+ testonly = True, # Unblocks testonly deps.
)
phony_root(name = "phonyroot",
deps = [":buildroot"],
+ testonly = True, # Unblocks testonly deps.
)
`
configNodeFormatString := `
config_node(name = "%s",
arch = "%s",
os = "%s",
+ within_apex = %s,
+ apex_sdk_version = "%s",
deps = [%s],
+ testonly = True, # Unblocks testonly deps.
)
`
configNodesSection := ""
labelsByConfig := map[string][]string{}
- for val, _ := range context.requests {
+
+ for _, val := range context.requests {
labelString := fmt.Sprintf("\"@%s\"", val.label)
configString := getConfigString(val)
labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
}
+ // Configs need to be sorted to maintain determinism of the BUILD file.
+ sortedConfigs := make([]string, 0, len(labelsByConfig))
+ for val := range labelsByConfig {
+ sortedConfigs = append(sortedConfigs, val)
+ }
+ sort.Slice(sortedConfigs, func(i, j int) bool { return sortedConfigs[i] < sortedConfigs[j] })
+
allLabels := []string{}
- for configString, labels := range labelsByConfig {
+ for _, configString := range sortedConfigs {
+ labels := labelsByConfig[configString]
configTokens := strings.Split(configString, "|")
- if len(configTokens) != 2 {
+ if len(configTokens) < 2 {
panic(fmt.Errorf("Unexpected config string format: %s", configString))
}
archString := configTokens[0]
osString := configTokens[1]
+ withinApex := "False"
+ apexSdkVerString := ""
targetString := fmt.Sprintf("%s_%s", osString, archString)
+ if len(configTokens) > 2 {
+ targetString += "_" + configTokens[2]
+ if configTokens[2] == withinApexToString(true) {
+ withinApex = "True"
+ }
+ }
+ if len(configTokens) > 3 {
+ targetString += "_" + configTokens[3]
+ apexSdkVerString = configTokens[3]
+ }
allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString))
labelsString := strings.Join(labels, ",\n ")
- configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, labelsString)
+ configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, withinApex, apexSdkVerString,
+ labelsString)
}
return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n ")))
@@ -532,14 +961,18 @@
// Returns the file contents of the buildroot.cquery file that should be used for the cquery
// expression in order to obtain information about buildroot and its dependencies.
-// The contents of this file depend on the bazelContext's requests; requests are enumerated
+// The contents of this file depend on the mixedBuildBazelContext's requests; requests are enumerated
// and grouped by their request type. The data retrieved for each label depends on its
// request type.
-func (context *bazelContext) cqueryStarlarkFileContents() []byte {
+func (context *mixedBuildBazelContext) cqueryStarlarkFileContents() []byte {
requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
- for val, _ := range context.requests {
+ requestTypes := []cqueryRequest{}
+ for _, val := range context.requests {
cqueryId := getCqueryId(val)
mapEntryString := fmt.Sprintf("%q : True", cqueryId)
+ if _, seenKey := requestTypeToCqueryIdEntries[val.requestType]; !seenKey {
+ requestTypes = append(requestTypes, val.requestType)
+ }
requestTypeToCqueryIdEntries[val.requestType] =
append(requestTypeToCqueryIdEntries[val.requestType], mapEntryString)
}
@@ -553,15 +986,15 @@
}
`
functionDefFormatString := `
-def %s(target):
+def %s(target, id_string):
%s
`
mainSwitchSectionFormatString := `
if id_string in %s:
- return id_string + ">>" + %s(target)
+ return id_string + ">>" + %s(target, id_string)
`
- for requestType := range requestTypeToCqueryIdEntries {
+ for _, requestType := range requestTypes {
labelMapName := requestType.Name() + "_Labels"
functionName := requestType.Name() + "_Fn"
labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString,
@@ -577,11 +1010,9 @@
formatString := `
# This file is generated by soong_build. Do not edit.
-# Label Map Section
-%s
+{LABEL_REGISTRATION_MAP_SECTION}
-# Function Def Section
-%s
+{FUNCTION_DEF_SECTION}
def get_arch(target):
# TODO(b/199363072): filegroups and file targets aren't associated with any
@@ -589,39 +1020,64 @@
# Soong treats filegroups, but it may not be the case with manually-written
# filegroup BUILD targets.
buildoptions = build_options(target)
+
if buildoptions == None:
# File targets do not have buildoptions. File targets aren't associated with
# any specific platform architecture in mixed builds, so use the host.
return "x86_64|linux"
- platforms = build_options(target)["//command_line_option:platforms"]
+ platforms = buildoptions["//command_line_option:platforms"]
if len(platforms) != 1:
# An individual configured target should have only one platform architecture.
# Note that it's fine for there to be multiple architectures for the same label,
# but each is its own configured target.
fail("expected exactly 1 platform for " + str(target.label) + " but got " + str(platforms))
- platform_name = build_options(target)["//command_line_option:platforms"][0].name
+ platform_name = platforms[0].name
if platform_name == "host":
return "HOST"
+ if not platform_name.startswith("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}"):
+ fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
+ platform_name = platform_name.removeprefix("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}").removeprefix("_")
+ config_key = ""
+ if not platform_name:
+ config_key = "target|android"
elif platform_name.startswith("android_"):
- return platform_name[len("android_"):] + "|" + platform_name[:len("android_")-1]
+ config_key = platform_name.removeprefix("android_") + "|android"
elif platform_name.startswith("linux_"):
- return platform_name[len("linux_"):] + "|" + platform_name[:len("linux_")-1]
+ config_key = platform_name.removeprefix("linux_") + "|linux"
else:
- fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
- return "UNKNOWN"
+ fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
+
+ within_apex = buildoptions.get("//build/bazel/rules/apex:within_apex")
+ apex_sdk_version = buildoptions.get("//build/bazel/rules/apex:min_sdk_version")
+
+ if within_apex:
+ config_key += "|within_apex"
+ if apex_sdk_version != None and len(apex_sdk_version) > 0:
+ config_key += "|" + apex_sdk_version
+
+ return config_key
def format(target):
id_string = str(target.label) + "|" + get_arch(target)
- # Main switch section
- %s
+ # TODO(b/248106697): Remove once Bazel is updated to always normalize labels.
+ if id_string.startswith("//"):
+ id_string = "@" + id_string
+
+ {MAIN_SWITCH_SECTION}
+
# This target was not requested via cquery, and thus must be a dependency
# of a requested target.
return id_string + ">>NONE"
`
+ replacer := strings.NewReplacer(
+ "{TARGET_PRODUCT}", context.targetProduct,
+ "{TARGET_BUILD_VARIANT}", context.targetBuildVariant,
+ "{LABEL_REGISTRATION_MAP_SECTION}", labelRegistrationMapSection,
+ "{FUNCTION_DEF_SECTION}", functionDefSection,
+ "{MAIN_SWITCH_SECTION}", mainSwitchSection)
- return []byte(fmt.Sprintf(formatString, labelRegistrationMapSection, functionDefSection,
- mainSwitchSection))
+ return []byte(replacer.Replace(formatString))
}
// Returns a path containing build-related metadata required for interfacing
@@ -647,72 +1103,82 @@
return filepath.Dir(p.soongOutDir)
}
+const buildrootLabel = "@soong_injection//mixed_builds:buildroot"
+
+var (
+ cqueryCmd = bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
+ aqueryCmd = bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}
+ buildCmd = bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
+)
+
// Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext.
-func (context *bazelContext) InvokeBazel() error {
+func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx invokeBazelContext) error {
+ eventHandler := ctx.GetEventHandler()
+ eventHandler.Begin("bazel")
+ defer eventHandler.End("bazel")
+
+ if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
+ if err := os.MkdirAll(metricsDir, 0777); err != nil {
+ return err
+ }
+ }
context.results = make(map[cqueryKey]string)
+ if err := context.runCquery(config, ctx); err != nil {
+ return err
+ }
+ if err := context.runAquery(config, ctx); err != nil {
+ return err
+ }
+ if err := context.generateBazelSymlinks(config, ctx); err != nil {
+ return err
+ }
- var cqueryOutput string
- var cqueryErr string
- var err error
+ // Clear requests.
+ context.requests = []cqueryKey{}
+ return nil
+}
+func (context *mixedBuildBazelContext) runCquery(config Config, ctx invokeBazelContext) error {
+ eventHandler := ctx.GetEventHandler()
+ eventHandler.Begin("cquery")
+ defer eventHandler.End("cquery")
soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds")
if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) {
err = os.MkdirAll(mixedBuildsPath, 0777)
- }
- 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 {
+ if err := writeFileBytesIfChanged(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
return err
}
-
- err = ioutil.WriteFile(
- filepath.Join(mixedBuildsPath, "main.bzl"),
- context.mainBzlFileContents(), 0666)
- if err != nil {
+ if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
return err
}
-
- err = ioutil.WriteFile(
- filepath.Join(mixedBuildsPath, "BUILD.bazel"),
- context.mainBuildFileContents(), 0666)
- if err != nil {
+ if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
return err
}
cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
- err = ioutil.WriteFile(
- absolutePath(cqueryFileRelpath),
- context.cqueryStarlarkFileContents(), 0666)
- if err != nil {
+ if err := writeFileBytesIfChanged(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
return err
}
- buildrootLabel := "@soong_injection//mixed_builds:buildroot"
- cqueryOutput, cqueryErr, err = context.issueBazelCommand(
- context.paths,
- bazel.CqueryBuildRootRunName,
- bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)},
- "--output=starlark",
- "--starlark:file="+absolutePath(cqueryFileRelpath))
- err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"),
- []byte(cqueryOutput), 0666)
- if err != nil {
- return err
+ extraFlags := []string{"--output=starlark", "--starlark:file=" + absolutePath(cqueryFileRelpath)}
+ if Bool(config.productVariables.ClangCoverage) {
+ extraFlags = append(extraFlags, "--collect_code_coverage")
}
- if err != nil {
+ cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd, extraFlags...)
+ cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag, eventHandler)
+ if cqueryErr != nil {
+ return cqueryErr
+ }
+ cqueryCommandPrint := fmt.Sprintf("cquery command line:\n %s \n\n\n", printableCqueryCommand(cqueryCommandWithFlag))
+ if err := os.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil {
return err
}
-
cqueryResults := map[string]string{}
for _, outputLine := range strings.Split(cqueryOutput, "\n") {
if strings.Contains(outputLine, ">>") {
@@ -720,59 +1186,82 @@
cqueryResults[splitLine[0]] = splitLine[1]
}
}
-
- for val := range context.requests {
+ for _, val := range context.requests {
if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
context.results[val] = cqueryResult
} else {
return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
- getCqueryId(val), cqueryOutput, cqueryErr)
+ getCqueryId(val), cqueryOutput, cqueryErrorMessage)
}
}
-
- // Issue an aquery command to retrieve action information about the bazel build tree.
- //
- // TODO(cparsons): Use --target_pattern_file to avoid command line limits.
- var aqueryOutput string
- aqueryOutput, _, err = context.issueBazelCommand(
- context.paths,
- bazel.AqueryBuildRootRunName,
- bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
- // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
- // proto sources, which would add a number of unnecessary dependencies.
- "--output=jsonproto")
-
- if err != nil {
- return err
- }
-
- context.buildStatements, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
- if err != nil {
- return err
- }
-
- // Issue a build command of the phony root to generate symlink forests for dependencies of the
- // Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
- // but some of symlinks may be required to resolve source dependencies of the build.
- _, _, err = context.issueBazelCommand(
- context.paths,
- bazel.BazelBuildPhonyRootRunName,
- bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"})
-
- if err != nil {
- return err
- }
-
- // Clear requests.
- context.requests = map[cqueryKey]bool{}
return nil
}
-func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+func writeFileBytesIfChanged(path string, contents []byte, perm os.FileMode) error {
+ oldContents, err := os.ReadFile(path)
+ if err != nil || !bytes.Equal(contents, oldContents) {
+ err = os.WriteFile(path, contents, perm)
+ }
+ return nil
+}
+
+func (context *mixedBuildBazelContext) runAquery(config Config, ctx invokeBazelContext) error {
+ eventHandler := ctx.GetEventHandler()
+ eventHandler.Begin("aquery")
+ defer eventHandler.End("aquery")
+ // Issue an aquery command to retrieve action information about the bazel build tree.
+ //
+ // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
+ // proto sources, which would add a number of unnecessary dependencies.
+ extraFlags := []string{"--output=proto", "--include_file_write_contents"}
+ if Bool(config.productVariables.ClangCoverage) {
+ extraFlags = append(extraFlags, "--collect_code_coverage")
+ paths := make([]string, 0, 2)
+ if p := config.productVariables.NativeCoveragePaths; len(p) > 0 {
+ for i := range p {
+ // TODO(b/259404593) convert path wildcard to regex values
+ if p[i] == "*" {
+ p[i] = ".*"
+ }
+ }
+ paths = append(paths, JoinWithPrefixAndSeparator(p, "+", ","))
+ }
+ if p := config.productVariables.NativeCoverageExcludePaths; len(p) > 0 {
+ paths = append(paths, JoinWithPrefixAndSeparator(p, "-", ","))
+ }
+ if len(paths) > 0 {
+ extraFlags = append(extraFlags, "--instrumentation_filter="+strings.Join(paths, ","))
+ }
+ }
+ aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
+ extraFlags...), eventHandler)
+ if err != nil {
+ return err
+ }
+ context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput), eventHandler)
+ return err
+}
+
+func (context *mixedBuildBazelContext) generateBazelSymlinks(config Config, ctx invokeBazelContext) error {
+ eventHandler := ctx.GetEventHandler()
+ eventHandler.Begin("symlinks")
+ defer eventHandler.End("symlinks")
+ // Issue a build command of the phony root to generate symlink forests for dependencies of the
+ // Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
+ // but some of symlinks may be required to resolve source dependencies of the build.
+ _, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd), eventHandler)
+ return err
+}
+
+func (context *mixedBuildBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
return context.buildStatements
}
-func (context *bazelContext) OutputBase() string {
+func (context *mixedBuildBazelContext) AqueryDepsets() []bazel.AqueryDepset {
+ return context.depsets
+}
+
+func (context *mixedBuildBazelContext) OutputBase() string {
return context.paths.outputBase
}
@@ -786,7 +1275,7 @@
func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
// bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
- if !ctx.Config().BazelContext.BazelEnabled() {
+ if !ctx.Config().IsMixedBuildsEnabled() {
return
}
@@ -795,7 +1284,7 @@
filepath.Dir(ctx.Config().moduleListFile), "bazel.list"))
ctx.AddNinjaFileDeps(bazelBuildList)
- data, err := ioutil.ReadFile(bazelBuildList)
+ data, err := os.ReadFile(bazelBuildList)
if err != nil {
ctx.Errorf(err.Error())
}
@@ -804,56 +1293,138 @@
ctx.AddNinjaFileDeps(file)
}
- // Register bazel-owned build statements (obtained from the aquery invocation).
+ for _, depset := range ctx.Config().BazelContext.AqueryDepsets() {
+ var outputs []Path
+ var orderOnlies []Path
+ for _, depsetDepHash := range depset.TransitiveDepSetHashes {
+ otherDepsetName := bazelDepsetName(depsetDepHash)
+ outputs = append(outputs, PathForPhony(ctx, otherDepsetName))
+ }
+ for _, artifactPath := range depset.DirectArtifacts {
+ pathInBazelOut := PathForBazelOut(ctx, artifactPath)
+ if artifactPath == "bazel-out/volatile-status.txt" {
+ // See https://bazel.build/docs/user-manual#workspace-status
+ orderOnlies = append(orderOnlies, pathInBazelOut)
+ } else {
+ outputs = append(outputs, pathInBazelOut)
+ }
+ }
+ thisDepsetName := bazelDepsetName(depset.ContentHash)
+ ctx.Build(pctx, BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []WritablePath{PathForPhony(ctx, thisDepsetName)},
+ Implicits: outputs,
+ OrderOnly: orderOnlies,
+ })
+ }
+
+ executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
+ bazelOutDir := path.Join(executionRoot, "bazel-out")
for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
- if len(buildStatement.Command) < 1 {
+ // nil build statements are a valid case where we do not create an action because it is
+ // unnecessary or handled by other processing
+ if buildStatement == nil {
+ continue
+ }
+ if len(buildStatement.Command) > 0 {
+ rule := NewRuleBuilder(pctx, ctx)
+ createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
+ desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
+ rule.Build(fmt.Sprintf("bazel %d", index), desc)
+ continue
+ }
+ // Certain actions returned by aquery (for instance FileWrite) do not contain a command
+ // and thus require special treatment. If BuildStatement were an interface implementing
+ // buildRule(ctx) function, the code here would just call it.
+ // Unfortunately, the BuildStatement is defined in
+ // the 'bazel' package, which cannot depend on 'android' package where ctx is defined,
+ // because this would cause circular dependency. So, until we move aquery processing
+ // to the 'android' package, we need to handle special cases here.
+ switch buildStatement.Mnemonic {
+ case "FileWrite", "SourceSymlinkManifest":
+ out := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
+ WriteFileRuleVerbatim(ctx, out, buildStatement.FileContents)
+ case "SymlinkTree":
+ // build-runfiles arguments are the manifest file and the target directory
+ // where it creates the symlink tree according to this manifest (and then
+ // writes the MANIFEST file to it).
+ outManifest := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
+ outManifestPath := outManifest.String()
+ if !strings.HasSuffix(outManifestPath, "MANIFEST") {
+ panic("the base name of the symlink tree action should be MANIFEST, got " + outManifestPath)
+ }
+ outDir := filepath.Dir(outManifestPath)
+ ctx.Build(pctx, BuildParams{
+ Rule: buildRunfilesRule,
+ Output: outManifest,
+ Inputs: []Path{PathForBazelOut(ctx, buildStatement.InputPaths[0])},
+ Description: "symlink tree for " + outDir,
+ Args: map[string]string{
+ "outDir": outDir,
+ },
+ })
+ default:
panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
}
- rule := NewRuleBuilder(pctx, ctx)
- cmd := rule.Command()
+ }
+}
- // cd into Bazel's execution root, which is the action cwd.
- cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ &&", ctx.Config().BazelContext.OutputBase()))
+// Register bazel-owned build statements (obtained from the aquery invocation).
+func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext) {
+ // executionRoot is the action cwd.
+ cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
- // Remove old outputs, as some actions might not rerun if the outputs are detected.
- if len(buildStatement.OutputPaths) > 0 {
- cmd.Text("rm -f")
- for _, outputPath := range buildStatement.OutputPaths {
- cmd.Text(outputPath)
- }
- cmd.Text("&&")
- }
-
- for _, pair := range buildStatement.Env {
- // Set per-action env variables, if any.
- cmd.Flag(pair.Key + "=" + pair.Value)
- }
-
- // The actual Bazel action.
- cmd.Text(" " + buildStatement.Command)
-
+ // Remove old outputs, as some actions might not rerun if the outputs are detected.
+ if len(buildStatement.OutputPaths) > 0 {
+ cmd.Text("rm -rf") // -r because outputs can be Bazel dir/tree artifacts.
for _, outputPath := range buildStatement.OutputPaths {
- cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
+ cmd.Text(fmt.Sprintf("'%s'", outputPath))
}
- for _, inputPath := range buildStatement.InputPaths {
- cmd.Implicit(PathForBazelOut(ctx, inputPath))
- }
+ cmd.Text("&&")
+ }
- if depfile := buildStatement.Depfile; depfile != nil {
- cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
- }
+ for _, pair := range buildStatement.Env {
+ // Set per-action env variables, if any.
+ cmd.Flag(pair.Key + "=" + pair.Value)
+ }
- for _, symlinkPath := range buildStatement.SymlinkPaths {
- cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
- }
+ // The actual Bazel action.
+ if len(buildStatement.Command) > 16*1024 {
+ commandFile := PathForBazelOut(ctx, buildStatement.OutputPaths[0]+".sh")
+ WriteFileRule(ctx, commandFile, buildStatement.Command)
- // This is required to silence warnings pertaining to unexpected timestamps. Particularly,
- // some Bazel builtins (such as files in the bazel_tools directory) have far-future
- // timestamps. Without restat, Ninja would emit warnings that the input files of a
- // build statement have later timestamps than the outputs.
- rule.Restat()
+ cmd.Text("bash").Text(buildStatement.OutputPaths[0] + ".sh").Implicit(commandFile)
+ } else {
+ cmd.Text(buildStatement.Command)
+ }
- rule.Build(fmt.Sprintf("bazel %d", index), buildStatement.Mnemonic)
+ for _, outputPath := range buildStatement.OutputPaths {
+ cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
+ }
+ 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 {
+ // The paths in depfile are relative to `executionRoot`.
+ // Hence, they need to be corrected by replacing "bazel-out"
+ // with the full `bazelOutDir`.
+ // Otherwise, implicit outputs and implicit inputs under "bazel-out/"
+ // would be deemed missing.
+ // (Note: The regexp uses a capture group because the version of sed
+ // does not support a look-behind pattern.)
+ replacement := fmt.Sprintf(`&& sed -i'' -E 's@(^|\s|")bazel-out/@\1%s/@g' '%s'`,
+ bazelOutDir, *depfile)
+ cmd.Text(replacement)
+ cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
+ }
+
+ for _, symlinkPath := range buildStatement.SymlinkPaths {
+ cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
}
}
@@ -864,21 +1435,64 @@
func getConfigString(key cqueryKey) string {
arch := key.configKey.arch
if len(arch) == 0 || arch == "common" {
- // Use host platform, which is currently hardcoded to be x86_64.
- arch = "x86_64"
+ if key.configKey.osType.Class == Device {
+ // For the generic Android, the expected result is "target|android", which
+ // corresponds to the product_variable_config named "android_target" in
+ // build/bazel/platforms/BUILD.bazel.
+ arch = "target"
+ } else {
+ // Use host platform, which is currently hardcoded to be x86_64.
+ arch = "x86_64"
+ }
}
- os := key.configKey.osType.Name
- if len(os) == 0 || os == "common_os" || os == "linux_glibc" {
+ osName := key.configKey.osType.Name
+ if len(osName) == 0 || osName == "common_os" || osName == "linux_glibc" || osName == "linux_musl" {
// Use host OS, which is currently hardcoded to be linux.
- os = "linux"
+ osName = "linux"
}
- return arch + "|" + os
+ keyString := arch + "|" + osName
+ if key.configKey.apexKey.WithinApex {
+ keyString += "|" + withinApexToString(key.configKey.apexKey.WithinApex)
+ }
+
+ if len(key.configKey.apexKey.ApexSdkVersion) > 0 {
+ keyString += "|" + key.configKey.apexKey.ApexSdkVersion
+ }
+
+ return keyString
}
-func GetConfigKey(ctx ModuleContext) configKey {
+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 GetConfigKeyApexVariant(ctx BaseModuleContext, apexKey *ApexConfigKey) configKey {
+ configKey := GetConfigKey(ctx)
+
+ if apexKey != nil {
+ configKey.apexKey = ApexConfigKey{
+ WithinApex: apexKey.WithinApex,
+ ApexSdkVersion: apexKey.ApexSdkVersion,
+ }
+ }
+
+ return configKey
+}
+
+func bazelDepsetName(contentHash string) string {
+ return fmt.Sprintf("bazel_depset_%s", contentHash)
+}
+
+func EnvironmentVarsFile(config Config) string {
+ return fmt.Sprintf(bazel.GeneratedBazelFileWarning+`
+_env = %s
+
+env = _env
+`,
+ starlark_fmt.PrintStringList(allowedBazelEnvironmentVars, 0),
+ )
+}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index e5cff90..c67d7fb 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -1,37 +1,67 @@
package android
import (
+ "encoding/json"
"os"
"path/filepath"
"reflect"
+ "strings"
"testing"
+
+ "android/soong/bazel/cquery"
+ analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
+
+ "github.com/google/blueprint/metrics"
+ "google.golang.org/protobuf/proto"
)
+var testConfig = TestConfig("out", nil, "", nil)
+
+type testInvokeBazelContext struct{}
+
+func (t *testInvokeBazelContext) GetEventHandler() *metrics.EventHandler {
+ return &metrics.EventHandler{}
+}
+
func TestRequestResultsAfterInvokeBazel(t *testing.T) {
- label := "//foo:bar"
- 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_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)
+ label_foo := "@//foo:foo"
+ label_bar := "@//foo:bar"
+ apexKey := ApexConfigKey{
+ WithinApex: true,
+ ApexSdkVersion: "29",
}
- err := bazelContext.InvokeBazel()
+ cfg_foo := configKey{"arm64_armv8-a", Android, apexKey}
+ cfg_bar := configKey{arch: "arm64_armv8-a", osType: Android}
+ cmd_results := []string{
+ `@//foo:foo|arm64_armv8-a|android|within_apex|29>>out/foo/foo.txt`,
+ `@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
+ }
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
+ bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: strings.Join(cmd_results, "\n"),
+ })
+
+ bazelContext.QueueBazelRequest(label_foo, cquery.GetOutputFiles, cfg_foo)
+ bazelContext.QueueBazelRequest(label_bar, cquery.GetOutputFiles, cfg_bar)
+ err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
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")
- } else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) {
+ verifyCqueryResult(t, bazelContext, label_foo, cfg_foo, "out/foo/foo.txt")
+ verifyCqueryResult(t, bazelContext, label_bar, cfg_bar, "out/foo/bar.txt")
+}
+
+func verifyCqueryResult(t *testing.T, ctx *mixedBuildBazelContext, label string, cfg configKey, result string) {
+ g, err := ctx.GetOutputFiles(label, cfg)
+ if err != nil {
+ t.Errorf("Expected cquery results after running InvokeBazel(), but got err %v", err)
+ } else if w := []string{result}; !reflect.DeepEqual(w, g) {
t.Errorf("Expected output %s, got %s", w, g)
}
}
func TestInvokeBazelWritesBazelFiles(t *testing.T) {
bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
- err := bazelContext.InvokeBazel()
+ err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
@@ -55,50 +85,211 @@
}
func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
- bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
- bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: `
+ type testCase struct {
+ input string
+ command string
+ }
+
+ var testCases = []testCase{
+ {`
{
- "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]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "one"
- }, {
- "id": 2,
- "label": "two"
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 }],
+ "actions": [{
+ "target_Id": 1,
+ "action_Key": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [1],
+ "output_Ids": [1],
+ "primary_output_id": 1
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1, 2] }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "two" }]
}`,
- })
- err := bazelContext.InvokeBazel()
+ "cd 'test/exec_root' && rm -rf 'one' && touch foo",
+ }, {`
+{
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 10 },
+ { "id": 2, "path_fragment_id": 20 }],
+ "actions": [{
+ "target_Id": 100,
+ "action_Key": "x",
+ "mnemonic": "x",
+ "arguments": ["bogus", "command"],
+ "output_Ids": [1, 2],
+ "primary_output_id": 1
+ }],
+ "path_fragments": [
+ { "id": 10, "label": "one", "parent_id": 30 },
+ { "id": 20, "label": "one.d", "parent_id": 30 },
+ { "id": 30, "label": "parent" }]
+}`,
+ `cd 'test/exec_root' && rm -rf 'parent/one' && bogus command && sed -i'' -E 's@(^|\s|")bazel-out/@\1test/bazel_out/@g' 'parent/one.d'`,
+ },
+ }
+
+ for i, testCase := range testCases {
+ data, err := JsonToActionGraphContainer(testCase.input)
+ if err != nil {
+ t.Error(err)
+ }
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
+ bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: string(data)})
+
+ err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
+ if err != nil {
+ t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err)
+ }
+
+ got := bazelContext.BuildStatementsToRegister()
+ if want := 1; len(got) != want {
+ t.Fatalf("expected %d registered build statements, but got %#v", want, got)
+ }
+
+ cmd := RuleBuilderCommand{}
+ ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
+ createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx)
+ if actual, expected := cmd.buf.String(), testCase.command; expected != actual {
+ t.Errorf("expected: [%s], actual: [%s]", expected, actual)
+ }
+ }
+}
+
+func TestCoverageFlagsAfterInvokeBazel(t *testing.T) {
+ testConfig.productVariables.ClangCoverage = boolPtr(true)
+
+ testConfig.productVariables.NativeCoveragePaths = []string{"foo1", "foo2"}
+ testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1", "bar2"}
+ verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,+foo2,-bar1,-bar2`)
+
+ testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
+ testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
+ verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,-bar1`)
+
+ testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
+ testConfig.productVariables.NativeCoverageExcludePaths = nil
+ verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1`)
+
+ testConfig.productVariables.NativeCoveragePaths = nil
+ testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
+ verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=-bar1`)
+
+ testConfig.productVariables.NativeCoveragePaths = []string{"*"}
+ testConfig.productVariables.NativeCoverageExcludePaths = nil
+ verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+.*`)
+
+ testConfig.productVariables.ClangCoverage = boolPtr(false)
+ actual := verifyExtraFlags(t, testConfig, ``)
+ if strings.Contains(actual, "--collect_code_coverage") ||
+ strings.Contains(actual, "--instrumentation_filter=") {
+ t.Errorf("Expected code coverage disabled, but got %#v", actual)
+ }
+}
+
+func TestBazelRequestsSorted(t *testing.T) {
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
+
+ cfgKeyArm64Android := configKey{arch: "arm64_armv8-a", osType: Android}
+ cfgKeyArm64Linux := configKey{arch: "arm64_armv8-a", osType: Linux}
+ cfgKeyOtherAndroid := configKey{arch: "otherarch", osType: Android}
+
+ bazelContext.QueueBazelRequest("zzz", cquery.GetOutputFiles, cfgKeyArm64Android)
+ bazelContext.QueueBazelRequest("ccc", cquery.GetApexInfo, cfgKeyArm64Android)
+ bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
+ bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
+ bazelContext.QueueBazelRequest("xxx", cquery.GetOutputFiles, cfgKeyArm64Linux)
+ bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyArm64Android)
+ bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyOtherAndroid)
+ bazelContext.QueueBazelRequest("bbb", cquery.GetOutputFiles, cfgKeyOtherAndroid)
+
+ if len(bazelContext.requests) != 7 {
+ t.Error("Expected 7 request elements, but got", len(bazelContext.requests))
+ }
+
+ lastString := ""
+ for _, val := range bazelContext.requests {
+ thisString := val.String()
+ if thisString <= lastString {
+ t.Errorf("Requests are not ordered correctly. '%s' came before '%s'", lastString, thisString)
+ }
+ lastString = thisString
+ }
+}
+
+func TestIsModuleNameAllowed(t *testing.T) {
+ libDisabled := "lib_disabled"
+ libEnabled := "lib_enabled"
+ libDclaWithinApex := "lib_dcla_within_apex"
+ libDclaNonApex := "lib_dcla_non_apex"
+ libNotConverted := "lib_not_converted"
+
+ disabledModules := map[string]bool{
+ libDisabled: true,
+ }
+ enabledModules := map[string]bool{
+ libEnabled: true,
+ }
+ dclaEnabledModules := map[string]bool{
+ libDclaWithinApex: true,
+ libDclaNonApex: true,
+ }
+
+ bazelContext := &mixedBuildBazelContext{
+ modulesDefaultToBazel: false,
+ bazelEnabledModules: enabledModules,
+ bazelDisabledModules: disabledModules,
+ bazelDclaEnabledModules: dclaEnabledModules,
+ }
+
+ if bazelContext.IsModuleNameAllowed(libDisabled, true) {
+ t.Fatalf("%s shouldn't be allowed for mixed build", libDisabled)
+ }
+
+ if !bazelContext.IsModuleNameAllowed(libEnabled, true) {
+ t.Fatalf("%s should be allowed for mixed build", libEnabled)
+ }
+
+ if !bazelContext.IsModuleNameAllowed(libDclaWithinApex, true) {
+ t.Fatalf("%s should be allowed for mixed build", libDclaWithinApex)
+ }
+
+ if bazelContext.IsModuleNameAllowed(libDclaNonApex, false) {
+ t.Fatalf("%s shouldn't be allowed for mixed build", libDclaNonApex)
+ }
+
+ if bazelContext.IsModuleNameAllowed(libNotConverted, true) {
+ t.Fatalf("%s shouldn't be allowed for mixed build", libNotConverted)
+ }
+}
+
+func verifyExtraFlags(t *testing.T, config Config, expected string) string {
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
+
+ err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
- got := bazelContext.BuildStatementsToRegister()
- if want := 1; len(got) != want {
- t.Errorf("Expected %d registered build statements, got %#v", want, got)
+ flags := bazelContext.bazelRunner.(*mockBazelRunner).extraFlags
+ if expected := 3; len(flags) != expected {
+ t.Errorf("Expected %d extra flags got %#v", expected, flags)
}
+
+ actual := flags[1]
+ if !strings.Contains(actual, expected) {
+ t.Errorf("Expected %#v got %#v", expected, actual)
+ }
+
+ return actual
}
-func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
+func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*mixedBuildBazelContext, string) {
t.Helper()
p := bazelPaths{
soongOutDir: t.TempDir(),
@@ -107,12 +298,22 @@
}
aqueryCommand := bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}
if _, exists := bazelCommandResults[aqueryCommand]; !exists {
- bazelCommandResults[aqueryCommand] = "{}\n"
+ bazelCommandResults[aqueryCommand] = ""
}
runner := &mockBazelRunner{bazelCommandResults: bazelCommandResults}
- return &bazelContext{
+ return &mixedBuildBazelContext{
bazelRunner: runner,
paths: &p,
- requests: map[cqueryKey]bool{},
}, p.soongOutDir
}
+
+// Transform the json format to ActionGraphContainer
+func JsonToActionGraphContainer(inputString string) ([]byte, error) {
+ var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
+ err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
+ if err != nil {
+ return []byte(""), err
+ }
+ data, _ := proto.Marshal(&aqueryProtoResult)
+ return data, err
+}
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index bacb4f6..bad7baf 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -32,14 +32,14 @@
// There is often a similar method for Bazel as there is for Soong path handling and should be used
// in similar circumstances
//
-// Bazel Soong
-//
-// BazelLabelForModuleSrc PathForModuleSrc
-// BazelLabelForModuleSrcExcludes PathForModuleSrcExcludes
-// BazelLabelForModuleDeps n/a
-// tbd PathForSource
-// tbd ExistentPathsForSources
-// PathForBazelOut PathForModuleOut
+// Bazel Soong
+// ==============================================================
+// BazelLabelForModuleSrc PathForModuleSrc
+// BazelLabelForModuleSrcExcludes PathForModuleSrcExcludes
+// BazelLabelForModuleDeps n/a
+// tbd PathForSource
+// tbd ExistentPathsForSources
+// PathForBazelOut PathForModuleOut
//
// Use cases:
// * Module contains a property (often tagged `android:"path"`) that expects paths *relative to the
@@ -68,7 +68,7 @@
// cannot be resolved,the function will panic. This is often due to the dependency not being added
// via an AddDependency* method.
-// A minimal context interface to check if a module should be converted by bp2build,
+// BazelConversionContext is a minimal context interface to check if a module should be converted by bp2build,
// with functions containing information to match against allowlists and denylists.
// If a module is deemed to be convertible by bp2build, then it should rely on a
// BazelConversionPathContext for more functions for dep/path features.
@@ -202,20 +202,26 @@
return labels
}
-// Returns true if a prefix + components[:i] + /Android.bp exists
-// TODO(b/185358476) Could check for BUILD file instead of checking for Android.bp file, or ensure BUILD is always generated?
-func directoryHasBlueprint(fs pathtools.FileSystem, prefix string, components []string, componentIndex int) bool {
- blueprintPath := prefix
- if blueprintPath != "" {
- blueprintPath = blueprintPath + "/"
- }
- blueprintPath = blueprintPath + strings.Join(components[:componentIndex+1], "/")
- blueprintPath = blueprintPath + "/Android.bp"
- if exists, _, _ := fs.Exists(blueprintPath); exists {
+// Returns true if a prefix + components[:i] is a package boundary.
+//
+// A package boundary is determined by a BUILD file in the directory. This can happen in 2 cases:
+//
+// 1. An Android.bp exists, which bp2build will always convert to a sibling BUILD file.
+// 2. An Android.bp doesn't exist, but a checked-in BUILD/BUILD.bazel file exists, and that file
+// is allowlisted by the bp2build configuration to be merged into the symlink forest workspace.
+func isPackageBoundary(config Config, prefix string, components []string, componentIndex int) bool {
+ prefix = filepath.Join(prefix, filepath.Join(components[:componentIndex+1]...))
+ if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "Android.bp")); exists {
return true
- } else {
- return false
+ } else if config.Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(prefix) {
+ if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD")); exists {
+ return true
+ } else if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD.bazel")); exists {
+ return true
+ }
}
+
+ return false
}
// Transform a path (if necessary) to acknowledge package boundaries
@@ -242,17 +248,37 @@
newPath.Label = path.Label
return newPath
}
-
- newLabel := ""
+ if strings.HasPrefix(path.Label, "./") {
+ // Drop "./" for consistent handling of paths.
+ // Specifically, to not let "." be considered a package boundary.
+ // Say `inputPath` is `x/Android.bp` and that file has some module
+ // with `srcs=["y/a.c", "z/b.c"]`.
+ // And say the directory tree is:
+ // x
+ // ├── Android.bp
+ // ├── y
+ // │ ├── a.c
+ // │ └── Android.bp
+ // └── z
+ // └── b.c
+ // Then bazel equivalent labels in srcs should be:
+ // //x/y:a.c, x/z/b.c
+ // The above should still be the case if `x/Android.bp` had
+ // srcs=["./y/a.c", "./z/b.c"]
+ // However, if we didn't strip "./", we'd get
+ // //x/./y:a.c, //x/.:z/b.c
+ path.Label = strings.TrimPrefix(path.Label, "./")
+ }
pathComponents := strings.Split(path.Label, "/")
- foundBlueprint := false
+ newLabel := ""
+ foundPackageBoundary := false
// Check the deepest subdirectory first and work upwards
for i := len(pathComponents) - 1; i >= 0; i-- {
pathComponent := pathComponents[i]
var sep string
- if !foundBlueprint && directoryHasBlueprint(ctx.Config().fs, ctx.ModuleDir(), pathComponents, i) {
+ if !foundPackageBoundary && isPackageBoundary(ctx.Config(), ctx.ModuleDir(), pathComponents, i) {
sep = ":"
- foundBlueprint = true
+ foundPackageBoundary = true
} else {
sep = "/"
}
@@ -262,7 +288,7 @@
newLabel = pathComponent + sep + newLabel
}
}
- if foundBlueprint {
+ if foundPackageBoundary {
// Ensure paths end up looking like //bionic/... instead of //./bionic/...
moduleDir := ctx.ModuleDir()
if strings.HasPrefix(moduleDir, ".") {
@@ -429,6 +455,9 @@
func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string {
moduleName := ctx.OtherModuleName(module)
moduleDir := ctx.OtherModuleDir(module)
+ if moduleDir == Bp2BuildTopLevel {
+ moduleDir = ""
+ }
return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
}
@@ -454,25 +483,53 @@
return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
-// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to
-// bazel-owned outputs.
-func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath {
- execRootPathComponents := append([]string{"execroot", "__main__"}, paths...)
- execRootPath := filepath.Join(execRootPathComponents...)
- validatedExecRootPath, err := validatePath(execRootPath)
+// PathForBazelOutRelative returns a BazelOutPath representing the path under an output directory dedicated to
+// bazel-owned outputs. Calling .Rel() on the result will give the input path as relative to the given
+// relativeRoot.
+func PathForBazelOutRelative(ctx PathContext, relativeRoot string, path string) BazelOutPath {
+ validatedPath, err := validatePath(filepath.Join("execroot", "__main__", path))
if err != nil {
reportPathError(ctx, err)
}
+ var relativeRootPath string
+ if pathComponents := strings.SplitN(path, "/", 4); len(pathComponents) >= 3 &&
+ pathComponents[0] == "bazel-out" && pathComponents[2] == "bin" {
+ // If the path starts with something like: bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/
+ // make it relative to that folder. bazel-out/volatile-status.txt is an example
+ // of something that starts with bazel-out but is not relative to the bin folder
+ relativeRootPath = filepath.Join("execroot", "__main__", pathComponents[0], pathComponents[1], pathComponents[2], relativeRoot)
+ } else {
+ relativeRootPath = filepath.Join("execroot", "__main__", relativeRoot)
+ }
- outputPath := OutputPath{basePath{"", ""},
+ var relPath string
+ if relPath, err = filepath.Rel(relativeRootPath, validatedPath); err != nil || strings.HasPrefix(relPath, "../") {
+ // We failed to make this path relative to execroot/__main__, fall back to a non-relative path
+ // One case where this happens is when path is ../bazel_tools/something
+ relativeRootPath = ""
+ relPath = validatedPath
+ }
+
+ outputPath := OutputPath{
+ basePath{"", ""},
ctx.Config().soongOutDir,
- ctx.Config().BazelContext.OutputBase()}
+ ctx.Config().BazelContext.OutputBase(),
+ }
return BazelOutPath{
- OutputPath: outputPath.withRel(validatedExecRootPath),
+ // .withRel() appends its argument onto the current path, and only the most
+ // recently appended part is returned by outputPath.rel().
+ // So outputPath.rel() will return relPath.
+ OutputPath: outputPath.withRel(relativeRootPath).withRel(relPath),
}
}
+// PathForBazelOut returns a BazelOutPath representing the path under an output directory dedicated to
+// bazel-owned outputs.
+func PathForBazelOut(ctx PathContext, path string) BazelOutPath {
+ return PathForBazelOutRelative(ctx, "", path)
+}
+
// PathsForBazelOut returns a list of paths representing the paths under an output directory
// dedicated to Bazel-owned outputs.
func PathsForBazelOut(ctx PathContext, paths []string) Paths {
@@ -482,3 +539,55 @@
}
return outs
}
+
+// BazelStringOrLabelFromProp splits a Soong module property that can be
+// either a string literal, path (with android:path tag) or a module reference
+// into separate bazel string or label attributes. Bazel treats string and label
+// attributes as distinct types, so this function categorizes a string property
+// into either one of them.
+//
+// e.g. apex.private_key = "foo.pem" can either refer to:
+//
+// 1. "foo.pem" in the current directory -> file target
+// 2. "foo.pem" module -> rule target
+// 3. "foo.pem" file in a different directory, prefixed by a product variable handled
+// in a bazel macro. -> string literal
+//
+// For the first two cases, they are defined using the label attribute. For the third case,
+// it's defined with the string attribute.
+func BazelStringOrLabelFromProp(
+ ctx TopDownMutatorContext,
+ propToDistinguish *string) (bazel.LabelAttribute, bazel.StringAttribute) {
+
+ var labelAttr bazel.LabelAttribute
+ var strAttr bazel.StringAttribute
+
+ if propToDistinguish == nil {
+ // nil pointer
+ return labelAttr, strAttr
+ }
+
+ prop := String(propToDistinguish)
+ if SrcIsModule(prop) != "" {
+ // If it's a module (SrcIsModule will return the module name), set the
+ // resolved label to the label attribute.
+ labelAttr.SetValue(BazelLabelForModuleDepSingle(ctx, prop))
+ } else {
+ // Not a module name. This could be a string literal or a file target in
+ // the current dir. Check if the path exists:
+ path := ExistentPathForSource(ctx, ctx.ModuleDir(), prop)
+
+ if path.Valid() && parentDir(path.String()) == ctx.ModuleDir() {
+ // If it exists and the path is relative to the current dir, resolve the bazel label
+ // for the _file target_ and set it to the label attribute.
+ //
+ // Resolution is necessary because this could be a file in a subpackage.
+ labelAttr.SetValue(BazelLabelForModuleSrcSingle(ctx, prop))
+ } else {
+ // Otherwise, treat it as a string literal and assign to the string attribute.
+ strAttr.Value = propToDistinguish
+ }
+ }
+
+ return labelAttr, strAttr
+}
diff --git a/android/bazel_paths_test.go b/android/bazel_paths_test.go
new file mode 100644
index 0000000..450bf76
--- /dev/null
+++ b/android/bazel_paths_test.go
@@ -0,0 +1,183 @@
+// 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 (
+ "path/filepath"
+ "testing"
+
+ "android/soong/bazel"
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/pathtools"
+)
+
+type TestBazelPathContext struct{}
+
+func (*TestBazelPathContext) Config() Config {
+ cfg := NullConfig("out", "out/soong")
+ cfg.BazelContext = MockBazelContext{
+ OutputBaseDir: "out/bazel",
+ }
+ return cfg
+}
+
+func (*TestBazelPathContext) AddNinjaFileDeps(...string) {
+ panic("Unimplemented")
+}
+
+func TestPathForBazelOut(t *testing.T) {
+ ctx := &TestBazelPathContext{}
+ out := PathForBazelOut(ctx, "foo/bar/baz/boq.txt")
+ expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/__main__/foo/bar/baz/boq.txt")
+ if out.String() != expectedPath {
+ t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+ }
+
+ expectedRelPath := "foo/bar/baz/boq.txt"
+ if out.Rel() != expectedRelPath {
+ t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+ }
+}
+
+func TestPathForBazelOutRelative(t *testing.T) {
+ ctx := &TestBazelPathContext{}
+ out := PathForBazelOutRelative(ctx, "foo/bar", "foo/bar/baz/boq.txt")
+
+ expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/__main__/foo/bar/baz/boq.txt")
+ if out.String() != expectedPath {
+ t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+ }
+
+ expectedRelPath := "baz/boq.txt"
+ if out.Rel() != expectedRelPath {
+ t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+ }
+}
+
+func TestPathForBazelOutRelativeUnderBinFolder(t *testing.T) {
+ ctx := &TestBazelPathContext{}
+ out := PathForBazelOutRelative(ctx, "foo/bar", "bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/foo/bar/baz/boq.txt")
+
+ expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/__main__/bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/foo/bar/baz/boq.txt")
+ if out.String() != expectedPath {
+ t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+ }
+
+ expectedRelPath := "baz/boq.txt"
+ if out.Rel() != expectedRelPath {
+ t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+ }
+}
+
+func TestPathForBazelOutOutsideOfExecroot(t *testing.T) {
+ ctx := &TestBazelPathContext{}
+ out := PathForBazelOut(ctx, "../bazel_tools/linux_x86_64-fastbuild/bin/tools/android/java_base_extras.jar")
+
+ expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/bazel_tools/linux_x86_64-fastbuild/bin/tools/android/java_base_extras.jar")
+ if out.String() != expectedPath {
+ t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+ }
+
+ expectedRelPath := "execroot/bazel_tools/linux_x86_64-fastbuild/bin/tools/android/java_base_extras.jar"
+ if out.Rel() != expectedRelPath {
+ t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+ }
+}
+
+func TestPathForBazelOutRelativeWithParentDirectoryRoot(t *testing.T) {
+ ctx := &TestBazelPathContext{}
+ out := PathForBazelOutRelative(ctx, "../bazel_tools", "../bazel_tools/foo/bar/baz.sh")
+
+ expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/bazel_tools/foo/bar/baz.sh")
+ if out.String() != expectedPath {
+ t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+ }
+
+ expectedRelPath := "foo/bar/baz.sh"
+ if out.Rel() != expectedRelPath {
+ t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+ }
+}
+
+type TestBazelConversionPathContext struct {
+ TestBazelConversionContext
+ moduleDir string
+ cfg Config
+}
+
+func (ctx *TestBazelConversionPathContext) AddNinjaFileDeps(...string) {
+ panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) GlobWithDeps(string, []string) ([]string, error) {
+ panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) PropertyErrorf(string, string, ...interface{}) {
+ panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) GetDirectDep(string) (blueprint.Module, blueprint.DependencyTag) {
+ panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) ModuleFromName(string) (blueprint.Module, bool) {
+ panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) AddUnconvertedBp2buildDep(string) {
+ panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) AddMissingBp2buildDep(string) {
+ panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) Module() Module {
+ panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) Config() Config {
+ return ctx.cfg
+}
+
+func (ctx *TestBazelConversionPathContext) ModuleDir() string {
+ return ctx.moduleDir
+}
+
+func TestTransformSubpackagePath(t *testing.T) {
+ cfg := NullConfig("out", "out/soong")
+ cfg.fs = pathtools.MockFs(map[string][]byte{
+ "x/Android.bp": nil,
+ "x/y/Android.bp": nil,
+ })
+
+ var ctx BazelConversionPathContext = &TestBazelConversionPathContext{
+ moduleDir: "x",
+ cfg: cfg,
+ }
+ pairs := map[string]string{
+ "y/a.c": "//x/y:a.c",
+ "./y/a.c": "//x/y:a.c",
+ "z/b.c": "z/b.c",
+ "./z/b.c": "z/b.c",
+ }
+ for in, out := range pairs {
+ actual := transformSubpackagePath(ctx, bazel.Label{Label: in}).Label
+ if actual != out {
+ t.Errorf("expected:\n%v\nactual:\n%v", out, actual)
+ }
+ }
+}
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 87bd18e..87b2c8f 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -14,11 +14,12 @@
package android
import (
- "android/soong/android/allowlists"
- "android/soong/bazel"
"fmt"
"testing"
+ "android/soong/android/allowlists"
+ "android/soong/bazel"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -70,6 +71,20 @@
},
packageDir: "a",
},
+ {
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultFalseRecursively,
+ "a/b": allowlists.Bp2BuildDefaultTrue,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultFalseRecursively,
+ "a/b": allowlists.Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b/c",
+ },
}
for _, test := range testCases {
@@ -132,6 +147,20 @@
},
packageDir: "a",
},
+ {
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultFalseRecursively,
+ "a/b": allowlists.Bp2BuildDefaultTrue,
+ },
+ packageDir: "a/b/c",
+ },
+ {
+ prefixes: allowlists.Bp2BuildConfig{
+ "a": allowlists.Bp2BuildDefaultTrueRecursively,
+ "a/b": allowlists.Bp2BuildDefaultFalseRecursively,
+ },
+ packageDir: "a/b/c",
+ },
}
for _, test := range testCases {
@@ -157,7 +186,7 @@
type TestBazelConversionContext struct {
omc bazel.OtherModuleTestContext
- allowlist bp2BuildConversionAllowlist
+ allowlist Bp2BuildConversionAllowlist
errors []string
}
@@ -182,7 +211,7 @@
func (bcc *TestBazelConversionContext) Config() Config {
return Config{
&config{
- bp2buildPackageConfig: bcc.allowlist,
+ Bp2buildPackageConfig: bcc.allowlist,
},
}
}
@@ -201,7 +230,7 @@
shouldConvert bool
expectedErrors []string
module TestBazelModule
- allowlist bp2BuildConversionAllowlist
+ allowlist Bp2BuildConversionAllowlist
}{
{
description: "allowlist enables module",
@@ -214,7 +243,7 @@
},
BazelModuleBase: bazelableBazelModuleBase,
},
- allowlist: bp2BuildConversionAllowlist{
+ allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
@@ -232,7 +261,7 @@
},
BazelModuleBase: bazelableBazelModuleBase,
},
- allowlist: bp2BuildConversionAllowlist{
+ allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
@@ -253,7 +282,7 @@
},
BazelModuleBase: bazelableBazelModuleBase,
},
- allowlist: bp2BuildConversionAllowlist{
+ allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
@@ -263,30 +292,9 @@
},
},
{
- 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'"},
+ expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir' Module: 'foo'"},
module: TestBazelModule{
TestModuleInfo: bazel.TestModuleInfo{
ModuleName: "foo",
@@ -295,7 +303,7 @@
},
BazelModuleBase: bazelableBazelModuleBase,
},
- allowlist: bp2BuildConversionAllowlist{
+ allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
@@ -307,7 +315,7 @@
{
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'"},
+ expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir' Module: 'foo'"},
module: TestBazelModule{
TestModuleInfo: bazel.TestModuleInfo{
ModuleName: "foo",
@@ -316,7 +324,7 @@
},
BazelModuleBase: bazelableBazelModuleBase,
},
- allowlist: bp2BuildConversionAllowlist{
+ allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
@@ -343,7 +351,7 @@
},
},
},
- allowlist: bp2BuildConversionAllowlist{
+ allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
@@ -386,3 +394,45 @@
})
}
}
+
+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])
+ }
+ }
+}
+
+func TestShouldKeepExistingBuildFileForDir(t *testing.T) {
+ allowlist := NewBp2BuildAllowlist()
+ // entry "a/b2/c2" is moot because of its parent "a/b2"
+ allowlist.SetKeepExistingBuildFile(map[string]bool{"a": false, "a/b1": false, "a/b2": true, "a/b1/c1": true, "a/b2/c2": false})
+ truths := []string{"a", "a/b1", "a/b2", "a/b1/c1", "a/b2/c", "a/b2/c2", "a/b2/c2/d"}
+ falsities := []string{"a1", "a/b", "a/b1/c"}
+ for _, dir := range truths {
+ if !allowlist.ShouldKeepExistingBuildFileForDir(dir) {
+ t.Errorf("%s expected TRUE but was FALSE", dir)
+ }
+ }
+ for _, dir := range falsities {
+ if allowlist.ShouldKeepExistingBuildFileForDir(dir) {
+ t.Errorf("%s expected FALSE but was TRUE", dir)
+ }
+ }
+}
diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go
index 6339a71..46f6488 100644
--- a/android/buildinfo_prop.go
+++ b/android/buildinfo_prop.go
@@ -61,11 +61,10 @@
return
}
- rule := NewRuleBuilder(pctx, ctx)
- cmd := rule.Command().Text("(")
+ lines := make([]string, 0)
writeString := func(str string) {
- cmd.Text(`echo "` + str + `" && `)
+ lines = append(lines, str)
}
writeString("# begin build properties")
@@ -89,6 +88,7 @@
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())
+ writeProp("ro.build.version.known_codenames", config.PlatformVersionKnownCodenames())
if config.Eng() {
writeProp("ro.build.type", "eng")
@@ -109,7 +109,6 @@
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`)
@@ -142,8 +141,7 @@
writeString("# end build properties")
- cmd.Text("true) > ").Output(p.outputFilePath)
- rule.Build("build.prop", "generating build.prop")
+ WriteFileRule(ctx, p.outputFilePath, strings.Join(lines, "\n"))
if !p.installable() {
p.SkipInstall()
diff --git a/android/config.go b/android/config.go
index b30a524..980460a 100644
--- a/android/config.go
+++ b/android/config.go
@@ -18,10 +18,9 @@
// product variables necessary for soong_build's operation.
import (
+ "bytes"
"encoding/json"
- "errors"
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -53,6 +52,15 @@
// FutureApiLevelInt is a placeholder constant for unreleased API levels.
const FutureApiLevelInt = 10000
+// PrivateApiLevel represents the api level of SdkSpecPrivate (sdk_version: "")
+// This api_level exists to differentiate user-provided "" from "current" sdk_version
+// The differentiation is necessary to enable different validation rules for these two possible values.
+var PrivateApiLevel = ApiLevel{
+ value: "current", // The value is current since aidl expects `current` as the default (TestAidlFlagsWithMinSdkVersion)
+ number: FutureApiLevelInt + 1, // This is used to differentiate it from FutureApiLevel
+ isPreview: true,
+}
+
// FutureApiLevel represents unreleased API levels.
var FutureApiLevel = ApiLevel{
value: "current",
@@ -68,6 +76,74 @@
*config
}
+type SoongBuildMode int
+
+type CmdArgs struct {
+ bootstrap.Args
+ RunGoTests bool
+ OutDir string
+ SoongOutDir string
+
+ SymlinkForestMarker string
+ Bp2buildMarker string
+ BazelQueryViewDir string
+ BazelApiBp2buildDir string
+ ModuleGraphFile string
+ ModuleActionsFile string
+ DocFile string
+
+ MultitreeBuild bool
+
+ BazelMode bool
+ BazelModeDev bool
+ BazelModeStaging bool
+ BazelForceEnabledModules string
+
+ UseBazelProxy bool
+
+ BuildFromTextStub bool
+}
+
+// Build modes that soong_build can run as.
+const (
+ // Don't use bazel at all during module analysis.
+ AnalysisNoBazel SoongBuildMode = iota
+
+ // Symlink fores mode: merge two directory trees into a symlink forest
+ SymlinkForest
+
+ // Bp2build mode: Generate BUILD files from blueprint files and exit.
+ Bp2build
+
+ // Generate BUILD files which faithfully represent the dependency graph of
+ // blueprint modules. Individual BUILD targets will not, however, faitfhully
+ // express build semantics.
+ GenerateQueryView
+
+ // Generate BUILD files for API contributions to API surfaces
+ ApiBp2build
+
+ // Create a JSON representation of the module graph and exit.
+ GenerateModuleGraph
+
+ // Generate a documentation file for module type definitions and exit.
+ GenerateDocFile
+
+ // Use bazel during analysis of many allowlisted build modules. The allowlist
+ // is considered a "developer mode" allowlist, as some modules may be
+ // allowlisted on an experimental basis.
+ BazelDevMode
+
+ // Use bazel during analysis of a few allowlisted build modules. The allowlist
+ // is considered "staging, as these are modules being prepared to be released
+ // into prod mode shortly after.
+ BazelStagingMode
+
+ // Use bazel during analysis of build modules from an allowlist carefully
+ // curated by the build team to be proven stable.
+ BazelProdMode
+)
+
// SoongOutDir returns the build output directory for the configuration.
func (c Config) SoongOutDir() string {
return c.soongOutDir
@@ -98,6 +174,13 @@
return c.config.TestProductVariables != nil
}
+// MaxPageSizeSupported returns the max page size supported by the device. This
+// value will define the ELF segment alignment for binaries (executables and
+// shared libraries).
+func (c Config) MaxPageSizeSupported() string {
+ return String(c.config.productVariables.DeviceMaxPageSizeSupported)
+}
+
// A DeviceConfig object represents the configuration for a particular device
// being built. For now there will only be one of these, but in the future there
// may be multiple devices being built.
@@ -162,10 +245,14 @@
fs pathtools.FileSystem
mockBpList string
- runningAsBp2Build bool
- bp2buildPackageConfig bp2BuildConversionAllowlist
+ BuildMode SoongBuildMode
+ Bp2buildPackageConfig Bp2BuildConversionAllowlist
Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
+ // If MultitreeBuild is true then this is one inner tree of a multitree
+ // build directed by the multitree orchestrator.
+ MultitreeBuild bool
+
// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
// in tests when a path doesn't exist.
TestAllowNonExistentPaths bool
@@ -175,6 +262,29 @@
ninjaFileDepsSet sync.Map
OncePer
+
+ // These fields are only used for metrics collection. A module should be added
+ // to these maps only if its implementation supports Bazel handling in mixed
+ // builds. A module being in the "enabled" list indicates that there is a
+ // variant of that module for which bazel-handling actually took place.
+ // A module being in the "disabled" list indicates that there is a variant of
+ // that module for which bazel-handling was denied.
+ mixedBuildsLock sync.Mutex
+ mixedBuildEnabledModules map[string]struct{}
+ mixedBuildDisabledModules map[string]struct{}
+
+ // These are modules to be built with Bazel beyond the allowlisted/build-mode
+ // specified modules. They are passed via the command-line flag
+ // "--bazel-force-enabled-modules"
+ bazelForceEnabledModules map[string]struct{}
+
+ // If true, for any requests to Bazel, communicate with a Bazel proxy using
+ // unix sockets, instead of spawning Bazel as a subprocess.
+ UseBazelProxy bool
+
+ // If buildFromTextStub is true then the Java API stubs are
+ // built from the signature text files, not the source Java files.
+ buildFromTextStub bool
}
type deviceConfig struct {
@@ -250,7 +360,7 @@
return fmt.Errorf("cannot marshal config data: %s", err.Error())
}
- f, err := ioutil.TempFile(filepath.Dir(filename), "config")
+ f, err := os.CreateTemp(filepath.Dir(filename), "config")
if err != nil {
return fmt.Errorf("cannot create empty config file %s: %s", filename, err.Error())
}
@@ -306,6 +416,9 @@
if err != nil {
return fmt.Errorf("cannot marshal config data: %s", err.Error())
}
+ // The backslashes need to be escaped because this text is going to be put
+ // inside a Starlark string literal.
+ configJson = bytes.ReplaceAll(configJson, []byte("\\"), []byte("\\\\"))
bzl := []string{
bazel.GeneratedBazelFileWarning,
@@ -314,15 +427,26 @@
fmt.Sprintf(`_arch_variant_product_var_constraints = %s`, archVariantProductVariablesJson),
"\n", `
product_vars = _product_vars
+
+# TODO(b/269577299) Remove these when everything switches over to loading them from product_variable_constants.bzl
product_var_constraints = _product_var_constraints
arch_variant_product_var_constraints = _arch_variant_product_var_constraints
`,
}
- err = ioutil.WriteFile(filepath.Join(dir, "product_variables.bzl"), []byte(strings.Join(bzl, "\n")), 0644)
+ err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variables.bzl"),
+ []byte(strings.Join(bzl, "\n")), 0644)
if err != nil {
return fmt.Errorf("Could not write .bzl config file %s", err)
}
- err = ioutil.WriteFile(filepath.Join(dir, "BUILD"), []byte(bazel.GeneratedBazelFileWarning), 0644)
+ err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variable_constants.bzl"), []byte(fmt.Sprintf(`
+product_var_constraints = %s
+arch_variant_product_var_constraints = %s
+`, nonArchVariantProductVariablesJson, archVariantProductVariablesJson)), 0644)
+ if err != nil {
+ return fmt.Errorf("Could not write .bzl config file %s", err)
+ }
+ err = pathtools.WriteFileIfChanged(filepath.Join(dir, "BUILD"),
+ []byte(bazel.GeneratedBazelFileWarning), 0644)
if err != nil {
return fmt.Errorf("Could not write BUILD config file %s", err)
}
@@ -342,140 +466,30 @@
}
}
-// TestConfig returns a Config object for testing.
-func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
- envCopy := make(map[string]string)
- for k, v := range env {
- envCopy[k] = v
- }
-
- // Copy the real PATH value to the test environment, it's needed by
- // NonHermeticHostSystemTool() used in x86_darwin_host.go
- envCopy["PATH"] = os.Getenv("PATH")
-
- config := &config{
- productVariables: productVariables{
- 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,
- soongOutDir: filepath.Join(buildDir, "soong"),
- captureBuild: true,
- env: envCopy,
-
- // Set testAllowNonExistentPaths so that test contexts don't need to specify every path
- // passed to PathForSource or PathForModuleSrc.
- TestAllowNonExistentPaths: true,
-
- BazelContext: noopBazelContext{},
- }
- config.deviceConfig = &deviceConfig{
- config: config,
- }
- config.TestProductVariables = &config.productVariables
-
- config.mockFileSystem(bp, fs)
-
- determineBuildOS(config)
-
- return Config{config}
-}
-
-func modifyTestConfigToSupportArchMutator(testConfig Config) {
- config := testConfig.config
-
- config.Targets = map[OsType][]Target{
- Android: []Target{
- {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
- {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
- },
- config.BuildOS: []Target{
- {config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
- {config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
- },
- }
-
- // Make the CommonOS OsType available for all products.
- config.Targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}
-
- if runtime.GOOS == "darwin" {
- config.Targets[config.BuildOS] = config.Targets[config.BuildOS][:1]
- }
-
- config.BuildOSTarget = config.Targets[config.BuildOS][0]
- config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
- config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
- config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
- config.TestProductVariables.DeviceArch = proptools.StringPtr("arm64")
- config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a")
- config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm")
- config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon")
-}
-
-func modifyTestConfigForMusl(config Config) {
- delete(config.Targets, config.BuildOS)
- config.productVariables.HostMusl = boolPtr(true)
- determineBuildOS(config.config)
- config.Targets[config.BuildOS] = []Target{
- {config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
- {config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
- }
-
- config.BuildOSTarget = config.Targets[config.BuildOS][0]
- config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
-}
-
-// TestArchConfig returns a Config object suitable for using for tests that
-// need to run the arch mutator.
-func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
- testConfig := TestConfig(buildDir, env, bp, fs)
- modifyTestConfigToSupportArchMutator(testConfig)
- return testConfig
-}
-
-// ConfigForAdditionalRun is a config object which is "reset" for another
-// bootstrap run. Only per-run data is reset. Data which needs to persist across
-// multiple runs in the same program execution is carried over (such as Bazel
-// context or environment deps).
-func ConfigForAdditionalRun(c Config) (Config, error) {
- newConfig, err := NewConfig(c.moduleListFile, c.runGoTests, c.outDir, c.soongOutDir, c.env)
- if err != nil {
- return Config{}, err
- }
- newConfig.BazelContext = c.BazelContext
- newConfig.envDeps = c.envDeps
- return newConfig, nil
-}
-
// NewConfig creates a new Config object. The srcDir argument specifies the path
// to the root source directory. It also loads the config file, if found.
-func NewConfig(moduleListFile string, runGoTests bool, outDir, soongOutDir string, availableEnv map[string]string) (Config, error) {
+func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) {
// Make a config with default options.
config := &config{
- ProductVariablesFileName: filepath.Join(soongOutDir, productVariablesFileName),
+ ProductVariablesFileName: filepath.Join(cmdArgs.SoongOutDir, productVariablesFileName),
env: availableEnv,
- outDir: outDir,
- soongOutDir: soongOutDir,
- runGoTests: runGoTests,
+ outDir: cmdArgs.OutDir,
+ soongOutDir: cmdArgs.SoongOutDir,
+ runGoTests: cmdArgs.RunGoTests,
multilibConflicts: make(map[ArchType]bool),
- moduleListFile: moduleListFile,
- fs: pathtools.NewOsFs(absSrcDir),
+ moduleListFile: cmdArgs.ModuleListFile,
+ fs: pathtools.NewOsFs(absSrcDir),
+ mixedBuildDisabledModules: make(map[string]struct{}),
+ mixedBuildEnabledModules: make(map[string]struct{}),
+ bazelForceEnabledModules: make(map[string]struct{}),
+
+ MultitreeBuild: cmdArgs.MultitreeBuild,
+ UseBazelProxy: cmdArgs.UseBazelProxy,
+
+ buildFromTextStub: cmdArgs.BuildFromTextStub,
}
config.deviceConfig = &deviceConfig{
@@ -484,7 +498,7 @@
// Soundness check of the build and source directories. This won't catch strange
// configurations with symlinks, but at least checks the obvious case.
- absBuildDir, err := filepath.Abs(soongOutDir)
+ absBuildDir, err := filepath.Abs(cmdArgs.SoongOutDir)
if err != nil {
return Config{}, err
}
@@ -504,7 +518,7 @@
return Config{}, err
}
- KatiEnabledMarkerFile := filepath.Join(soongOutDir, ".soong.kati_enabled")
+ KatiEnabledMarkerFile := filepath.Join(cmdArgs.SoongOutDir, ".soong.kati_enabled")
if _, err := os.Stat(absolutePath(KatiEnabledMarkerFile)); err == nil {
config.katiEnabled = true
}
@@ -557,8 +571,39 @@
config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
}
+ setBuildMode := func(arg string, mode SoongBuildMode) {
+ if arg != "" {
+ if config.BuildMode != AnalysisNoBazel {
+ fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", arg)
+ os.Exit(1)
+ }
+ config.BuildMode = mode
+ }
+ }
+ setBazelMode := func(arg bool, argName string, mode SoongBuildMode) {
+ if arg {
+ if config.BuildMode != AnalysisNoBazel {
+ fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", argName)
+ os.Exit(1)
+ }
+ config.BuildMode = mode
+ }
+ }
+ setBuildMode(cmdArgs.SymlinkForestMarker, SymlinkForest)
+ setBuildMode(cmdArgs.Bp2buildMarker, Bp2build)
+ setBuildMode(cmdArgs.BazelQueryViewDir, GenerateQueryView)
+ setBuildMode(cmdArgs.BazelApiBp2buildDir, ApiBp2build)
+ setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
+ setBuildMode(cmdArgs.DocFile, GenerateDocFile)
+ setBazelMode(cmdArgs.BazelModeDev, "--bazel-mode-dev", BazelDevMode)
+ setBazelMode(cmdArgs.BazelMode, "--bazel-mode", BazelProdMode)
+ setBazelMode(cmdArgs.BazelModeStaging, "--bazel-mode-staging", BazelStagingMode)
+
+ for _, module := range strings.Split(cmdArgs.BazelForceEnabledModules, ",") {
+ config.bazelForceEnabledModules[module] = struct{}{}
+ }
config.BazelContext, err = NewBazelContext(config)
- config.bp2buildPackageConfig = bp2buildAllowlist
+ config.Bp2buildPackageConfig = GetBp2BuildAllowList()
return Config{config}, err
}
@@ -593,6 +638,37 @@
c.mockBpList = blueprint.MockModuleListFile
}
+// TODO(b/265062549): Add a field to our collected (and uploaded) metrics which
+// describes a reason that we fell back to non-mixed builds.
+// Returns true if "Bazel builds" is enabled. In this mode, part of build
+// analysis is handled by Bazel.
+func (c *config) IsMixedBuildsEnabled() bool {
+ globalMixedBuildsSupport := c.Once(OnceKey{"globalMixedBuildsSupport"}, func() interface{} {
+ if c.productVariables.DeviceArch != nil && *c.productVariables.DeviceArch == "riscv64" {
+ return false
+ }
+ if c.IsEnvTrue("GLOBAL_THINLTO") {
+ return false
+ }
+ if len(c.productVariables.SanitizeHost) > 0 {
+ return false
+ }
+ if len(c.productVariables.SanitizeDevice) > 0 {
+ return false
+ }
+ if len(c.productVariables.SanitizeDeviceDiag) > 0 {
+ return false
+ }
+ if len(c.productVariables.SanitizeDeviceArch) > 0 {
+ return false
+ }
+ return true
+ }).(bool)
+
+ bazelModeEnabled := c.BuildMode == BazelProdMode || c.BuildMode == BazelDevMode || c.BuildMode == BazelStagingMode
+ return globalMixedBuildsSupport && bazelModeEnabled
+}
+
func (c *config) SetAllowMissingDependencies() {
c.productVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
}
@@ -735,12 +811,18 @@
// 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.
+// NOTE: Do not base conditional logic on this value. It may break product inheritance.
func (c *config) DeviceProduct() string {
return *c.productVariables.DeviceProduct
}
+// HasDeviceProduct returns if the build has a product. A build will not
+// necessarily have a product when --skip-config is passed to soong, like it is
+// in prebuilts/build-tools/build-prebuilts.sh
+func (c *config) HasDeviceProduct() bool {
+ return c.productVariables.DeviceProduct != nil
+}
+
func (c *config) DeviceResourceOverlays() []string {
return c.productVariables.DeviceResourceOverlays
}
@@ -757,6 +839,10 @@
return uncheckedFinalApiLevel(*c.productVariables.Platform_sdk_version)
}
+func (c *config) RawPlatformSdkVersion() *int {
+ return c.productVariables.Platform_sdk_version
+}
+
func (c *config) PlatformSdkFinal() bool {
return Bool(c.productVariables.Platform_sdk_final)
}
@@ -793,8 +879,12 @@
return String(c.productVariables.Platform_version_last_stable)
}
+func (c *config) PlatformVersionKnownCodenames() string {
+ return String(c.productVariables.Platform_version_known_codenames)
+}
+
func (c *config) MinSupportedSdkVersion() ApiLevel {
- return uncheckedFinalApiLevel(19)
+ return uncheckedFinalApiLevel(21)
}
func (c *config) FinalApiLevels() []ApiLevel {
@@ -836,12 +926,22 @@
// DefaultAppTargetSdk returns the API level that platform apps are targeting.
// This converts a codename to the exact ApiLevel it represents.
func (c *config) DefaultAppTargetSdk(ctx EarlyModuleContext) ApiLevel {
+ // This logic is replicated in starlark, if changing logic here update starlark code too
+ // https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=72;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
if Bool(c.productVariables.Platform_sdk_final) {
return c.PlatformSdkVersion()
}
codename := c.PlatformSdkCodename()
+ hostOnlyBuild := c.productVariables.DeviceArch == nil
if codename == "" {
- return NoneApiLevel
+ // There are some host-only builds (those are invoked by build-prebuilts.sh) which
+ // don't set platform sdk codename. Platform sdk codename makes sense only when we
+ // are building the platform. So we don't enforce the below panic for the host-only
+ // builds.
+ if hostOnlyBuild {
+ return NoneApiLevel
+ }
+ panic("Platform_sdk_codename must be set")
}
if codename == "REL" {
panic("Platform_sdk_codename should not be REL when Platform_sdk_final is true")
@@ -858,6 +958,11 @@
return c.productVariables.Platform_version_active_codenames
}
+// All unreleased codenames.
+func (c *config) PlatformVersionAllPreviewCodenames() []string {
+ return c.productVariables.Platform_version_all_preview_codenames
+}
+
func (c *config) ProductAAPTConfig() []string {
return c.productVariables.AAPTConfig
}
@@ -903,6 +1008,15 @@
return PathForSource(ctx, filepath.Dir(defaultCert))
}
+// Certificate for the NetworkStack sepolicy context
+func (c *config) MainlineSepolicyDevCertificatesDir(ctx ModuleContext) SourcePath {
+ cert := String(c.productVariables.MainlineSepolicyDevCertificates)
+ if cert != "" {
+ return PathForSource(ctx, cert)
+ }
+ return c.DefaultAppCertificateDir(ctx)
+}
+
// AllowMissingDependencies configures Blueprint/Soong to not fail when modules
// are configured to depend on non-existent modules. Note that this does not
// affect missing input dependencies at the Ninja level.
@@ -1105,6 +1219,14 @@
return append([]string(nil), c.productVariables.NamespacesToExport...)
}
+func (c *config) SourceRootDirs() []string {
+ return c.productVariables.SourceRootDirs
+}
+
+func (c *config) IncludeTags() []string {
+ return c.productVariables.IncludeTags
+}
+
func (c *config) HostStaticBinaries() bool {
return Bool(c.productVariables.HostStaticBinaries)
}
@@ -1140,14 +1262,14 @@
return nil, nil
}
ctx.AddNinjaFileDeps(path.String())
- return ioutil.ReadFile(absolutePath(path.String()))
+ return os.ReadFile(absolutePath(path.String()))
}
func (c *deviceConfig) WithDexpreopt() bool {
return c.config.productVariables.WithDexpreopt
}
-func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
+func (c *config) FrameworksBaseDirExists(ctx PathGlobContext) bool {
return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid()
}
@@ -1159,10 +1281,14 @@
return c.multilibConflicts[arch]
}
-func (c *config) PrebuiltHiddenApiDir(ctx PathContext) string {
+func (c *config) PrebuiltHiddenApiDir(_ PathContext) string {
return String(c.productVariables.PrebuiltHiddenApiDir)
}
+func (c *config) BazelModulesForceEnabledByFlag() map[string]struct{} {
+ return c.bazelForceEnabledModules
+}
+
func (c *deviceConfig) Arches() []Arch {
var arches []Arch
for _, target := range c.config.Targets[Android] {
@@ -1303,6 +1429,11 @@
}
}
if coverage && len(c.config.productVariables.NativeCoverageExcludePaths) > 0 {
+ // Workaround coverage boot failure.
+ // http://b/269981180
+ if strings.HasPrefix(path, "external/protobuf") {
+ coverage = false
+ }
if HasAnyPrefix(path, c.config.productVariables.NativeCoverageExcludePaths) {
coverage = false
}
@@ -1310,14 +1441,25 @@
return coverage
}
-func (c *deviceConfig) AfdoAdditionalProfileDirs() []string {
- return c.config.productVariables.AfdoAdditionalProfileDirs
-}
-
func (c *deviceConfig) PgoAdditionalProfileDirs() []string {
return c.config.productVariables.PgoAdditionalProfileDirs
}
+// AfdoProfile returns fully qualified path associated to the given module name
+func (c *deviceConfig) AfdoProfile(name string) (*string, error) {
+ for _, afdoProfile := range c.config.productVariables.AfdoProfiles {
+ split := strings.Split(afdoProfile, ":")
+ if len(split) != 3 {
+ return nil, fmt.Errorf("AFDO_PROFILES has invalid value: %s. "+
+ "The expected format is <module>:<fully-qualified-path-to-fdo_profile>", afdoProfile)
+ }
+ if split[0] == name {
+ return proptools.StringPtr(strings.Join([]string{split[1], split[2]}, ":")), nil
+ }
+ }
+ return nil, nil
+}
+
func (c *deviceConfig) VendorSepolicyDirs() []string {
return c.config.productVariables.BoardVendorSepolicyDirs
}
@@ -1422,6 +1564,13 @@
return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths) && !c.MemtagHeapDisabledForPath(path)
}
+func (c *config) HWASanEnabledForPath(path string) bool {
+ if len(c.productVariables.HWASanIncludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.HWASanIncludePaths)
+}
+
func (c *config) VendorConfig(name string) VendorConfig {
return soongconfig.Config(c.productVariables.VendorVars[name])
}
@@ -1442,8 +1591,12 @@
return Bool(c.productVariables.ForceApexSymlinkOptimization)
}
-func (c *config) CompressedApex() bool {
- return Bool(c.productVariables.CompressedApex)
+func (c *config) ApexCompressionEnabled() bool {
+ return Bool(c.productVariables.CompressedApex) && !c.UnbundledBuildApps()
+}
+
+func (c *config) ApexTrimEnabled() bool {
+ return Bool(c.productVariables.TrimmedApex)
}
func (c *config) EnforceSystemCertificate() bool {
@@ -1498,6 +1651,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)
}
@@ -1669,6 +1826,18 @@
return uncheckedFinalApiLevel(apiLevel)
}
+func (c *deviceConfig) BuildBrokenClangAsFlags() bool {
+ return c.config.productVariables.BuildBrokenClangAsFlags
+}
+
+func (c *deviceConfig) BuildBrokenClangCFlags() bool {
+ return c.config.productVariables.BuildBrokenClangCFlags
+}
+
+func (c *deviceConfig) BuildBrokenClangProperty() bool {
+ return c.config.productVariables.BuildBrokenClangProperty
+}
+
func (c *deviceConfig) BuildBrokenEnforceSyspropOwner() bool {
return c.config.productVariables.BuildBrokenEnforceSyspropOwner
}
@@ -1677,6 +1846,10 @@
return c.config.productVariables.BuildBrokenTrebleSyspropNeverallow
}
+func (c *deviceConfig) BuildBrokenUsesSoongPython2Modules() bool {
+ return c.config.productVariables.BuildBrokenUsesSoongPython2Modules
+}
+
func (c *deviceConfig) BuildDebugfsRestrictionsEnabled() bool {
return c.config.productVariables.BuildDebugfsRestrictionsEnabled
}
@@ -1689,6 +1862,10 @@
return InList(name, c.config.productVariables.BuildBrokenInputDirModules)
}
+func (c *deviceConfig) BuildBrokenDepfile() bool {
+ return Bool(c.config.productVariables.BuildBrokenDepfile)
+}
+
func (c *deviceConfig) RequiresInsecureExecmemForSwiftshader() bool {
return c.config.productVariables.RequiresInsecureExecmemForSwiftshader
}
@@ -1713,316 +1890,10 @@
return c.config.productVariables.GenerateAidlNdkPlatformBackend
}
-// The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
-// Such lists are used in the build system for things like bootclasspath jars or system server jars.
-// The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
-// module name. The pairs come from Make product variables as a list of colon-separated strings.
-//
-// Examples:
-// - "com.android.art:core-oj"
-// - "platform:framework"
-// - "system_ext:foo"
-//
-type ConfiguredJarList struct {
- // A list of apex components, which can be an apex name,
- // or special names like "platform" or "system_ext".
- apexes []string
-
- // A list of jar module name components.
- jars []string
+func (c *config) IgnorePrefer32OnDevice() bool {
+ return c.productVariables.IgnorePrefer32OnDevice
}
-// Len returns the length of the list of jars.
-func (l *ConfiguredJarList) Len() int {
- return len(l.jars)
-}
-
-// Jar returns the idx-th jar component of (apex, jar) pairs.
-func (l *ConfiguredJarList) Jar(idx int) string {
- return l.jars[idx]
-}
-
-// Apex returns the idx-th apex component of (apex, jar) pairs.
-func (l *ConfiguredJarList) Apex(idx int) string {
- return l.apexes[idx]
-}
-
-// ContainsJar returns true if the (apex, jar) pairs contains a pair with the
-// given jar module name.
-func (l *ConfiguredJarList) ContainsJar(jar string) bool {
- return InList(jar, l.jars)
-}
-
-// If the list contains the given (apex, jar) pair.
-func (l *ConfiguredJarList) containsApexJarPair(apex, jar string) bool {
- for i := 0; i < l.Len(); i++ {
- if apex == l.apexes[i] && jar == l.jars[i] {
- return true
- }
- }
- return false
-}
-
-// ApexOfJar returns the apex component of the first pair with the given jar name on the list, or
-// an empty string if not found.
-func (l *ConfiguredJarList) ApexOfJar(jar string) string {
- if idx := IndexList(jar, l.jars); idx != -1 {
- return l.Apex(IndexList(jar, l.jars))
- }
- return ""
-}
-
-// IndexOfJar returns the first pair with the given jar name on the list, or -1
-// if not found.
-func (l *ConfiguredJarList) IndexOfJar(jar string) int {
- return IndexList(jar, l.jars)
-}
-
-func copyAndAppend(list []string, item string) []string {
- // Create the result list to be 1 longer than the input.
- result := make([]string, len(list)+1)
-
- // Copy the whole input list into the result.
- count := copy(result, list)
-
- // Insert the extra item at the end.
- result[count] = item
-
- return result
-}
-
-// Append an (apex, jar) pair to the list.
-func (l *ConfiguredJarList) Append(apex string, jar string) ConfiguredJarList {
- // Create a copy of the backing arrays before appending to avoid sharing backing
- // arrays that are mutated across instances.
- apexes := copyAndAppend(l.apexes, apex)
- jars := copyAndAppend(l.jars, jar)
-
- return ConfiguredJarList{apexes, jars}
-}
-
-// Append a list of (apex, jar) pairs to the list.
-func (l *ConfiguredJarList) AppendList(other *ConfiguredJarList) ConfiguredJarList {
- apexes := make([]string, 0, l.Len()+other.Len())
- jars := make([]string, 0, l.Len()+other.Len())
-
- apexes = append(apexes, l.apexes...)
- jars = append(jars, l.jars...)
-
- apexes = append(apexes, other.apexes...)
- jars = append(jars, other.jars...)
-
- return ConfiguredJarList{apexes, jars}
-}
-
-// RemoveList filters out a list of (apex, jar) pairs from the receiving list of pairs.
-func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList {
- apexes := make([]string, 0, l.Len())
- jars := make([]string, 0, l.Len())
-
- for i, jar := range l.jars {
- apex := l.apexes[i]
- if !list.containsApexJarPair(apex, jar) {
- apexes = append(apexes, apex)
- jars = append(jars, jar)
- }
- }
-
- return ConfiguredJarList{apexes, jars}
-}
-
-// Filter keeps the entries if a jar appears in the given list of jars to keep. Returns a new list
-// and any remaining jars that are not on this list.
-func (l *ConfiguredJarList) Filter(jarsToKeep []string) (ConfiguredJarList, []string) {
- var apexes []string
- var jars []string
-
- for i, jar := range l.jars {
- if InList(jar, jarsToKeep) {
- apexes = append(apexes, l.apexes[i])
- jars = append(jars, jar)
- }
- }
-
- return ConfiguredJarList{apexes, jars}, RemoveListFromList(jarsToKeep, jars)
-}
-
-// CopyOfJars returns a copy of the list of strings containing jar module name
-// components.
-func (l *ConfiguredJarList) CopyOfJars() []string {
- return CopyOf(l.jars)
-}
-
-// CopyOfApexJarPairs returns a copy of the list of strings with colon-separated
-// (apex, jar) pairs.
-func (l *ConfiguredJarList) CopyOfApexJarPairs() []string {
- pairs := make([]string, 0, l.Len())
-
- for i, jar := range l.jars {
- apex := l.apexes[i]
- pairs = append(pairs, apex+":"+jar)
- }
-
- return pairs
-}
-
-// BuildPaths returns a list of build paths based on the given directory prefix.
-func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths {
- paths := make(WritablePaths, l.Len())
- for i, jar := range l.jars {
- paths[i] = dir.Join(ctx, ModuleStem(jar)+".jar")
- }
- return paths
-}
-
-// BuildPathsByModule returns a map from module name to build paths based on the given directory
-// prefix.
-func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
- paths := map[string]WritablePath{}
- for _, jar := range l.jars {
- paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
- }
- return paths
-}
-
-// UnmarshalJSON converts JSON configuration from raw bytes into a
-// ConfiguredJarList structure.
-func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {
- // Try and unmarshal into a []string each item of which contains a pair
- // <apex>:<jar>.
- var list []string
- err := json.Unmarshal(b, &list)
- if err != nil {
- // Did not work so return
- return err
- }
-
- apexes, jars, err := splitListOfPairsIntoPairOfLists(list)
- if err != nil {
- return err
- }
- l.apexes = apexes
- l.jars = jars
- return nil
-}
-
-func (l *ConfiguredJarList) MarshalJSON() ([]byte, error) {
- if len(l.apexes) != len(l.jars) {
- return nil, errors.New(fmt.Sprintf("Inconsistent ConfiguredJarList: apexes: %q, jars: %q", l.apexes, l.jars))
- }
-
- list := make([]string, 0, len(l.apexes))
-
- for i := 0; i < len(l.apexes); i++ {
- list = append(list, l.apexes[i]+":"+l.jars[i])
- }
-
- return json.Marshal(list)
-}
-
-// ModuleStem hardcodes the stem of framework-minus-apex to return "framework".
-//
-// TODO(b/139391334): hard coded until we find a good way to query the stem of a
-// module before any other mutators are run.
-func ModuleStem(module string) string {
- if module == "framework-minus-apex" {
- return "framework"
- }
- return module
-}
-
-// DevicePaths computes the on-device paths for the list of (apex, jar) pairs,
-// based on the operating system.
-func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
- paths := make([]string, l.Len())
- for i, jar := range l.jars {
- apex := l.apexes[i]
- name := ModuleStem(jar) + ".jar"
-
- var subdir string
- if apex == "platform" {
- subdir = "system/framework"
- } else if apex == "system_ext" {
- subdir = "system_ext/framework"
- } else {
- subdir = filepath.Join("apex", apex, "javalib")
- }
-
- if ostype.Class == Host {
- paths[i] = filepath.Join(cfg.Getenv("OUT_DIR"), "host", cfg.PrebuiltOS(), subdir, name)
- } else {
- paths[i] = filepath.Join("/", subdir, name)
- }
- }
- return paths
-}
-
-func (l *ConfiguredJarList) String() string {
- var pairs []string
- for i := 0; i < l.Len(); i++ {
- pairs = append(pairs, l.apexes[i]+":"+l.jars[i])
- }
- return strings.Join(pairs, ",")
-}
-
-func splitListOfPairsIntoPairOfLists(list []string) ([]string, []string, error) {
- // Now we need to populate this list by splitting each item in the slice of
- // pairs and appending them to the appropriate list of apexes or jars.
- apexes := make([]string, len(list))
- jars := make([]string, len(list))
-
- for i, apexjar := range list {
- apex, jar, err := splitConfiguredJarPair(apexjar)
- if err != nil {
- return nil, nil, err
- }
- apexes[i] = apex
- jars[i] = jar
- }
-
- return apexes, jars, nil
-}
-
-// Expected format for apexJarValue = <apex name>:<jar name>
-func splitConfiguredJarPair(str string) (string, string, error) {
- pair := strings.SplitN(str, ":", 2)
- if len(pair) == 2 {
- apex := pair[0]
- jar := pair[1]
- if apex == "" {
- return apex, jar, fmt.Errorf("invalid apex '%s' in <apex>:<jar> pair '%s', expected format: <apex>:<jar>", apex, str)
- }
- return apex, jar, nil
- } else {
- return "error-apex", "error-jar", fmt.Errorf("malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
- }
-}
-
-// CreateTestConfiguredJarList is a function to create ConfiguredJarList for tests.
-func CreateTestConfiguredJarList(list []string) ConfiguredJarList {
- // Create the ConfiguredJarList in as similar way as it is created at runtime by marshalling to
- // a json list of strings and then unmarshalling into a ConfiguredJarList instance.
- b, err := json.Marshal(list)
- if err != nil {
- panic(err)
- }
-
- var jarList ConfiguredJarList
- err = json.Unmarshal(b, &jarList)
- if err != nil {
- panic(err)
- }
-
- return jarList
-}
-
-// EmptyConfiguredJarList returns an empty jar list.
-func EmptyConfiguredJarList() ConfiguredJarList {
- return ConfiguredJarList{}
-}
-
-var earlyBootJarsKey = NewOnceKey("earlyBootJars")
-
func (c *config) BootJars() []string {
return c.Once(earlyBootJarsKey, func() interface{} {
list := c.productVariables.BootJars.CopyOfJars()
@@ -2046,3 +1917,38 @@
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{}{}
+ }
+}
+
+// ApiSurfaces directory returns the source path inside the api_surfaces repo
+// (relative to workspace root).
+func (c *config) ApiSurfacesDir(s ApiSurface, version string) string {
+ return filepath.Join(
+ "build",
+ "bazel",
+ "api_surfaces",
+ s.String(),
+ version)
+}
+
+func (c *config) BuildFromTextStub() bool {
+ return c.buildFromTextStub
+}
+
+func (c *config) SetBuildFromTextStub(b bool) {
+ c.buildFromTextStub = b
+}
+func (c *config) AddForceEnabledModules(forceEnabled []string) {
+ for _, forceEnabledModule := range forceEnabled {
+ c.bazelForceEnabledModules[forceEnabledModule] = struct{}{}
+ }
+}
diff --git a/android/config_bp2build.go b/android/config_bp2build.go
index d6b2bcf..830890d 100644
--- a/android/config_bp2build.go
+++ b/android/config_bp2build.go
@@ -69,6 +69,7 @@
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)...)
+ ret = append(ret, ev.exportedConfigDependingVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
return ret
}
@@ -94,6 +95,15 @@
return ev.pctx.VariableConfigMethod(name, method)
}
+func (ev ExportedVariables) ExportStringStaticVariableWithEnvOverride(name, envVar, defaultVal string) {
+ ev.ExportVariableConfigMethod(name, func(config Config) string {
+ if override := config.Getenv(envVar); override != "" {
+ return override
+ }
+ return defaultVal
+ })
+}
+
// ExportSourcePathVariable declares a static "source path" variable and exports
// it to Bazel's toolchain.
func (ev ExportedVariables) ExportSourcePathVariable(name string, value string) {
@@ -141,6 +151,33 @@
m[k] = v
}
+func (m ExportedConfigDependingVariables) asBazel(config Config,
+ stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant {
+ ret := make([]bazelConstant, 0, len(m))
+ for variable, unevaluatedVar := range m {
+ evalFunc := reflect.ValueOf(unevaluatedVar)
+ validateVariableMethod(variable, evalFunc)
+ evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
+ evaluatedValue := evaluatedResult[0].Interface().(string)
+ expandedVars, err := expandVar(config, evaluatedValue, stringVars, stringListVars, cfgDepVars)
+ if err != nil {
+ panic(fmt.Errorf("error expanding config variable %s: %s", variable, err))
+ }
+ if len(expandedVars) > 1 {
+ ret = append(ret, bazelConstant{
+ variableName: variable,
+ internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
+ })
+ } else {
+ ret = append(ret, bazelConstant{
+ variableName: variable,
+ internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVars[0])),
+ })
+ }
+ }
+ return ret
+}
+
// 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`, `"`, `\`} {
diff --git a/android/configured_jars.go b/android/configured_jars.go
new file mode 100644
index 0000000..53fef05
--- /dev/null
+++ b/android/configured_jars.go
@@ -0,0 +1,314 @@
+// 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 (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "path/filepath"
+ "strings"
+)
+
+// The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
+// Such lists are used in the build system for things like bootclasspath jars or system server jars.
+// The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
+// module name. The pairs come from Make product variables as a list of colon-separated strings.
+//
+// Examples:
+// - "com.android.art:core-oj"
+// - "platform:framework"
+// - "system_ext:foo"
+type ConfiguredJarList struct {
+ // A list of apex components, which can be an apex name,
+ // or special names like "platform" or "system_ext".
+ apexes []string
+
+ // A list of jar module name components.
+ jars []string
+}
+
+// Len returns the length of the list of jars.
+func (l *ConfiguredJarList) Len() int {
+ return len(l.jars)
+}
+
+// Jar returns the idx-th jar component of (apex, jar) pairs.
+func (l *ConfiguredJarList) Jar(idx int) string {
+ return l.jars[idx]
+}
+
+// Apex returns the idx-th apex component of (apex, jar) pairs.
+func (l *ConfiguredJarList) Apex(idx int) string {
+ return l.apexes[idx]
+}
+
+// ContainsJar returns true if the (apex, jar) pairs contains a pair with the
+// given jar module name.
+func (l *ConfiguredJarList) ContainsJar(jar string) bool {
+ return InList(jar, l.jars)
+}
+
+// If the list contains the given (apex, jar) pair.
+func (l *ConfiguredJarList) containsApexJarPair(apex, jar string) bool {
+ for i := 0; i < l.Len(); i++ {
+ if apex == l.apexes[i] && jar == l.jars[i] {
+ return true
+ }
+ }
+ return false
+}
+
+// ApexOfJar returns the apex component of the first pair with the given jar name on the list, or
+// an empty string if not found.
+func (l *ConfiguredJarList) ApexOfJar(jar string) string {
+ if idx := IndexList(jar, l.jars); idx != -1 {
+ return l.Apex(IndexList(jar, l.jars))
+ }
+ return ""
+}
+
+// IndexOfJar returns the first pair with the given jar name on the list, or -1
+// if not found.
+func (l *ConfiguredJarList) IndexOfJar(jar string) int {
+ return IndexList(jar, l.jars)
+}
+
+func copyAndAppend(list []string, item string) []string {
+ // Create the result list to be 1 longer than the input.
+ result := make([]string, len(list)+1)
+
+ // Copy the whole input list into the result.
+ count := copy(result, list)
+
+ // Insert the extra item at the end.
+ result[count] = item
+
+ return result
+}
+
+// Append an (apex, jar) pair to the list.
+func (l *ConfiguredJarList) Append(apex string, jar string) ConfiguredJarList {
+ // Create a copy of the backing arrays before appending to avoid sharing backing
+ // arrays that are mutated across instances.
+ apexes := copyAndAppend(l.apexes, apex)
+ jars := copyAndAppend(l.jars, jar)
+
+ return ConfiguredJarList{apexes, jars}
+}
+
+// Append a list of (apex, jar) pairs to the list.
+func (l *ConfiguredJarList) AppendList(other *ConfiguredJarList) ConfiguredJarList {
+ apexes := make([]string, 0, l.Len()+other.Len())
+ jars := make([]string, 0, l.Len()+other.Len())
+
+ apexes = append(apexes, l.apexes...)
+ jars = append(jars, l.jars...)
+
+ apexes = append(apexes, other.apexes...)
+ jars = append(jars, other.jars...)
+
+ return ConfiguredJarList{apexes, jars}
+}
+
+// RemoveList filters out a list of (apex, jar) pairs from the receiving list of pairs.
+func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList {
+ apexes := make([]string, 0, l.Len())
+ jars := make([]string, 0, l.Len())
+
+ for i, jar := range l.jars {
+ apex := l.apexes[i]
+ if !list.containsApexJarPair(apex, jar) {
+ apexes = append(apexes, apex)
+ jars = append(jars, jar)
+ }
+ }
+
+ return ConfiguredJarList{apexes, jars}
+}
+
+// Filter keeps the entries if a jar appears in the given list of jars to keep. Returns a new list
+// and any remaining jars that are not on this list.
+func (l *ConfiguredJarList) Filter(jarsToKeep []string) (ConfiguredJarList, []string) {
+ var apexes []string
+ var jars []string
+
+ for i, jar := range l.jars {
+ if InList(jar, jarsToKeep) {
+ apexes = append(apexes, l.apexes[i])
+ jars = append(jars, jar)
+ }
+ }
+
+ return ConfiguredJarList{apexes, jars}, RemoveListFromList(jarsToKeep, jars)
+}
+
+// CopyOfJars returns a copy of the list of strings containing jar module name
+// components.
+func (l *ConfiguredJarList) CopyOfJars() []string {
+ return CopyOf(l.jars)
+}
+
+// CopyOfApexJarPairs returns a copy of the list of strings with colon-separated
+// (apex, jar) pairs.
+func (l *ConfiguredJarList) CopyOfApexJarPairs() []string {
+ pairs := make([]string, 0, l.Len())
+
+ for i, jar := range l.jars {
+ apex := l.apexes[i]
+ pairs = append(pairs, apex+":"+jar)
+ }
+
+ return pairs
+}
+
+// BuildPaths returns a list of build paths based on the given directory prefix.
+func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths {
+ paths := make(WritablePaths, l.Len())
+ for i, jar := range l.jars {
+ paths[i] = dir.Join(ctx, ModuleStem(jar)+".jar")
+ }
+ return paths
+}
+
+// BuildPathsByModule returns a map from module name to build paths based on the given directory
+// prefix.
+func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
+ paths := map[string]WritablePath{}
+ for _, jar := range l.jars {
+ paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
+ }
+ return paths
+}
+
+// UnmarshalJSON converts JSON configuration from raw bytes into a
+// ConfiguredJarList structure.
+func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {
+ // Try and unmarshal into a []string each item of which contains a pair
+ // <apex>:<jar>.
+ var list []string
+ err := json.Unmarshal(b, &list)
+ if err != nil {
+ // Did not work so return
+ return err
+ }
+
+ apexes, jars, err := splitListOfPairsIntoPairOfLists(list)
+ if err != nil {
+ return err
+ }
+ l.apexes = apexes
+ l.jars = jars
+ return nil
+}
+
+func (l *ConfiguredJarList) MarshalJSON() ([]byte, error) {
+ if len(l.apexes) != len(l.jars) {
+ return nil, errors.New(fmt.Sprintf("Inconsistent ConfiguredJarList: apexes: %q, jars: %q", l.apexes, l.jars))
+ }
+
+ list := make([]string, 0, len(l.apexes))
+
+ for i := 0; i < len(l.apexes); i++ {
+ list = append(list, l.apexes[i]+":"+l.jars[i])
+ }
+
+ return json.Marshal(list)
+}
+
+// ModuleStem hardcodes the stem of framework-minus-apex to return "framework".
+//
+// TODO(b/139391334): hard coded until we find a good way to query the stem of a
+// module before any other mutators are run.
+func ModuleStem(module string) string {
+ if module == "framework-minus-apex" {
+ return "framework"
+ }
+ return module
+}
+
+// DevicePaths computes the on-device paths for the list of (apex, jar) pairs,
+// based on the operating system.
+func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
+ paths := make([]string, l.Len())
+ for i, jar := range l.jars {
+ apex := l.apexes[i]
+ name := ModuleStem(jar) + ".jar"
+
+ var subdir string
+ if apex == "platform" {
+ subdir = "system/framework"
+ } else if apex == "system_ext" {
+ subdir = "system_ext/framework"
+ } else {
+ subdir = filepath.Join("apex", apex, "javalib")
+ }
+
+ if ostype.Class == Host {
+ paths[i] = filepath.Join(cfg.Getenv("OUT_DIR"), "host", cfg.PrebuiltOS(), subdir, name)
+ } else {
+ paths[i] = filepath.Join("/", subdir, name)
+ }
+ }
+ return paths
+}
+
+func (l *ConfiguredJarList) String() string {
+ var pairs []string
+ for i := 0; i < l.Len(); i++ {
+ pairs = append(pairs, l.apexes[i]+":"+l.jars[i])
+ }
+ return strings.Join(pairs, ",")
+}
+
+func splitListOfPairsIntoPairOfLists(list []string) ([]string, []string, error) {
+ // Now we need to populate this list by splitting each item in the slice of
+ // pairs and appending them to the appropriate list of apexes or jars.
+ apexes := make([]string, len(list))
+ jars := make([]string, len(list))
+
+ for i, apexjar := range list {
+ apex, jar, err := splitConfiguredJarPair(apexjar)
+ if err != nil {
+ return nil, nil, err
+ }
+ apexes[i] = apex
+ jars[i] = jar
+ }
+
+ return apexes, jars, nil
+}
+
+// Expected format for apexJarValue = <apex name>:<jar name>
+func splitConfiguredJarPair(str string) (string, string, error) {
+ pair := strings.SplitN(str, ":", 2)
+ if len(pair) == 2 {
+ apex := pair[0]
+ jar := pair[1]
+ if apex == "" {
+ return apex, jar, fmt.Errorf("invalid apex '%s' in <apex>:<jar> pair '%s', expected format: <apex>:<jar>", apex, str)
+ }
+ return apex, jar, nil
+ } else {
+ return "error-apex", "error-jar", fmt.Errorf("malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
+ }
+}
+
+// EmptyConfiguredJarList returns an empty jar list.
+func EmptyConfiguredJarList() ConfiguredJarList {
+ return ConfiguredJarList{}
+}
+
+var earlyBootJarsKey = NewOnceKey("earlyBootJars")
diff --git a/android/defaults.go b/android/defaults.go
index 03b2efb..31d6014 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -15,8 +15,6 @@
package android
import (
- "bytes"
- "fmt"
"reflect"
"github.com/google/blueprint"
@@ -55,7 +53,7 @@
d.hook = hook
}
-func (d *DefaultableModuleBase) callHookIfAvailable(ctx DefaultableHookContext) {
+func (d *DefaultableModuleBase) CallHookIfAvailable(ctx DefaultableHookContext) {
if d.hook != nil {
d.hook(ctx)
}
@@ -69,11 +67,9 @@
// Set the property structures into which defaults will be added.
setProperties(props []interface{}, variableProperties interface{})
- // Apply defaults from the supplied DefaultsModule to the property structures supplied to
+ // Apply defaults from the supplied Defaults to the property structures supplied to
// setProperties(...).
- applyDefaults(TopDownMutatorContext, []DefaultsModule)
-
- applySingleDefaultsWithTracker(EarlyModuleContext, DefaultsModule, defaultsTrackerFunc)
+ applyDefaults(TopDownMutatorContext, []Defaults)
// Set the hook to be called after any defaults have been applied.
//
@@ -82,7 +78,7 @@
SetDefaultableHook(hook DefaultableHook)
// Call the hook if specified.
- callHookIfAvailable(context DefaultableHookContext)
+ CallHookIfAvailable(context DefaultableHookContext)
}
type DefaultableModule interface {
@@ -119,23 +115,9 @@
Defaults_visibility []string
}
-// AdditionalDefaultsProperties contains properties of defaults modules which
-// can have other defaults applied.
-type AdditionalDefaultsProperties struct {
-
- // The list of properties set by the default whose values must not be changed by any module that
- // applies these defaults. It is an error if a property is not supported by the defaults module or
- // has not been set to a non-zero value. If this contains "*" then that must be the only entry in
- // which case all properties that are set on this defaults will be protected (except the
- // protected_properties and visibility.
- Protected_properties []string
-}
-
type DefaultsModuleBase struct {
DefaultableModuleBase
- defaultsProperties AdditionalDefaultsProperties
-
// Included to support setting bazel_module.label for multiple Soong modules to the same Bazel
// target. This is primarily useful for modules that were architecture specific and instead are
// handled in Bazel as a select().
@@ -169,18 +151,6 @@
// DefaultsModuleBase will type-assert to the Defaults interface.
isDefaults() bool
- // additionalDefaultableProperties returns additional properties provided by the defaults which
- // can themselves have defaults applied.
- additionalDefaultableProperties() []interface{}
-
- // protectedProperties returns the names of the properties whose values cannot be changed by a
- // module that applies these defaults.
- protectedProperties() []string
-
- // setProtectedProperties sets the names of the properties whose values cannot be changed by a
- // module that applies these defaults.
- setProtectedProperties(protectedProperties []string)
-
// Get the structures containing the properties for which defaults can be provided.
properties() []interface{}
@@ -197,18 +167,6 @@
Bazelable
}
-func (d *DefaultsModuleBase) additionalDefaultableProperties() []interface{} {
- return []interface{}{&d.defaultsProperties}
-}
-
-func (d *DefaultsModuleBase) protectedProperties() []string {
- return d.defaultsProperties.Protected_properties
-}
-
-func (d *DefaultsModuleBase) setProtectedProperties(protectedProperties []string) {
- d.defaultsProperties.Protected_properties = protectedProperties
-}
-
func (d *DefaultsModuleBase) properties() []interface{} {
return d.defaultableProperties
}
@@ -232,10 +190,6 @@
&ApexProperties{},
&distProperties{})
- // Additional properties of defaults modules that can themselves have
- // defaults applied.
- module.AddProperties(module.additionalDefaultableProperties()...)
-
// Bazel module must be initialized _before_ Defaults to be included in cc_defaults module.
InitBazelModule(module)
initAndroidModuleBase(module)
@@ -263,58 +217,6 @@
// The applicable licenses property for defaults is 'licenses'.
setPrimaryLicensesProperty(module, "licenses", &commonProperties.Licenses)
-
- AddLoadHook(module, func(ctx LoadHookContext) {
-
- protectedProperties := module.protectedProperties()
- if len(protectedProperties) == 0 {
- return
- }
-
- propertiesAvailable := map[string]struct{}{}
- propertiesSet := map[string]struct{}{}
-
- // A defaults tracker which will keep track of which properties have been set on this module.
- collector := func(defaults DefaultsModule, property string, dstValue interface{}, srcValue interface{}) bool {
- value := reflect.ValueOf(dstValue)
- propertiesAvailable[property] = struct{}{}
- if !value.IsZero() {
- propertiesSet[property] = struct{}{}
- }
- // Skip all the properties so that there are no changes to the defaults.
- return false
- }
-
- // Try and apply this module's defaults to itself, so that the properties can be collected but
- // skip all the properties so it doesn't actually do anything.
- module.applySingleDefaultsWithTracker(ctx, module, collector)
-
- if InList("*", protectedProperties) {
- if len(protectedProperties) != 1 {
- ctx.PropertyErrorf("protected_properties", `if specified then "*" must be the only property listed`)
- return
- }
-
- // Do not automatically protect the protected_properties property.
- delete(propertiesSet, "protected_properties")
-
- // Or the visibility property.
- delete(propertiesSet, "visibility")
-
- // Replace the "*" with the names of all the properties that have been set.
- protectedProperties = SortedStringKeys(propertiesSet)
- module.setProtectedProperties(protectedProperties)
- } else {
- for _, property := range protectedProperties {
- if _, ok := propertiesAvailable[property]; !ok {
- ctx.PropertyErrorf(property, "property is not supported by this module type %q",
- ctx.ModuleType())
- } else if _, ok := propertiesSet[property]; !ok {
- ctx.PropertyErrorf(property, "is not set; protected properties must be explicitly set")
- }
- }
- }
- })
}
var _ Defaults = (*DefaultsModuleBase)(nil)
@@ -366,204 +268,35 @@
b.setNamespacedVariableProps(dst)
}
-// defaultValueInfo contains information about each default value that applies to a protected
-// property.
-type defaultValueInfo struct {
- // The DefaultsModule providing the value, which may be defined on that module or applied as a
- // default from other modules.
- module Module
-
- // The default value, as returned by getComparableValue
- defaultValue reflect.Value
-}
-
-// protectedPropertyInfo contains information about each property that has to be protected when
-// applying defaults.
-type protectedPropertyInfo struct {
- // True if the property was set on the module to which defaults are applied, this is an error.
- propertySet bool
-
- // The original value of the property on the module, as returned by getComparableValue.
- originalValue reflect.Value
-
- // A list of defaults for the property that are being applied.
- defaultValues []defaultValueInfo
-}
-
-// getComparableValue takes a reflect.Value that may be a pointer to another value and returns a
-// reflect.Value to the underlying data or the original if was not a pointer or was nil. The
-// returned values can then be compared for equality.
-func getComparableValue(value reflect.Value) reflect.Value {
- if value.IsZero() {
- return value
- }
- for value.Kind() == reflect.Ptr {
- value = value.Elem()
- }
- return value
-}
-
func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
- defaultsList []DefaultsModule) {
-
- // Collate information on all the properties protected by each of the default modules applied
- // to this module.
- allProtectedProperties := map[string]*protectedPropertyInfo{}
- for _, defaults := range defaultsList {
- for _, property := range defaults.protectedProperties() {
- info := allProtectedProperties[property]
- if info == nil {
- info = &protectedPropertyInfo{}
- allProtectedProperties[property] = info
- }
- }
- }
-
- // If there are any protected properties then collate information about attempts to change them.
- var protectedPropertyInfoCollector defaultsTrackerFunc
- if len(allProtectedProperties) > 0 {
- protectedPropertyInfoCollector = func(defaults DefaultsModule, property string,
- dstValue interface{}, srcValue interface{}) bool {
-
- // If the property is not protected then return immediately.
- info := allProtectedProperties[property]
- if info == nil {
- return true
- }
-
- currentValue := reflect.ValueOf(dstValue)
- if info.defaultValues == nil {
- info.propertySet = !currentValue.IsZero()
- info.originalValue = getComparableValue(currentValue)
- }
-
- defaultValue := reflect.ValueOf(srcValue)
- if !defaultValue.IsZero() {
- info.defaultValues = append(info.defaultValues,
- defaultValueInfo{defaults, getComparableValue(defaultValue)})
- }
-
- return true
- }
- }
+ defaultsList []Defaults) {
for _, defaults := range defaultsList {
- if ctx.Config().runningAsBp2Build {
+ if ctx.Config().BuildMode == Bp2build {
applyNamespacedVariableDefaults(defaults, ctx)
}
-
- defaultable.applySingleDefaultsWithTracker(ctx, defaults, protectedPropertyInfoCollector)
- }
-
- // Check the status of any protected properties.
- for property, info := range allProtectedProperties {
- if len(info.defaultValues) == 0 {
- // No defaults were applied to the protected properties. Possibly because this module type
- // does not support any of them.
- continue
- }
-
- // Check to make sure that there are no conflicts between the defaults.
- conflictingDefaults := false
- previousDefaultValue := reflect.ValueOf(false)
- for _, defaultInfo := range info.defaultValues {
- defaultValue := defaultInfo.defaultValue
- if previousDefaultValue.IsZero() {
- previousDefaultValue = defaultValue
- } else if !reflect.DeepEqual(previousDefaultValue.Interface(), defaultValue.Interface()) {
- conflictingDefaults = true
- break
- }
- }
-
- if conflictingDefaults {
- var buf bytes.Buffer
- for _, defaultInfo := range info.defaultValues {
- buf.WriteString(fmt.Sprintf("\n defaults module %q provides value %#v",
- ctx.OtherModuleName(defaultInfo.module), defaultInfo.defaultValue))
- }
- result := buf.String()
- ctx.ModuleErrorf("has conflicting default values for protected property %q:%s", property, result)
- continue
- }
-
- // Now check to see whether there the current module tried to override/append to the defaults.
- if info.propertySet {
- originalValue := info.originalValue
- // Just compare against the first defaults.
- defaultValue := info.defaultValues[0].defaultValue
- defaults := info.defaultValues[0].module
-
- if originalValue.Kind() == reflect.Slice {
- ctx.ModuleErrorf("attempts to append %q to protected property %q's value of %q defined in module %q",
- originalValue,
- property,
- defaultValue,
- ctx.OtherModuleName(defaults))
+ for _, prop := range defaultable.defaultableProperties {
+ if prop == defaultable.defaultableVariableProperties {
+ defaultable.applyDefaultVariableProperties(ctx, defaults, prop)
} else {
- same := reflect.DeepEqual(originalValue.Interface(), defaultValue.Interface())
- message := ""
- if same {
- message = fmt.Sprintf(" with a matching value (%#v) so this property can simply be removed.", originalValue)
- } else {
- message = fmt.Sprintf(" with a different value (override %#v with %#v) so removing the property may necessitate other changes.", defaultValue, originalValue)
- }
- ctx.ModuleErrorf("attempts to override protected property %q defined in module %q%s",
- property,
- ctx.OtherModuleName(defaults), message)
+ defaultable.applyDefaultProperties(ctx, defaults, prop)
}
}
}
}
-func (defaultable *DefaultableModuleBase) applySingleDefaultsWithTracker(ctx EarlyModuleContext, defaults DefaultsModule, tracker defaultsTrackerFunc) {
- for _, prop := range defaultable.defaultableProperties {
- var err error
- if prop == defaultable.defaultableVariableProperties {
- err = defaultable.applyDefaultVariableProperties(defaults, prop, tracker)
- } else {
- err = defaultable.applyDefaultProperties(defaults, prop, tracker)
- }
- if err != nil {
- if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
- ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
- } else {
- panic(err)
- }
- }
- }
-}
-
-// defaultsTrackerFunc is the type of a function that can be used to track how defaults are applied.
-type defaultsTrackerFunc func(defaults DefaultsModule, property string,
- dstValue interface{}, srcValue interface{}) bool
-
-// filterForTracker wraps a defaultsTrackerFunc in a proptools.ExtendPropertyFilterFunc
-func filterForTracker(defaults DefaultsModule, tracker defaultsTrackerFunc) proptools.ExtendPropertyFilterFunc {
- if tracker == nil {
- return nil
- }
- return func(property string,
- dstField, srcField reflect.StructField,
- dstValue, srcValue interface{}) (bool, error) {
-
- apply := tracker(defaults, property, dstValue, srcValue)
- return apply, nil
- }
-}
-
// Product variable properties need special handling, the type of the filtered product variable
// property struct may not be identical between the defaults module and the defaultable module.
// Use PrependMatchingProperties to apply whichever properties match.
-func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(defaults DefaultsModule,
- defaultableProp interface{}, tracker defaultsTrackerFunc) error {
+func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx TopDownMutatorContext,
+ defaults Defaults, defaultableProp interface{}) {
if defaultableProp == nil {
- return nil
+ return
}
defaultsProp := defaults.productVariableProperties()
if defaultsProp == nil {
- return nil
+ return
}
dst := []interface{}{
@@ -573,26 +306,31 @@
proptools.CloneEmptyProperties(reflect.ValueOf(defaultsProp)).Interface(),
}
- filter := filterForTracker(defaults, tracker)
-
- return proptools.PrependMatchingProperties(dst, defaultsProp, filter)
+ err := proptools.PrependMatchingProperties(dst, defaultsProp, nil)
+ if err != nil {
+ if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+ ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ } else {
+ panic(err)
+ }
+ }
}
-func (defaultable *DefaultableModuleBase) applyDefaultProperties(defaults DefaultsModule,
- defaultableProp interface{}, checker defaultsTrackerFunc) error {
-
- filter := filterForTracker(defaults, checker)
+func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMutatorContext,
+ defaults Defaults, defaultableProp interface{}) {
for _, def := range defaults.properties() {
if proptools.TypeEqual(defaultableProp, def) {
- err := proptools.PrependProperties(defaultableProp, def, filter)
+ err := proptools.PrependProperties(defaultableProp, def, nil)
if err != nil {
- return err
+ if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+ ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ } else {
+ panic(err)
+ }
}
}
}
-
- return nil
}
func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
@@ -609,12 +347,12 @@
func defaultsMutator(ctx TopDownMutatorContext) {
if defaultable, ok := ctx.Module().(Defaultable); ok {
if len(defaultable.defaults().Defaults) > 0 {
- var defaultsList []DefaultsModule
+ var defaultsList []Defaults
seen := make(map[Defaults]bool)
ctx.WalkDeps(func(module, parent Module) bool {
if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag {
- if defaults, ok := module.(DefaultsModule); ok {
+ if defaults, ok := module.(Defaults); ok {
if !seen[defaults] {
seen[defaults] = true
defaultsList = append(defaultsList, defaults)
@@ -630,6 +368,6 @@
defaultable.applyDefaults(ctx, defaultsList)
}
- defaultable.callHookIfAvailable(ctx)
+ defaultable.CallHookIfAvailable(ctx)
}
}
diff --git a/android/defaults_test.go b/android/defaults_test.go
index d80f40c..a7542ab 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -19,14 +19,7 @@
)
type defaultsTestProperties struct {
- Foo []string
- Bar []string
- Nested struct {
- Fizz *bool
- }
- Other struct {
- Buzz *string
- }
+ Foo []string
}
type defaultsTestModule struct {
@@ -137,167 +130,3 @@
// TODO: missing transitive defaults is currently not handled
_ = missingTransitiveDefaults
}
-
-func TestProtectedProperties_ProtectedPropertyNotSet(t *testing.T) {
- bp := `
- defaults {
- name: "transitive",
- protected_properties: ["foo"],
- }
- `
-
- GroupFixturePreparers(
- prepareForDefaultsTest,
- FixtureWithRootAndroidBp(bp),
- ).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
- "module \"transitive\": foo: is not set; protected properties must be explicitly set")).
- RunTest(t)
-}
-
-func TestProtectedProperties_ProtectedPropertyNotLeaf(t *testing.T) {
- bp := `
- defaults {
- name: "transitive",
- protected_properties: ["nested"],
- nested: {
- fizz: true,
- },
- }
- `
-
- GroupFixturePreparers(
- prepareForDefaultsTest,
- FixtureWithRootAndroidBp(bp),
- ).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
- `\Qmodule "transitive": nested: property is not supported by this module type "defaults"\E`)).
- RunTest(t)
-}
-
-// TestProtectedProperties_ApplyDefaults makes sure that the protected_properties property has
-// defaults applied.
-func TestProtectedProperties_HasDefaultsApplied(t *testing.T) {
-
- bp := `
- defaults {
- name: "transitive",
- protected_properties: ["foo"],
- foo: ["transitive"],
- }
-
- defaults {
- name: "defaults",
- defaults: ["transitive"],
- protected_properties: ["bar"],
- bar: ["defaults"],
- }
- `
-
- result := GroupFixturePreparers(
- prepareForDefaultsTest,
- FixtureWithRootAndroidBp(bp),
- ).RunTest(t)
-
- defaults := result.Module("defaults", "").(DefaultsModule)
- AssertDeepEquals(t, "defaults protected properties", []string{"foo", "bar"}, defaults.protectedProperties())
-}
-
-// TestProtectedProperties_ProtectAllProperties makes sure that protected_properties: ["*"] protects
-// all properties.
-func TestProtectedProperties_ProtectAllProperties(t *testing.T) {
-
- bp := `
- defaults {
- name: "transitive",
- protected_properties: ["other.buzz"],
- other: {
- buzz: "transitive",
- },
- }
-
- defaults {
- name: "defaults",
- defaults: ["transitive"],
- visibility: ["//visibility:private"],
- protected_properties: ["*"],
- foo: ["other"],
- bar: ["defaults"],
- nested: {
- fizz: true,
- }
- }
- `
-
- result := GroupFixturePreparers(
- prepareForDefaultsTest,
- FixtureWithRootAndroidBp(bp),
- ).RunTest(t)
-
- defaults := result.Module("defaults", "").(DefaultsModule)
- AssertDeepEquals(t, "defaults protected properties", []string{"other.buzz", "bar", "foo", "nested.fizz"},
- defaults.protectedProperties())
-}
-
-func TestProtectedProperties_DetectedOverride(t *testing.T) {
- bp := `
- defaults {
- name: "defaults",
- protected_properties: ["foo", "nested.fizz"],
- foo: ["defaults"],
- nested: {
- fizz: true,
- },
- }
-
- test {
- name: "foo",
- defaults: ["defaults"],
- foo: ["module"],
- nested: {
- fizz: false,
- },
- }
- `
-
- GroupFixturePreparers(
- prepareForDefaultsTest,
- FixtureWithRootAndroidBp(bp),
- ).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(
- []string{
- `\Qmodule "foo": attempts to append ["module"] to protected property "foo"'s value of ["defaults"] defined in module "defaults"\E`,
- `\Qmodule "foo": attempts to override protected property "nested.fizz" defined in module "defaults" with a different value (override true with false) so removing the property may necessitate other changes.\E`,
- })).RunTest(t)
-}
-
-func TestProtectedProperties_DefaultsConflict(t *testing.T) {
- bp := `
- defaults {
- name: "defaults1",
- protected_properties: ["other.buzz"],
- other: {
- buzz: "value",
- },
- }
-
- defaults {
- name: "defaults2",
- protected_properties: ["other.buzz"],
- other: {
- buzz: "another",
- },
- }
-
- test {
- name: "foo",
- defaults: ["defaults1", "defaults2"],
- }
- `
-
- GroupFixturePreparers(
- prepareForDefaultsTest,
- FixtureWithRootAndroidBp(bp),
- ).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
- `\Qmodule "foo": has conflicting default values for protected property "other.buzz":
- defaults module "defaults1" provides value "value"
- defaults module "defaults2" provides value "another"\E`,
- )).RunTest(t)
-}
diff --git a/android/defs.go b/android/defs.go
index 362b382..18eed2d 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -25,7 +25,8 @@
)
var (
- pctx = NewPackageContext("android/soong/android")
+ pctx = NewPackageContext("android/soong/android")
+ exportedVars = NewExportedVariables(pctx)
cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
Config.CpPreserveSymlinksFlags)
@@ -57,6 +58,14 @@
},
"cpFlags", "extraCmds")
+ // A copy rule that doesn't preserve symlinks.
+ CpNoPreserveSymlink = pctx.AndroidStaticRule("CpNoPreserveSymlink",
+ blueprint.RuleParams{
+ Command: "rm -f $out && cp $cpFlags $in $out$extraCmds",
+ Description: "cp $out",
+ },
+ "cpFlags", "extraCmds")
+
// A copy rule that only updates the output if it changed.
CpIfChanged = pctx.AndroidStaticRule("CpIfChanged",
blueprint.RuleParams{
@@ -68,7 +77,7 @@
CpExecutable = pctx.AndroidStaticRule("CpExecutable",
blueprint.RuleParams{
- Command: "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out && chmod +x $out$extraCmds",
+ Command: "rm -f $out && cp $cpFlags $in $out && chmod +x $out$extraCmds",
Description: "cp $out",
},
"cpFlags", "extraCmds")
@@ -128,6 +137,13 @@
pctx.VariableFunc("RBEWrapper", func(ctx PackageVarContext) string {
return ctx.Config().RBEWrapper()
})
+
+ exportedVars.ExportStringList("NeverAllowNotInIncludeDir", neverallowNotInIncludeDir)
+ exportedVars.ExportStringList("NeverAllowNoUseIncludeDir", neverallowNoUseIncludeDir)
+}
+
+func BazelCcToolchainVars(config Config) string {
+ return BazelToolchainVars(config, exportedVars)
}
var (
@@ -166,10 +182,15 @@
// WriteFileRule creates a ninja rule to write contents to a file. The contents will be escaped
// so that the file contains exactly the contents passed to the function, plus a trailing newline.
func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
+ WriteFileRuleVerbatim(ctx, outputFile, content+"\n")
+}
+
+// WriteFileRuleVerbatim creates a ninja rule to write contents to a file. The contents will be
+// escaped so that the file contains exactly the contents passed to the function.
+func WriteFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) {
// This is MAX_ARG_STRLEN subtracted with some safety to account for shell escapes
const SHARD_SIZE = 131072 - 10000
- content += "\n"
if len(content) > SHARD_SIZE {
var chunks WritablePaths
for i, c := range ShardString(content, SHARD_SIZE) {
diff --git a/android/filegroup.go b/android/filegroup.go
index a2e69ef..f30ee51 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -15,9 +15,12 @@
package android
import (
+ "path/filepath"
+ "regexp"
"strings"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
"github.com/google/blueprint"
)
@@ -35,14 +38,57 @@
ctx.RegisterModuleType("filegroup_defaults", FileGroupDefaultsFactory)
}
+var convertedProtoLibrarySuffix = "_bp2build_converted"
+
// IsFilegroup checks that a module is a filegroup type
func IsFilegroup(ctx bazel.OtherModuleContext, m blueprint.Module) bool {
return ctx.OtherModuleType(m) == "filegroup"
}
+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]|$)")
+ filegroupLikelyAidlPattern = regexp.MustCompile("(?i)(^|[^a-z])aidl([^a-z]|$)")
+
+ ProtoSrcLabelPartition = bazel.LabelPartition{
+ Extensions: []string{".proto"},
+ LabelMapper: isFilegroupWithPattern(filegroupLikelyProtoPattern),
+ }
+ AidlSrcLabelPartition = bazel.LabelPartition{
+ Extensions: []string{".aidl"},
+ LabelMapper: isFilegroupWithPattern(filegroupLikelyAidlPattern),
+ }
+)
+
+func isFilegroupWithPattern(pattern *regexp.Regexp) bazel.LabelMapper {
+ return func(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
+ }
+ likelyMatched := pattern.MatchString(label.OriginalModuleName)
+ return labelStr, likelyMatched
+ }
+}
+
// https://docs.bazel.build/versions/master/be/general.html#filegroup
type bazelFilegroupAttributes struct {
- Srcs bazel.LabelListAttribute
+ Srcs bazel.LabelListAttribute
+ Applicable_licenses bazel.LabelListAttribute
+}
+
+type bazelAidlLibraryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Strip_import_prefix *string
+ Deps bazel.LabelListAttribute
+}
+
+// api srcs can be contained in filegroups.
+// this should be generated in api_bp2build workspace as well.
+func (fg *fileGroup) ConvertWithApiBp2build(ctx TopDownMutatorContext) {
+ fg.ConvertWithBp2build(ctx)
}
// ConvertWithBp2build performs bp2build conversion of filegroup
@@ -70,16 +116,65 @@
}
}
- attrs := &bazelFilegroupAttributes{
- Srcs: srcs,
- }
+ // Convert module that has only AIDL files to aidl_library
+ // If the module has a mixed bag of AIDL and non-AIDL files, split the filegroup manually
+ // and then convert
+ if fg.ShouldConvertToAidlLibrary(ctx) {
+ tags := []string{"apex_available=//apex_available:anyapex"}
+ deps := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, fg.properties.Aidl.Deps))
- props := bazel.BazelTargetModuleProperties{
- Rule_class: "filegroup",
- Bzl_load_location: "//build/bazel/rules:filegroup.bzl",
- }
+ attrs := &bazelAidlLibraryAttributes{
+ Srcs: srcs,
+ Strip_import_prefix: fg.properties.Path,
+ Deps: deps,
+ }
- ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "aidl_library",
+ Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(
+ props,
+ CommonAttributes{
+ Name: fg.Name(),
+ Tags: bazel.MakeStringListAttribute(tags),
+ },
+ attrs)
+ } else {
+ if fg.ShouldConvertToProtoLibrary(ctx) {
+ attrs := &ProtoAttrs{
+ Srcs: srcs,
+ Strip_import_prefix: fg.properties.Path,
+ }
+
+ tags := []string{
+ "apex_available=//apex_available:anyapex",
+ // TODO(b/246997908): we can remove this tag if we could figure out a solution for this bug.
+ "manual",
+ }
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
+ CommonAttributes{
+ Name: fg.Name() + convertedProtoLibrarySuffix,
+ Tags: bazel.MakeStringListAttribute(tags),
+ },
+ attrs)
+ }
+
+ // TODO(b/242847534): Still convert to a filegroup because other unconverted
+ // modules may depend on the filegroup
+ attrs := &bazelFilegroupAttributes{
+ Srcs: srcs,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "filegroup",
+ Bzl_load_location: "//build/bazel/rules:filegroup.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
+ }
}
type fileGroupProperties struct {
@@ -97,17 +192,28 @@
// Create a make variable with the specified name that contains the list of files in the
// filegroup, relative to the root of the source tree.
Export_to_make_var *string
+
+ // aidl is explicitly provided for implicit aidl dependencies
+ // TODO(b/278298615): aidl prop is a no-op in Soong and is an escape hatch
+ // to include implicit aidl dependencies for bazel migration compatibility
+ Aidl struct {
+ // List of aidl files or filegroup depended on by srcs
+ Deps []string `android:"path"`
+ }
}
type fileGroup struct {
ModuleBase
BazelModuleBase
DefaultableModuleBase
+ FileGroupAsLibrary
properties fileGroupProperties
srcs Paths
}
+var _ MixedBuildBuildable = (*fileGroup)(nil)
var _ SourceFileProducer = (*fileGroup)(nil)
+var _ FileGroupAsLibrary = (*fileGroup)(nil)
// filegroup contains a list of files that are referenced by other modules
// properties (such as "srcs") using the syntax ":<name>". filegroup are
@@ -121,33 +227,21 @@
return module
}
-func (fg *fileGroup) maybeGenerateBazelBuildActions(ctx ModuleContext) {
- if !fg.MixedBuildsEnabled(ctx) {
- return
- }
+var _ blueprint.JSONActionSupplier = (*fileGroup)(nil)
- archVariant := ctx.Arch().String()
- 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.String()
- 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) {
@@ -155,8 +249,6 @@
if fg.properties.Path != nil {
fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
}
-
- fg.maybeGenerateBazelBuildActions(ctx)
}
func (fg *fileGroup) Srcs() Paths {
@@ -169,6 +261,101 @@
}
}
+func (fg *fileGroup) QueueBazelCall(ctx BaseModuleContext) {
+ bazelCtx := ctx.Config().BazelContext
+
+ bazelCtx.QueueBazelRequest(
+ fg.GetBazelLabel(ctx, fg),
+ cquery.GetOutputFiles,
+ configKey{arch: Common.String(), osType: CommonOS})
+}
+
+func (fg *fileGroup) IsMixedBuildSupported(ctx BaseModuleContext) bool {
+ // TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups
+ return false
+}
+
+func (fg *fileGroup) ProcessBazelQueryResponse(ctx ModuleContext) {
+ bazelCtx := ctx.Config().BazelContext
+ // This is a short-term solution because we rely on info from Android.bp to handle
+ // a converted module. This will block when we want to remove Android.bp for all
+ // converted modules at some point.
+ // TODO(b/242847534): Implement a long-term solution in which we don't need to rely
+ // on info form Android.bp for modules that are already converted to Bazel
+ relativeRoot := ctx.ModuleDir()
+ if fg.properties.Path != nil {
+ relativeRoot = filepath.Join(relativeRoot, *fg.properties.Path)
+ }
+
+ filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{arch: Common.String(), osType: CommonOS})
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
+ }
+
+ bazelOuts := make(Paths, 0, len(filePaths))
+ for _, p := range filePaths {
+ bazelOuts = append(bazelOuts, PathForBazelOutRelative(ctx, relativeRoot, p))
+ }
+ fg.srcs = bazelOuts
+}
+
+func (fg *fileGroup) ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool {
+ return fg.shouldConvertToLibrary(ctx, ".aidl")
+}
+
+func (fg *fileGroup) ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool {
+ return fg.shouldConvertToLibrary(ctx, ".proto")
+}
+
+func (fg *fileGroup) shouldConvertToLibrary(ctx BazelConversionPathContext, suffix string) bool {
+ if len(fg.properties.Srcs) == 0 || !fg.ShouldConvertWithBp2build(ctx) {
+ return false
+ }
+ for _, src := range fg.properties.Srcs {
+ if !strings.HasSuffix(src, suffix) {
+ return false
+ }
+ }
+ return true
+}
+
+func (fg *fileGroup) GetAidlLibraryLabel(ctx BazelConversionPathContext) string {
+ return fg.getFileGroupAsLibraryLabel(ctx)
+}
+
+func (fg *fileGroup) GetProtoLibraryLabel(ctx BazelConversionPathContext) string {
+ return fg.getFileGroupAsLibraryLabel(ctx) + convertedProtoLibrarySuffix
+}
+
+func (fg *fileGroup) getFileGroupAsLibraryLabel(ctx BazelConversionPathContext) string {
+ if ctx.OtherModuleDir(fg.module) == ctx.ModuleDir() {
+ return ":" + fg.Name()
+ } else {
+ return fg.GetBazelLabel(ctx, fg)
+ }
+}
+
+// Given a name in srcs prop, check to see if the name references a filegroup
+// and the filegroup is converted to aidl_library
+func IsConvertedToAidlLibrary(ctx BazelConversionPathContext, name string) bool {
+ if fg, ok := ToFileGroupAsLibrary(ctx, name); ok {
+ return fg.ShouldConvertToAidlLibrary(ctx)
+ }
+ return false
+}
+
+func ToFileGroupAsLibrary(ctx BazelConversionPathContext, name string) (FileGroupAsLibrary, bool) {
+ if module, ok := ctx.ModuleFromName(name); ok {
+ if IsFilegroup(ctx, module) {
+ if fg, ok := module.(FileGroupAsLibrary); ok {
+ return fg, true
+ }
+ }
+ }
+ return nil, false
+}
+
// Defaults
type FileGroupDefaults struct {
ModuleBase
diff --git a/android/filegroup_test.go b/android/filegroup_test.go
index 670037d..893da57 100644
--- a/android/filegroup_test.go
+++ b/android/filegroup_test.go
@@ -1,9 +1,64 @@
package android
import (
+ "path/filepath"
"testing"
)
+func TestFileGroupWithPathProp(t *testing.T) {
+ // TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups
+ t.Skip("Re-enable once filegroups are corrected for mixed builds")
+ outBaseDir := "outputbase"
+ pathPrefix := outBaseDir + "/execroot/__main__"
+ expectedOutputfile := filepath.Join(pathPrefix, "a/b/c/d/test.aidl")
+
+ testCases := []struct {
+ bp string
+ rel string
+ }{
+ {
+ bp: `
+ filegroup {
+ name: "baz",
+ srcs: ["a/b/c/d/test.aidl"],
+ path: "a/b",
+ bazel_module: { label: "//:baz" },
+ }
+`,
+ rel: "c/d/test.aidl",
+ },
+ {
+ bp: `
+ filegroup {
+ name: "baz",
+ srcs: ["a/b/c/d/test.aidl"],
+ bazel_module: { label: "//:baz" },
+ }
+`,
+ rel: "a/b/c/d/test.aidl",
+ },
+ }
+
+ for _, testCase := range testCases {
+ outBaseDir := "outputbase"
+ result := GroupFixturePreparers(
+ PrepareForTestWithFilegroup,
+ FixtureModifyConfig(func(config Config) {
+ config.BazelContext = MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToOutputFiles: map[string][]string{
+ "//:baz": []string{"a/b/c/d/test.aidl"},
+ },
+ }
+ }),
+ ).RunTestWithBp(t, testCase.bp)
+
+ fg := result.Module("baz", "").(*fileGroup)
+ AssertStringEquals(t, "src relativeRoot", testCase.rel, fg.srcs[0].Rel())
+ AssertStringEquals(t, "src full path", expectedOutputfile, fg.srcs[0].String())
+ }
+}
+
func TestFilegroupDefaults(t *testing.T) {
bp := FixtureAddTextFile("p/Android.bp", `
filegroup_defaults {
diff --git a/android/fixture.go b/android/fixture.go
index 3f01f5a..dbc3bc5 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "runtime"
"strings"
"testing"
)
@@ -213,6 +214,46 @@
})
}
+// FixtureTestRunner determines the type of test to run.
+//
+// If no custom FixtureTestRunner is provided (using the FixtureSetTestRunner) then the default test
+// runner will run a standard Soong test that corresponds to what happens when Soong is run on the
+// command line.
+type FixtureTestRunner interface {
+ // FinalPreparer is a function that is run immediately before parsing the blueprint files. It is
+ // intended to perform the initialization needed by PostParseProcessor.
+ //
+ // It returns a CustomTestResult that is passed into PostParseProcessor and returned from
+ // FixturePreparer.RunTestWithCustomResult. If it needs to return some custom data then it must
+ // provide its own implementation of CustomTestResult and return an instance of that. Otherwise,
+ // it can just return the supplied *TestResult.
+ FinalPreparer(result *TestResult) CustomTestResult
+
+ // PostParseProcessor is called after successfully parsing the blueprint files and can do further
+ // work on the result of parsing the files.
+ //
+ // Successfully parsing simply means that no errors were encountered when parsing the blueprint
+ // files.
+ //
+ // This must collate any information useful for testing, e.g. errs, ninja deps and custom data in
+ // the supplied result.
+ PostParseProcessor(result CustomTestResult)
+}
+
+// FixtureSetTestRunner sets the FixtureTestRunner in the fixture.
+//
+// It is an error if more than one of these is applied to a single fixture. If none of these are
+// applied then the fixture will use the defaultTestRunner which will run the test as if it was
+// being run in `m <target>`.
+func FixtureSetTestRunner(testRunner FixtureTestRunner) FixturePreparer {
+ return newSimpleFixturePreparer(func(fixture *fixture) {
+ if fixture.testRunner != nil {
+ panic("fixture test runner has already been set")
+ }
+ fixture.testRunner = testRunner
+ })
+}
+
// Modify the config
func FixtureModifyConfig(mutator func(config Config)) FixturePreparer {
return newSimpleFixturePreparer(func(f *fixture) {
@@ -339,6 +380,12 @@
})
}
+var PrepareForSkipTestOnMac = newSimpleFixturePreparer(func(fixture *fixture) {
+ if runtime.GOOS != "linux" {
+ fixture.t.Skip("Test is only supported on linux.")
+ }
+})
+
// PrepareForDebug_DO_NOT_SUBMIT puts the fixture into debug which will cause it to output its
// state before running the test.
//
@@ -391,6 +438,21 @@
// Shorthand for Fixture(t).RunTest()
RunTest(t *testing.T) *TestResult
+ // RunTestWithCustomResult runs the test just as RunTest(t) does but instead of returning a
+ // *TestResult it returns the CustomTestResult that was returned by the custom
+ // FixtureTestRunner.PostParseProcessor method that ran the test, or the *TestResult if that
+ // method returned nil.
+ //
+ // This method must be used when needing to access custom data collected by the
+ // FixtureTestRunner.PostParseProcessor method.
+ //
+ // e.g. something like this
+ //
+ // preparers := ...FixtureSetTestRunner(&myTestRunner)...
+ // customResult := preparers.RunTestWithCustomResult(t).(*myCustomTestResult)
+ // doSomething(customResult.data)
+ RunTestWithCustomResult(t *testing.T) CustomTestResult
+
// Run the test with the supplied Android.bp file.
//
// preparer.RunTestWithBp(t, bp) is shorthand for
@@ -619,7 +681,7 @@
MockFS() MockFS
// Run the test, checking any errors reported and returning a TestResult instance.
- RunTest() *TestResult
+ RunTest() CustomTestResult
}
// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
@@ -642,6 +704,39 @@
NinjaDeps []string
}
+func (r *TestResult) testResult() *TestResult { return r }
+
+// CustomTestResult is the interface that FixtureTestRunner implementations who wish to return
+// custom data must implement. It must embed *TestResult and initialize that to the value passed
+// into the method. It is returned from the FixtureTestRunner.FinalPreparer, passed into the
+// FixtureTestRunner.PostParseProcessor and returned from FixturePreparer.RunTestWithCustomResult.
+//
+// e.g. something like this:
+//
+// type myCustomTestResult struct {
+// *android.TestResult
+// data []string
+// }
+//
+// func (r *myTestRunner) FinalPreparer(result *TestResult) CustomTestResult {
+// ... do some final test preparation ...
+// return &myCustomTestResult{TestResult: result)
+// }
+//
+// func (r *myTestRunner) PostParseProcessor(result CustomTestResult) {
+// ...
+// myData := []string {....}
+// ...
+// customResult := result.(*myCustomTestResult)
+// customResult.data = myData
+// }
+type CustomTestResult interface {
+ // testResult returns the embedded *TestResult.
+ testResult() *TestResult
+}
+
+var _ CustomTestResult = (*TestResult)(nil)
+
type TestPathContext struct {
*TestResult
}
@@ -696,6 +791,11 @@
func (b *baseFixturePreparer) RunTest(t *testing.T) *TestResult {
t.Helper()
+ return b.RunTestWithCustomResult(t).testResult()
+}
+
+func (b *baseFixturePreparer) RunTestWithCustomResult(t *testing.T) CustomTestResult {
+ t.Helper()
fixture := b.self.Fixture(t)
return fixture.RunTest()
}
@@ -724,13 +824,16 @@
ctx.SetModuleListFile(ctx.config.mockBpList)
}
- return fixture.RunTest()
+ return fixture.RunTest().testResult()
}
type fixture struct {
// The preparers used to create this fixture.
preparers []*simpleFixturePreparer
+ // The test runner used in this fixture, defaults to defaultTestRunner if not set.
+ testRunner FixtureTestRunner
+
// The gotest state of the go test within which this was created.
t *testing.T
@@ -762,7 +865,7 @@
return f.mockFS
}
-func (f *fixture) RunTest() *TestResult {
+func (f *fixture) RunTest() CustomTestResult {
f.t.Helper()
// If in debug mode output the state of the fixture before running the test.
@@ -800,30 +903,59 @@
// Set the NameResolver in the TestContext.
ctx.NameResolver = resolver
- ctx.Register()
- var ninjaDeps []string
- extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
- if len(errs) == 0 {
- ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- extraNinjaDeps, errs = ctx.PrepareBuildActions(f.config)
- if len(errs) == 0 {
- ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- }
+ // If test runner has not been set then use the default runner.
+ if f.testRunner == nil {
+ f.testRunner = defaultTestRunner
}
+ // Create the result to collate result information.
result := &TestResult{
testContext: testContext{ctx},
fixture: f,
Config: f.config,
- Errs: errs,
- NinjaDeps: ninjaDeps,
+ }
+
+ // Do any last minute preparation before parsing the blueprint files.
+ customResult := f.testRunner.FinalPreparer(result)
+
+ // Parse the blueprint files adding the information to the result.
+ extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
+ result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...)
+ result.Errs = append(result.Errs, errs...)
+
+ if len(result.Errs) == 0 {
+ // If parsing the blueprint files was successful then perform any additional processing.
+ f.testRunner.PostParseProcessor(customResult)
}
f.errorHandler.CheckErrors(f.t, result)
+ return customResult
+}
+
+// standardTestRunner is the implementation of the default test runner
+type standardTestRunner struct{}
+
+func (s *standardTestRunner) FinalPreparer(result *TestResult) CustomTestResult {
+ // Register the hard coded mutators and singletons used by the standard Soong build as well as
+ // any additional instances that have been registered with this fixture.
+ result.TestContext.Register()
return result
}
+func (s *standardTestRunner) PostParseProcessor(customResult CustomTestResult) {
+ result := customResult.(*TestResult)
+ ctx := result.TestContext
+ cfg := result.Config
+ // Prepare the build actions, i.e. run all the mutators, singletons and then invoke the
+ // GenerateAndroidBuildActions methods on all the modules.
+ extraNinjaDeps, errs := ctx.PrepareBuildActions(cfg)
+ result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...)
+ result.CollateErrs(errs)
+}
+
+var defaultTestRunner FixtureTestRunner = &standardTestRunner{}
+
func (f *fixture) outputDebugState() {
fmt.Printf("Begin Fixture State for %s\n", f.t.Name())
if len(f.config.env) == 0 {
@@ -909,3 +1041,10 @@
func (r *TestResult) Module(name string, variant string) Module {
return r.ModuleForTests(name, variant).Module()
}
+
+// CollateErrs adds additional errors to the result and returns true if there is more than one
+// error in the result.
+func (r *TestResult) CollateErrs(errs []error) bool {
+ r.Errs = append(r.Errs, errs...)
+ return len(r.Errs) > 0
+}
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 008aac5..091345b 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "path/filepath"
"strings"
"github.com/google/blueprint/proptools"
@@ -61,6 +62,9 @@
if mod == nil {
continue
}
+ if !mod.Enabled() { // don't depend on variants without build rules
+ continue
+ }
modules = append(modules, mod)
}
}
@@ -70,6 +74,7 @@
out(ctx, gm.output, ctx.ModuleName(gm),
proptools.StringDefault(gm.properties.ArtifactName, defaultName),
[]string{
+ filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName()) + "/",
ctx.Config().OutDir() + "/",
ctx.Config().SoongOutDir() + "/",
}, modules...)
diff --git a/android/hooks.go b/android/hooks.go
index 5e3a4a7..2ad3b5f 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -89,8 +89,17 @@
l.appendPrependHelper(props, proptools.PrependMatchingProperties)
}
-func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
- inherited := []interface{}{&l.Module().base().commonProperties}
+func (l *loadHookContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module {
+ return l.bp.CreateModule(factory, name, props...)
+}
+
+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 {
@@ -101,12 +110,12 @@
filePath, _ := factoryFunc.FileLine(factoryPtr)
typeName = fmt.Sprintf("%s_%s", path.Base(filePath), factoryFunc.Name())
}
- typeName = typeName + "_loadHookModule"
+ typeName = typeName + "_" + ext
- module := l.bp.CreateModule(ModuleFactoryAdaptor(factory), typeName, append(inherited, props...)...).(Module)
+ module := ctx.createModule(ModuleFactoryAdaptor(factory), typeName, append(inherited, props...)...).(Module)
- if l.Module().base().variableProperties != nil && module.base().variableProperties != nil {
- src := l.Module().base().variableProperties
+ 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
@@ -122,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 ebee055..a09422b 100644
--- a/android/license.go
+++ b/android/license.go
@@ -15,7 +15,12 @@
package android
import (
+ "fmt"
+ "os"
+
"github.com/google/blueprint"
+
+ "android/soong/bazel"
)
type licenseKindDependencyTag struct {
@@ -48,15 +53,63 @@
Visibility []string
}
+var _ Bazelable = &licenseModule{}
+
type licenseModule struct {
ModuleBase
DefaultableModuleBase
- SdkBase
+ BazelModuleBase
properties licenseProperties
}
+type bazelLicenseAttributes struct {
+ License_kinds []string
+ Copyright_notice *string
+ License_text bazel.LabelAttribute
+ Package_name *string
+ Visibility []string
+}
+
+func (m *licenseModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+ attrs := &bazelLicenseAttributes{
+ License_kinds: m.properties.License_kinds,
+ Copyright_notice: m.properties.Copyright_notice,
+ Package_name: m.properties.Package_name,
+ Visibility: m.properties.Visibility,
+ }
+
+ // TODO(asmundak): Soong supports multiple license texts while Bazel's license
+ // rule does not. Have android_license create a genrule to concatenate multiple
+ // license texts.
+ if len(m.properties.License_text) > 1 && ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE") {
+ fmt.Fprintf(os.Stderr, "warning: using only the first license_text item from //%s:%s\n",
+ ctx.ModuleDir(), m.Name())
+ }
+ if len(m.properties.License_text) >= 1 {
+ attrs.License_text.SetValue(BazelLabelForModuleSrcSingle(ctx, m.properties.License_text[0]))
+ }
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "android_license",
+ Bzl_load_location: "//build/bazel/rules/license:license.bzl",
+ },
+ CommonAttributes{
+ Name: m.Name(),
+ },
+ attrs)
+}
+
func (m *licenseModule) DepsMutator(ctx BottomUpMutatorContext) {
+ for i, license := range m.properties.License_kinds {
+ for j := i + 1; j < len(m.properties.License_kinds); j++ {
+ if license == m.properties.License_kinds[j] {
+ ctx.ModuleErrorf("Duplicated license kind: %q", license)
+ break
+ }
+ }
+ }
ctx.AddVariationDependencies(nil, licenseKindTag, m.properties.License_kinds...)
}
@@ -78,14 +131,14 @@
module := &licenseModule{}
base := module.base()
- module.AddProperties(&base.nameProperties, &module.properties)
+ module.AddProperties(&base.nameProperties, &module.properties, &base.commonProperties.BazelConversionStatus)
// The visibility property needs to be checked and parsed by the visibility module.
setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
- InitSdkAwareModule(module)
initAndroidModuleBase(module)
InitDefaultableModule(module)
+ InitBazelModule(module)
return module
}
diff --git a/android/license_kind.go b/android/license_kind.go
index 838dedd..24b91e4 100644
--- a/android/license_kind.go
+++ b/android/license_kind.go
@@ -14,6 +14,8 @@
package android
+import "android/soong/bazel"
+
func init() {
RegisterLicenseKindBuildComponents(InitRegistrationContext)
}
@@ -32,13 +34,39 @@
Visibility []string
}
+var _ Bazelable = &licenseKindModule{}
+
type licenseKindModule struct {
ModuleBase
DefaultableModuleBase
+ BazelModuleBase
properties licenseKindProperties
}
+type bazelLicenseKindAttributes struct {
+ Conditions []string
+ Url string
+ Visibility []string
+}
+
+func (m *licenseKindModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+ attrs := &bazelLicenseKindAttributes{
+ Conditions: m.properties.Conditions,
+ Url: m.properties.Url,
+ Visibility: m.properties.Visibility,
+ }
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "license_kind",
+ Bzl_load_location: "@rules_license//rules:license_kind.bzl",
+ },
+ CommonAttributes{
+ Name: m.Name(),
+ },
+ attrs)
+}
+
func (m *licenseKindModule) DepsMutator(ctx BottomUpMutatorContext) {
// Nothing to do.
}
@@ -51,13 +79,14 @@
module := &licenseKindModule{}
base := module.base()
- module.AddProperties(&base.nameProperties, &module.properties)
+ module.AddProperties(&base.nameProperties, &module.properties, &base.commonProperties.BazelConversionStatus)
// The visibility property needs to be checked and parsed by the visibility module.
setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
initAndroidModuleBase(module)
InitDefaultableModule(module)
+ InitBazelModule(module)
return module
}
diff --git a/android/license_metadata.go b/android/license_metadata.go
index f2ab0a4..18b63d3 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -15,7 +15,6 @@
package android
import (
- "fmt"
"sort"
"strings"
@@ -67,6 +66,11 @@
return
}
+ // Defaults add properties and dependencies that get processed on their own.
+ if ctx.OtherModuleDependencyTag(dep) == DefaultsDepTag {
+ return
+ }
+
if ctx.OtherModuleHasProvider(dep, LicenseMetadataProvider) {
info := ctx.OtherModuleProvider(dep, LicenseMetadataProvider).(*LicenseMetadataInfo)
allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
@@ -94,6 +98,11 @@
var orderOnlyDeps Paths
var args []string
+ if n := ctx.ModuleName(); n != "" {
+ args = append(args,
+ "-mn "+proptools.NinjaAndShellEscape(n))
+ }
+
if t := ctx.ModuleType(); t != "" {
args = append(args,
"-mt "+proptools.NinjaAndShellEscape(t))
@@ -139,8 +148,6 @@
if len(outputFiles) > 0 {
args = append(args,
JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(outputFiles.Strings()), "-t "))
- } else {
- args = append(args, fmt.Sprintf("-t //%s:%s", ctx.ModuleDir(), ctx.ModuleName()))
}
// Installed files
diff --git a/android/license_test.go b/android/license_test.go
index 7222cd7..89e7f06 100644
--- a/android/license_test.go
+++ b/android/license_test.go
@@ -90,6 +90,36 @@
},
},
{
+ name: "must not duplicate license_kind",
+ fs: map[string][]byte{
+ "top/Android.bp": []byte(`
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ visibility: ["//visibility:private"],
+ }
+
+ license_kind {
+ name: "top_by_exception_only_2",
+ conditions: ["by_exception_only"],
+ visibility: ["//visibility:private"],
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: [
+ "top_by_exception_only",
+ "top_by_exception_only_2",
+ "top_by_exception_only"
+ ],
+ visibility: ["//visibility:public"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Android.bp:14:5: module "top_proprietary": Duplicated license kind: "top_by_exception_only"`,
+ },
+ },
+ {
name: "license_kind module must exist",
fs: map[string][]byte{
"top/Android.bp": []byte(`
diff --git a/android/licenses.go b/android/licenses.go
index c47b3e6..c6b3243 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -340,4 +340,5 @@
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())
+ ctx.Strict("COMPLIANCE_SBOM", ctx.Config().HostToolPath(ctx, "compliance_sbom").String())
}
diff --git a/android/makevars.go b/android/makevars.go
index 5165a55..0800190 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -471,7 +471,7 @@
fmt.Fprintf(buf, " %s", dep.String())
}
fmt.Fprintln(buf)
- fmt.Fprintln(buf, "\t@echo \"Install $@\"")
+ fmt.Fprintln(buf, "\t@echo \"Install: $@\"")
fmt.Fprintf(buf, "\trm -f $@ && cp -f %s $< $@\n", preserveSymlinksFlag)
if install.executable {
fmt.Fprintf(buf, "\tchmod +x $@\n")
@@ -515,7 +515,7 @@
fromStr = symlink.absFrom
}
- fmt.Fprintln(buf, "\t@echo \"Symlink $@\"")
+ fmt.Fprintln(buf, "\t@echo \"Symlink: $@\"")
fmt.Fprintf(buf, "\trm -f $@ && ln -sfn %s $@", fromStr)
fmt.Fprintln(buf)
fmt.Fprintln(buf)
diff --git a/android/metrics.go b/android/metrics.go
index 9038bde..3d41a1d 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -17,6 +17,7 @@
import (
"io/ioutil"
"runtime"
+ "sort"
"github.com/google/blueprint/metrics"
"google.golang.org/protobuf/proto"
@@ -31,8 +32,13 @@
Variants int
}
-func ReadSoongMetrics(config Config) SoongMetrics {
- return config.Get(soongMetricsOnceKey).(SoongMetrics)
+func readSoongMetrics(config Config) (SoongMetrics, bool) {
+ soongMetrics, ok := config.Peek(soongMetricsOnceKey)
+ if ok {
+ return soongMetrics.(SoongMetrics), true
+ } else {
+ return SoongMetrics{}, false
+ }
}
func init() {
@@ -56,12 +62,14 @@
})
}
-func collectMetrics(config Config, eventHandler metrics.EventHandler) *soong_metrics_proto.SoongBuildMetrics {
+func collectMetrics(config Config, eventHandler *metrics.EventHandler) *soong_metrics_proto.SoongBuildMetrics {
metrics := &soong_metrics_proto.SoongBuildMetrics{}
- soongMetrics := ReadSoongMetrics(config)
- metrics.Modules = proto.Uint32(uint32(soongMetrics.Modules))
- metrics.Variants = proto.Uint32(uint32(soongMetrics.Variants))
+ soongMetrics, ok := readSoongMetrics(config)
+ if ok {
+ metrics.Modules = proto.Uint32(uint32(soongMetrics.Modules))
+ metrics.Variants = proto.Uint32(uint32(soongMetrics.Variants))
+ }
memStats := runtime.MemStats{}
runtime.ReadMemStats(&memStats)
@@ -78,11 +86,28 @@
}
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, eventHandler metrics.EventHandler, metricsFile string) error {
+func WriteMetrics(config Config, eventHandler *metrics.EventHandler, metricsFile string) error {
metrics := collectMetrics(config, eventHandler)
buf, err := proto.Marshal(metrics)
diff --git a/android/module.go b/android/module.go
index 118ce50..ba47453 100644
--- a/android/module.go
+++ b/android/module.go
@@ -35,32 +35,66 @@
var (
DeviceSharedLibrary = "shared_library"
DeviceStaticLibrary = "static_library"
- DeviceExecutable = "executable"
- HostSharedLibrary = "host_shared_library"
- HostStaticLibrary = "host_static_library"
- HostExecutable = "host_executable"
)
+// BuildParameters describes the set of potential parameters to build a Ninja rule.
+// In general, these correspond to a Ninja concept.
type BuildParams struct {
- Rule blueprint.Rule
- Deps blueprint.Deps
- Depfile WritablePath
- Description string
- Output WritablePath
- Outputs WritablePaths
- SymlinkOutput WritablePath
- SymlinkOutputs WritablePaths
- ImplicitOutput WritablePath
+ // A Ninja Rule that will be written to the Ninja file. This allows factoring out common code
+ // among multiple modules to reduce repetition in the Ninja file of action requirements. A rule
+ // can contain variables that should be provided in Args.
+ Rule blueprint.Rule
+ // Deps represents the depfile format. When using RuleBuilder, this defaults to GCC when depfiles
+ // are used.
+ Deps blueprint.Deps
+ // Depfile is a writeable path that allows correct incremental builds when the inputs have not
+ // been fully specified by the Ninja rule. Ninja supports a subset of the Makefile depfile syntax.
+ Depfile WritablePath
+ // A description of the build action.
+ Description string
+ // Output is an output file of the action. When using this field, references to $out in the Ninja
+ // command will refer to this file.
+ Output WritablePath
+ // Outputs is a slice of output file of the action. When using this field, references to $out in
+ // the Ninja command will refer to these files.
+ Outputs WritablePaths
+ // SymlinkOutput is an output file specifically that is a symlink.
+ SymlinkOutput WritablePath
+ // SymlinkOutputs is a slice of output files specifically that is a symlink.
+ SymlinkOutputs WritablePaths
+ // ImplicitOutput is an output file generated by the action. Note: references to `$out` in the
+ // Ninja command will NOT include references to this file.
+ ImplicitOutput WritablePath
+ // ImplicitOutputs is a slice of output files generated by the action. Note: references to `$out`
+ // in the Ninja command will NOT include references to these files.
ImplicitOutputs WritablePaths
- Input Path
- Inputs Paths
- Implicit Path
- Implicits Paths
- OrderOnly Paths
- Validation Path
- Validations Paths
- Default bool
- Args map[string]string
+ // Input is an input file to the Ninja action. When using this field, references to $in in the
+ // Ninja command will refer to this file.
+ Input Path
+ // Inputs is a slice of input files to the Ninja action. When using this field, references to $in
+ // in the Ninja command will refer to these files.
+ Inputs Paths
+ // Implicit is an input file to the Ninja action. Note: references to `$in` in the Ninja command
+ // will NOT include references to this file.
+ Implicit Path
+ // Implicits is a slice of input files to the Ninja action. Note: references to `$in` in the Ninja
+ // command will NOT include references to these files.
+ Implicits Paths
+ // OrderOnly are Ninja order-only inputs to the action. When these are out of date, the output is
+ // not rebuilt until they are built, but changes in order-only dependencies alone do not cause the
+ // output to be rebuilt.
+ OrderOnly Paths
+ // Validation is an output path for a validation action. Validation outputs imply lower
+ // non-blocking priority to building non-validation outputs.
+ Validation Path
+ // Validations is a slice of output path for a validation action. Validation outputs imply lower
+ // non-blocking priority to building non-validation outputs.
+ Validations Paths
+ // Whether to skip outputting a default target statement which will be built by Ninja when no
+ // targets are specified on Ninja's command line.
+ Default bool
+ // Args is a key value mapping for replacements of variables within the Rule
+ Args map[string]string
}
type ModuleBuildParams BuildParams
@@ -506,6 +540,7 @@
InstallInRoot() bool
InstallInVendor() bool
InstallForceOS() (*OsType, *ArchType)
+ PartitionTag(DeviceConfig) string
HideFromMake()
IsHideFromMake() bool
IsSkipInstall() bool
@@ -515,7 +550,7 @@
ExportedToMake() bool
InitRc() Paths
VintfFragments() Paths
- NoticeFiles() Paths
+ EffectiveLicenseKinds() []string
EffectiveLicenseFiles() Paths
AddProperties(props ...interface{})
@@ -831,9 +866,6 @@
// names of other modules to install on target if this module is installed
Target_required []string `android:"arch_variant"`
- // relative path to a file to include in the list of notices for the device
- Notice *string `android:"path"`
-
// The OsType of artifacts that this module variant is responsible for creating.
//
// Set by osMutator
@@ -913,17 +945,8 @@
// constants in image.go, but can also be set to a custom value by individual module types.
ImageVariation string `blueprint:"mutated"`
- // Information about _all_ bp2build targets generated by this module. Multiple targets are
- // supported as Soong handles some things within a single target that we may choose to split into
- // multiple targets, e.g. renderscript, protos, yacc within a cc module.
- Bp2buildInfo []bp2buildInfo `blueprint:"mutated"`
-
- // UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to
- // Bazel
- UnconvertedBp2buildDeps []string `blueprint:"mutated"`
-
- // MissingBp2buildDep stores the module names of direct dependency that were not found
- MissingBp2buildDeps []string `blueprint:"mutated"`
+ // Bazel conversion status
+ BazelConversionStatus BazelConversionStatus `blueprint:"mutated"`
}
// CommonAttributes represents the common Bazel attributes from which properties
@@ -932,8 +955,21 @@
type CommonAttributes struct {
// Soong nameProperties -> Bazel name
Name string
+
// Data mapped from: Required
Data bazel.LabelListAttribute
+
+ // SkipData is neither a Soong nor Bazel target attribute
+ // If true, this will not fill the data attribute automatically
+ // This is useful for Soong modules that have 1:many Bazel targets
+ // Some of the generated Bazel targets might not have a data attribute
+ SkipData *bool
+
+ Tags bazel.StringListAttribute
+
+ Applicable_licenses bazel.LabelListAttribute
+
+ Testonly *bool
}
// constraintAttributes represents Bazel attributes pertaining to build constraints,
@@ -953,6 +989,27 @@
Dists []Dist `android:"arch_variant"`
}
+// CommonTestOptions represents the common `test_options` properties in
+// Android.bp.
+type CommonTestOptions struct {
+ // If the test is a hostside (no device required) unittest that shall be run
+ // during presubmit check.
+ Unit_test *bool
+
+ // Tags provide additional metadata to customize test execution by downstream
+ // test runners. The tags have no special meaning to Soong.
+ Tags []string
+}
+
+// SetAndroidMkEntries sets AndroidMkEntries according to the value of base
+// `test_options`.
+func (t *CommonTestOptions) SetAndroidMkEntries(entries *AndroidMkEntries) {
+ entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(t.Unit_test))
+ if len(t.Tags) > 0 {
+ entries.AddStrings("LOCAL_TEST_OPTIONS_TAGS", t.Tags...)
+ }
+}
+
// The key to use in TaggedDistFiles when a Dist structure does not specify a
// tag property. This intentionally does not use "" as the default because that
// would mean that an empty tag would have a different meaning when used in a dist
@@ -993,8 +1050,8 @@
}
func MakeDefaultDistFiles(paths ...Path) TaggedDistFiles {
- for _, path := range paths {
- if path == nil {
+ for _, p := range paths {
+ if p == nil {
panic("The path to a dist file cannot be nil.")
}
}
@@ -1018,7 +1075,6 @@
MultilibFirst Multilib = "first"
MultilibCommon Multilib = "common"
MultilibCommonFirst Multilib = "common_first"
- MultilibDefault Multilib = ""
)
type HostOrDeviceSupported int
@@ -1162,52 +1218,114 @@
func (attrs *CommonAttributes) fillCommonBp2BuildModuleAttrs(ctx *topDownMutatorContext,
enabledPropertyOverrides bazel.BoolAttribute) constraintAttributes {
- // Assert passed-in attributes include Name
- name := attrs.Name
- if len(name) == 0 {
- ctx.ModuleErrorf("CommonAttributes in fillCommonBp2BuildModuleAttrs expects a `.Name`!")
- }
mod := ctx.Module().base()
- props := &mod.commonProperties
+ // Assert passed-in attributes include Name
+ if len(attrs.Name) == 0 {
+ if ctx.ModuleType() != "package" {
+ ctx.ModuleErrorf("CommonAttributes in fillCommonBp2BuildModuleAttrs expects a `.Name`!")
+ }
+ }
depsToLabelList := func(deps []string) bazel.LabelListAttribute {
return bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, deps))
}
- data := &attrs.Data
-
- required := depsToLabelList(props.Required)
- 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 _, osType := range OsTypeList() {
+ if osType.Class == Host {
+ osSupport[osType.Name] = moduleSupportsHost
+ } else if osType.Class == Device {
+ osSupport[osType.Name] = moduleSupportsDevice
+ }
+ }
}
- 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 {
+ // we can't build this, disable
+ enabledProperty.Value = proptools.BoolPtr(false)
+ } else if mod.commonProperties.Enabled != nil {
+ enabledProperty.SetValue(mod.commonProperties.Enabled)
+ if !*mod.commonProperties.Enabled {
+ for oss, enabled := range osSupport {
+ if val := enabledProperty.SelectValue(bazel.OsConfigurationAxis, oss); enabled && val != nil && *val {
+ // if this should be disabled by default, clear out any enabling we've done
+ enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, oss, nil)
}
}
}
}
- if enabledPropertyOverrides.Value != nil {
- enabledProperty.Value = enabledPropertyOverrides.Value
+ attrs.Applicable_licenses = bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, mod.commonProperties.Licenses))
+
+ // The required property can contain the module itself. This causes a cycle
+ // when generated as the 'data' label list attribute in Bazel. Remove it if
+ // it exists. See b/247985196.
+ _, requiredWithoutCycles := RemoveFromList(ctx.ModuleName(), mod.commonProperties.Required)
+ requiredWithoutCycles = FirstUniqueStrings(requiredWithoutCycles)
+ required := depsToLabelList(requiredWithoutCycles)
+ archVariantProps := mod.GetArchVariantProperties(ctx, &commonProperties{})
+ for axis, configToProps := range archVariantProps {
+ for config, _props := range configToProps {
+ if archProps, ok := _props.(*commonProperties); ok {
+ _, requiredWithoutCycles := RemoveFromList(ctx.ModuleName(), archProps.Required)
+ requiredWithoutCycles = FirstUniqueStrings(requiredWithoutCycles)
+ required.SetSelectValue(axis, config, depsToLabelList(requiredWithoutCycles).Value)
+ if !neitherHostNorDevice {
+ if archProps.Enabled != nil {
+ if axis != bazel.OsConfigurationAxis || osSupport[config] {
+ enabledProperty.SetSelectValue(axis, config, archProps.Enabled)
+ }
+ }
+ }
+ }
+ }
}
- 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)
@@ -1224,33 +1342,38 @@
productConfigEnabledLabels, nil,
})
- moduleSupportsDevice := mod.commonProperties.HostOrDeviceSupported&deviceSupported == deviceSupported
- if mod.commonProperties.HostOrDeviceSupported != NeitherHostNorDeviceSupported && !moduleSupportsDevice {
- enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(false))
- }
-
platformEnabledAttribute, err := enabledProperty.ToLabelListAttribute(
- bazel.LabelList{[]bazel.Label{bazel.Label{Label: "@platforms//:incompatible"}}, nil},
+ bazel.LabelList{[]bazel.Label{{Label: "@platforms//:incompatible"}}, nil},
bazel.LabelList{[]bazel.Label{}, nil})
if err != nil {
ctx.ModuleErrorf("Error processing platform enabled attribute: %s", err)
}
- data.Append(required)
+ // 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)
+ }
- constraints := constraintAttributes{}
+ if !proptools.Bool(attrs.SkipData) {
+ attrs.Data.Append(required)
+ }
+ // SkipData is not an attribute of any Bazel target
+ // Set this to nil so that it does not appear in the generated build file
+ attrs.SkipData = nil
+
moduleEnableConstraints := bazel.LabelListAttribute{}
moduleEnableConstraints.Append(platformEnabledAttribute)
moduleEnableConstraints.Append(productConfigEnabledAttribute)
- constraints.Target_compatible_with = moduleEnableConstraints
- return constraints
+ return constraintAttributes{Target_compatible_with: moduleEnableConstraints}
}
// Check product variables for `enabled: true` flag override.
// Returns a list of the constraint_value targets who enable this override.
func productVariableConfigEnableLabels(ctx *topDownMutatorContext) []bazel.Label {
- productVariableProps := ProductVariableProperties(ctx)
+ productVariableProps := ProductVariableProperties(ctx, ctx.Module())
productConfigEnablingTargets := []bazel.Label{}
const propName = "Enabled"
if productConfigProps, exists := productVariableProps[propName]; exists {
@@ -1293,30 +1416,30 @@
//
// For example:
//
-// import (
-// "android/soong/android"
-// )
+// import (
+// "android/soong/android"
+// )
//
-// type myModule struct {
-// android.ModuleBase
-// properties struct {
-// MyProperty string
-// }
-// }
+// type myModule struct {
+// android.ModuleBase
+// properties struct {
+// MyProperty string
+// }
+// }
//
-// func NewMyModule() android.Module {
-// m := &myModule{}
-// m.AddProperties(&m.properties)
-// android.InitAndroidModule(m)
-// return m
-// }
+// func NewMyModule() android.Module {
+// m := &myModule{}
+// m.AddProperties(&m.properties)
+// android.InitAndroidModule(m)
+// return m
+// }
//
-// func (m *myModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-// // Get the CPU architecture for the current build variant.
-// variantArch := ctx.Arch()
+// func (m *myModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+// // Get the CPU architecture for the current build variant.
+// variantArch := ctx.Arch()
//
-// // ...
-// }
+// // ...
+// }
type ModuleBase struct {
// Putting the curiously recurring thing pointing to the thing that contains
// the thing pattern to good use.
@@ -1355,7 +1478,6 @@
checkbuildFiles Paths
packagingSpecs []PackagingSpec
packagingSpecsDepSet *packagingSpecsDepSet
- noticeFiles Paths
// katiInstalls tracks the install rules that were created by Soong but are being exported
// to Make to convert to ninja rules so that Make can add additional dependencies.
katiInstalls katiInstalls
@@ -1426,40 +1548,40 @@
}
func (m *ModuleBase) addBp2buildInfo(info bp2buildInfo) {
- m.commonProperties.Bp2buildInfo = append(m.commonProperties.Bp2buildInfo, info)
+ m.commonProperties.BazelConversionStatus.Bp2buildInfo = append(m.commonProperties.BazelConversionStatus.Bp2buildInfo, info)
}
// IsConvertedByBp2build returns whether this module was converted via bp2build.
func (m *ModuleBase) IsConvertedByBp2build() bool {
- return len(m.commonProperties.Bp2buildInfo) > 0
+ return len(m.commonProperties.BazelConversionStatus.Bp2buildInfo) > 0
}
// Bp2buildTargets returns the Bazel targets bp2build generated for this module.
func (m *ModuleBase) Bp2buildTargets() []bp2buildInfo {
- return m.commonProperties.Bp2buildInfo
+ return m.commonProperties.BazelConversionStatus.Bp2buildInfo
}
// AddUnconvertedBp2buildDep stores module name of a dependency that was not converted to Bazel.
func (b *baseModuleContext) AddUnconvertedBp2buildDep(dep string) {
- unconvertedDeps := &b.Module().base().commonProperties.UnconvertedBp2buildDeps
+ unconvertedDeps := &b.Module().base().commonProperties.BazelConversionStatus.UnconvertedDeps
*unconvertedDeps = append(*unconvertedDeps, dep)
}
// AddMissingBp2buildDep stores module name of a dependency that was not found in a Android.bp file.
func (b *baseModuleContext) AddMissingBp2buildDep(dep string) {
- missingDeps := &b.Module().base().commonProperties.MissingBp2buildDeps
+ missingDeps := &b.Module().base().commonProperties.BazelConversionStatus.MissingDeps
*missingDeps = append(*missingDeps, dep)
}
// GetUnconvertedBp2buildDeps returns the list of module names of this module's direct dependencies that
// were not converted to Bazel.
func (m *ModuleBase) GetUnconvertedBp2buildDeps() []string {
- return FirstUniqueStrings(m.commonProperties.UnconvertedBp2buildDeps)
+ return FirstUniqueStrings(m.commonProperties.BazelConversionStatus.UnconvertedDeps)
}
-// GetMissingBp2buildDeps eturns the list of module names that were not found in Android.bp files.
+// GetMissingBp2buildDeps returns the list of module names that were not found in Android.bp files.
func (m *ModuleBase) GetMissingBp2buildDeps() []string {
- return FirstUniqueStrings(m.commonProperties.MissingBp2buildDeps)
+ return FirstUniqueStrings(m.commonProperties.BazelConversionStatus.MissingDeps)
}
func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
@@ -1588,7 +1710,7 @@
// transformSourceToObj, and should only affects unit tests.
vars := m.VariablesForTests()
buildParams := append([]BuildParams(nil), m.buildParams...)
- for i, _ := range buildParams {
+ for i := range buildParams {
newArgs := make(map[string]string)
for k, v := range buildParams[i].Args {
newArgs[k] = v
@@ -1903,6 +2025,10 @@
return m.commonProperties.NamespaceExportedToMake
}
+func (m *ModuleBase) EffectiveLicenseKinds() []string {
+ return m.commonProperties.Effective_license_kinds
+}
+
func (m *ModuleBase) EffectiveLicenseFiles() Paths {
result := make(Paths, 0, len(m.commonProperties.Effective_license_text))
for _, p := range m.commonProperties.Effective_license_text {
@@ -1971,7 +2097,7 @@
}
func (m *ModuleBase) InstallInVendor() bool {
- return Bool(m.commonProperties.Vendor)
+ return Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Soc_specific) || Bool(m.commonProperties.Proprietary)
}
func (m *ModuleBase) InstallInRoot() bool {
@@ -1986,10 +2112,6 @@
return String(m.commonProperties.Owner)
}
-func (m *ModuleBase) NoticeFiles() Paths {
- return m.noticeFiles
-}
-
func (m *ModuleBase) setImageVariation(variant string) {
m.commonProperties.ImageVariation = variant
}
@@ -2237,7 +2359,7 @@
// Some common property checks for properties that will be used later in androidmk.go
checkDistProperties(ctx, "dist", &m.distProperties.Dist)
- for i, _ := range m.distProperties.Dists {
+ for i := range m.distProperties.Dists {
checkDistProperties(ctx, fmt.Sprintf("dists[%d]", i), &m.distProperties.Dists[i])
}
@@ -2249,25 +2371,16 @@
}
})
- m.noticeFiles = make([]Path, 0)
- optPath := OptionalPath{}
- notice := proptools.StringDefault(m.commonProperties.Notice, "")
- if module := SrcIsModule(notice); module != "" {
- optPath = ctx.ExpandOptionalSource(¬ice, "notice")
- } else if notice != "" {
- noticePath := filepath.Join(ctx.ModuleDir(), notice)
- optPath = ExistentPathForSource(ctx, noticePath)
- }
- if optPath.Valid() {
- m.noticeFiles = append(m.noticeFiles, optPath.Path())
- }
-
licensesPropertyFlattener(ctx)
if ctx.Failed() {
return
}
- m.module.GenerateAndroidBuildActions(ctx)
+ if mixedBuildMod, handled := m.isHandledByBazel(ctx); handled {
+ mixedBuildMod.ProcessBazelQueryResponse(ctx)
+ } else {
+ m.module.GenerateAndroidBuildActions(ctx)
+ }
if ctx.Failed() {
return
}
@@ -2323,6 +2436,15 @@
m.variables = ctx.variables
}
+func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) {
+ 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
@@ -2438,7 +2560,7 @@
bazelConversionMode bool
}
-func (b *baseModuleContext) BazelConversionMode() bool {
+func (b *baseModuleContext) isBazelConversionMode() bool {
return b.bazelConversionMode
}
func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
@@ -2827,7 +2949,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 != "" {
@@ -3412,14 +3534,6 @@
return sourceOrOutputDependencyTag{moduleName: moduleName, tag: tag}
}
-// IsSourceDepTag returns true if the supplied blueprint.DependencyTag is one that was used to add
-// dependencies by either ExtractSourceDeps, ExtractSourcesDeps or automatically for properties
-// tagged with `android:"path"`.
-func IsSourceDepTag(depTag blueprint.DependencyTag) bool {
- _, ok := depTag.(sourceOrOutputDependencyTag)
- return ok
-}
-
// IsSourceDepTagWithOutputTag returns true if the supplied blueprint.DependencyTag is one that was
// used to add dependencies by either ExtractSourceDeps, ExtractSourcesDeps or automatically for
// properties tagged with `android:"path"` AND it was added using a module reference of
@@ -3492,10 +3606,29 @@
reportPathError(ctx, err)
return nil
}
+ if len(paths) == 0 {
+ type addMissingDependenciesIntf interface {
+ AddMissingDependencies([]string)
+ OtherModuleName(blueprint.Module) string
+ }
+ if mctx, ok := ctx.(addMissingDependenciesIntf); ok && ctx.Config().AllowMissingDependencies() {
+ mctx.AddMissingDependencies([]string{mctx.OtherModuleName(module)})
+ } else {
+ ReportPathErrorf(ctx, "failed to get output files from module %q", pathContextName(ctx, module))
+ }
+ // Return a fake output file to avoid nil dereferences of Path objects later.
+ // This should never get used for an actual build as the error or missing
+ // dependency has already been reported.
+ p, err := pathForSource(ctx, filepath.Join("missing_output_file", pathContextName(ctx, module)))
+ if err != nil {
+ reportPathError(ctx, err)
+ return nil
+ }
+ return p
+ }
if len(paths) > 1 {
ReportPathErrorf(ctx, "got multiple output files from module %q, expected exactly one",
pathContextName(ctx, module))
- return nil
}
return paths[0]
}
@@ -3507,18 +3640,12 @@
return nil, fmt.Errorf("failed to get output file from module %q: %s",
pathContextName(ctx, module), err.Error())
}
- if len(paths) == 0 {
- return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
- }
return paths, nil
} else if sourceFileProducer, ok := module.(SourceFileProducer); ok {
if tag != "" {
return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag)
}
paths := sourceFileProducer.Srcs()
- if len(paths) == 0 {
- return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
- }
return paths, nil
} else {
return nil, fmt.Errorf("module %q is not an OutputFileProducer", pathContextName(ctx, module))
@@ -3546,14 +3673,14 @@
// be tagged with `android:"path" to support automatic source module dependency resolution.
//
// Deprecated: use PathForModuleSrc instead.
-func (m *moduleContext) ExpandSource(srcFile, prop string) Path {
+func (m *moduleContext) ExpandSource(srcFile, _ string) Path {
return PathForModuleSrc(m, srcFile)
}
// Returns an optional single path expanded from globs and modules referenced using ":module" syntax if
// the srcFile is non-nil. The property must be tagged with `android:"path" to support automatic source module
// dependency resolution.
-func (m *moduleContext) ExpandOptionalSource(srcFile *string, prop string) OptionalPath {
+func (m *moduleContext) ExpandOptionalSource(srcFile *string, _ string) OptionalPath {
if srcFile != nil {
return OptionalPathForPath(PathForModuleSrc(m, *srcFile))
}
@@ -3591,7 +3718,7 @@
// Ensure ancestor directories are in dirMap
// Make directories build their direct subdirectories
// Returns a slice of all directories and a slice of top-level directories.
- dirs := SortedStringKeys(dirMap)
+ dirs := SortedKeys(dirMap)
for _, dir := range dirs {
dir := parentDir(dir)
for dir != "." && dir != "/" {
@@ -3602,7 +3729,7 @@
dir = parentDir(dir)
}
}
- dirs = SortedStringKeys(dirMap)
+ dirs = SortedKeys(dirMap)
var topDirs []string
for _, dir := range dirs {
p := parentDir(dir)
@@ -3612,7 +3739,7 @@
topDirs = append(topDirs, dir)
}
}
- return SortedStringKeys(dirMap), topDirs
+ return SortedKeys(dirMap), topDirs
}
func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) {
@@ -3698,7 +3825,7 @@
}
// Wrap those into host|host-cross|target phony rules
- for _, class := range SortedStringKeys(osClass) {
+ for _, class := range SortedKeys(osClass) {
ctx.Phony(class, osClass[class]...)
}
}
diff --git a/android/module_test.go b/android/module_test.go
index 77ef146..1ca7422 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -15,6 +15,7 @@
package android
import (
+ "github.com/google/blueprint"
"path/filepath"
"runtime"
"testing"
@@ -911,3 +912,155 @@
})
}
}
+
+func TestSetAndroidMkEntriesWithTestOptions(t *testing.T) {
+ tests := []struct {
+ name string
+ testOptions CommonTestOptions
+ expected map[string][]string
+ }{
+ {
+ name: "empty",
+ testOptions: CommonTestOptions{},
+ expected: map[string][]string{},
+ },
+ {
+ name: "is unit test",
+ testOptions: CommonTestOptions{
+ Unit_test: boolPtr(true),
+ },
+ expected: map[string][]string{
+ "LOCAL_IS_UNIT_TEST": []string{"true"},
+ },
+ },
+ {
+ name: "is not unit test",
+ testOptions: CommonTestOptions{
+ Unit_test: boolPtr(false),
+ },
+ expected: map[string][]string{},
+ },
+ {
+ name: "empty tag",
+ testOptions: CommonTestOptions{
+ Tags: []string{},
+ },
+ expected: map[string][]string{},
+ },
+ {
+ name: "single tag",
+ testOptions: CommonTestOptions{
+ Tags: []string{"tag1"},
+ },
+ expected: map[string][]string{
+ "LOCAL_TEST_OPTIONS_TAGS": []string{"tag1"},
+ },
+ },
+ {
+ name: "multiple tag",
+ testOptions: CommonTestOptions{
+ Tags: []string{"tag1", "tag2", "tag3"},
+ },
+ expected: map[string][]string{
+ "LOCAL_TEST_OPTIONS_TAGS": []string{"tag1", "tag2", "tag3"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ actualEntries := AndroidMkEntries{
+ EntryMap: map[string][]string{},
+ }
+ tt.testOptions.SetAndroidMkEntries(&actualEntries)
+ actual := actualEntries.EntryMap
+ t.Logf("actual: %v", actual)
+ t.Logf("expected: %v", tt.expected)
+ AssertDeepEquals(t, "TestProcessCommonTestOptions ", tt.expected, actual)
+ })
+ }
+}
+
+type fakeBlueprintModule struct{}
+
+func (fakeBlueprintModule) Name() string { return "foo" }
+
+func (fakeBlueprintModule) GenerateBuildActions(blueprint.ModuleContext) {}
+
+type sourceProducerTestModule struct {
+ fakeBlueprintModule
+ source Path
+}
+
+func (s sourceProducerTestModule) Srcs() Paths { return Paths{s.source} }
+
+type outputFileProducerTestModule struct {
+ fakeBlueprintModule
+ output map[string]Path
+ error map[string]error
+}
+
+func (o outputFileProducerTestModule) OutputFiles(tag string) (Paths, error) {
+ return PathsIfNonNil(o.output[tag]), o.error[tag]
+}
+
+type pathContextAddMissingDependenciesWrapper struct {
+ PathContext
+ missingDeps []string
+}
+
+func (p *pathContextAddMissingDependenciesWrapper) AddMissingDependencies(deps []string) {
+ p.missingDeps = append(p.missingDeps, deps...)
+}
+func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module blueprint.Module) string {
+ return module.Name()
+}
+
+func TestOutputFileForModule(t *testing.T) {
+ testcases := []struct {
+ name string
+ module blueprint.Module
+ tag string
+ env map[string]string
+ config func(*config)
+ expected string
+ missingDeps []string
+ }{
+ {
+ name: "SourceFileProducer",
+ module: &sourceProducerTestModule{source: PathForTesting("foo.txt")},
+ expected: "foo.txt",
+ },
+ {
+ name: "OutputFileProducer",
+ module: &outputFileProducerTestModule{output: map[string]Path{"": PathForTesting("foo.txt")}},
+ expected: "foo.txt",
+ },
+ {
+ name: "OutputFileProducer_tag",
+ module: &outputFileProducerTestModule{output: map[string]Path{"foo": PathForTesting("foo.txt")}},
+ tag: "foo",
+ expected: "foo.txt",
+ },
+ {
+ name: "OutputFileProducer_AllowMissingDependencies",
+ config: func(config *config) {
+ config.TestProductVariables.Allow_missing_dependencies = boolPtr(true)
+ },
+ module: &outputFileProducerTestModule{},
+ missingDeps: []string{"foo"},
+ expected: "missing_output_file/foo",
+ },
+ }
+ for _, tt := range testcases {
+ config := TestConfig(buildDir, tt.env, "", nil)
+ if tt.config != nil {
+ tt.config(config.config)
+ }
+ ctx := &pathContextAddMissingDependenciesWrapper{
+ PathContext: PathContextForTesting(config),
+ }
+ got := OutputFileForModule(ctx, tt.module, tt.tag)
+ AssertPathRelativeToTopEquals(t, "expected source path", tt.expected, got)
+ AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
+ }
+}
diff --git a/android/mutator.go b/android/mutator.go
index 739e4ee..4ec9604 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:
@@ -34,22 +31,33 @@
// RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
+ bp2buildMutators := append(preArchMutators, registerBp2buildConversionMutator)
+ registerMutatorsForBazelConversion(ctx, bp2buildMutators)
+}
+
+// RegisterMutatorsForApiBazelConversion is an alternate registration pipeline for api_bp2build
+// This pipeline restricts generation of Bazel targets to Soong modules that contribute APIs
+func RegisterMutatorsForApiBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
+ bp2buildMutators := append(preArchMutators, registerApiBp2buildConversionMutator)
+ registerMutatorsForBazelConversion(ctx, bp2buildMutators)
+}
+
+func registerMutatorsForBazelConversion(ctx *Context, bp2buildMutators []RegisterMutatorFunc) {
mctx := ®isterMutatorsContext{
bazelConversionMode: true,
}
- bp2buildMutators := append([]RegisterMutatorFunc{
+ allMutators := append([]RegisterMutatorFunc{
RegisterNamespaceMutator,
RegisterDefaultsPreArchMutators,
// TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should
// evaluate the impact on conversion.
RegisterPrebuiltsPreArchMutators,
},
- preArchMutators...)
- bp2buildMutators = append(bp2buildMutators, registerBp2buildConversionMutator)
+ bp2buildMutators...)
// Register bp2build mutators
- for _, f := range bp2buildMutators {
+ for _, f := range allMutators {
f(mctx)
}
@@ -96,6 +104,7 @@
TopDown(name string, m TopDownMutator) MutatorHandle
BottomUp(name string, m BottomUpMutator) MutatorHandle
BottomUpBlueprint(name string, m blueprint.BottomUpMutator) MutatorHandle
+ Transition(name string, m TransitionMutator)
}
type RegisterMutatorFunc func(RegisterMutatorsContext)
@@ -235,9 +244,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)
@@ -262,6 +268,11 @@
// platforms, as dictated by a given bool attribute: the target will not be buildable in
// any platform for which this bool attribute is false.
CreateBazelTargetModuleWithRestrictions(bazel.BazelTargetModuleProperties, CommonAttributes, interface{}, bazel.BoolAttribute)
+
+ // CreateBazelTargetAliasInDir creates an alias definition in `dir` directory.
+ // This function can be used to create alias definitions in a directory that is different
+ // from the directory of the visited Soong module.
+ CreateBazelTargetAliasInDir(dir string, name string, actual bazel.Label)
}
type topDownMutatorContext struct {
@@ -403,7 +414,7 @@
return &bottomUpMutatorContext{
bp: ctx,
- baseModuleContext: a.base().baseModuleContextFactory(ctx),
+ baseModuleContext: moduleContext,
finalPhase: finalPhase,
}
}
@@ -427,6 +438,182 @@
return mutator
}
+type IncomingTransitionContext interface {
+ // Module returns the target of the dependency edge for which the transition
+ // is being computed
+ Module() Module
+
+ // Config returns the configuration for the build.
+ Config() Config
+}
+
+type OutgoingTransitionContext interface {
+ // Module returns the target of the dependency edge for which the transition
+ // is being computed
+ Module() Module
+
+ // DepTag() Returns the dependency tag through which this dependency is
+ // reached
+ DepTag() blueprint.DependencyTag
+}
+
+// Transition mutators implement a top-down mechanism where a module tells its
+// direct dependencies what variation they should be built in but the dependency
+// has the final say.
+//
+// When implementing a transition mutator, one needs to implement four methods:
+// - Split() that tells what variations a module has by itself
+// - OutgoingTransition() where a module tells what it wants from its
+// dependency
+// - IncomingTransition() where a module has the final say about its own
+// variation
+// - Mutate() that changes the state of a module depending on its variation
+//
+// That the effective variation of module B when depended on by module A is the
+// composition the outgoing transition of module A and the incoming transition
+// of module B.
+//
+// the outgoing transition should not take the properties of the dependency into
+// account, only those of the module that depends on it. For this reason, the
+// dependency is not even passed into it as an argument. Likewise, the incoming
+// transition should not take the properties of the depending module into
+// account and is thus not informed about it. This makes for a nice
+// decomposition of the decision logic.
+//
+// A given transition mutator only affects its own variation; other variations
+// stay unchanged along the dependency edges.
+//
+// Soong makes sure that all modules are created in the desired variations and
+// that dependency edges are set up correctly. This ensures that "missing
+// variation" errors do not happen and allows for more flexible changes in the
+// value of the variation among dependency edges (as oppposed to bottom-up
+// mutators where if module A in variation X depends on module B and module B
+// has that variation X, A must depend on variation X of B)
+//
+// The limited power of the context objects passed to individual mutators
+// methods also makes it more difficult to shoot oneself in the foot. Complete
+// safety is not guaranteed because no one prevents individual transition
+// mutators from mutating modules in illegal ways and for e.g. Split() or
+// Mutate() to run their own visitations of the transitive dependency of the
+// module and both of these are bad ideas, but it's better than no guardrails at
+// all.
+//
+// This model is pretty close to Bazel's configuration transitions. The mapping
+// between concepts in Soong and Bazel is as follows:
+// - Module == configured target
+// - Variant == configuration
+// - Variation name == configuration flag
+// - Variation == configuration flag value
+// - Outgoing transition == attribute transition
+// - Incoming transition == rule transition
+//
+// The Split() method does not have a Bazel equivalent and Bazel split
+// transitions do not have a Soong equivalent.
+//
+// Mutate() does not make sense in Bazel due to the different models of the
+// two systems: when creating new variations, Soong clones the old module and
+// thus some way is needed to change it state whereas Bazel creates each
+// configuration of a given configured target anew.
+type TransitionMutator interface {
+ // Split returns the set of variations that should be created for a module no
+ // matter who depends on it. Used when Make depends on a particular variation
+ // or when the module knows its variations just based on information given to
+ // it in the Blueprint file. This method should not mutate the module it is
+ // called on.
+ Split(ctx BaseModuleContext) []string
+
+ // Called on a module to determine which variation it wants from its direct
+ // dependencies. The dependency itself can override this decision. This method
+ // should not mutate the module itself.
+ OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string
+
+ // Called on a module to determine which variation it should be in based on
+ // the variation modules that depend on it want. This gives the module a final
+ // say about its own variations. This method should not mutate the module
+ // itself.
+ IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string
+
+ // Called after a module was split into multiple variations on each variation.
+ // It should not split the module any further but adding new dependencies is
+ // fine. Unlike all the other methods on TransitionMutator, this method is
+ // allowed to mutate the module.
+ Mutate(ctx BottomUpMutatorContext, variation string)
+}
+
+type androidTransitionMutator struct {
+ finalPhase bool
+ bazelConversionMode bool
+ mutator TransitionMutator
+}
+
+func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string {
+ if m, ok := ctx.Module().(Module); ok {
+ moduleContext := m.base().baseModuleContextFactory(ctx)
+ moduleContext.bazelConversionMode = a.bazelConversionMode
+ return a.mutator.Split(&moduleContext)
+ } else {
+ return []string{""}
+ }
+}
+
+type outgoingTransitionContextImpl struct {
+ bp blueprint.OutgoingTransitionContext
+}
+
+func (c *outgoingTransitionContextImpl) Module() Module {
+ return c.bp.Module().(Module)
+}
+
+func (c *outgoingTransitionContextImpl) DepTag() blueprint.DependencyTag {
+ return c.bp.DepTag()
+}
+
+func (a *androidTransitionMutator) OutgoingTransition(ctx blueprint.OutgoingTransitionContext, sourceVariation string) string {
+ if _, ok := ctx.Module().(Module); ok {
+ return a.mutator.OutgoingTransition(&outgoingTransitionContextImpl{bp: ctx}, sourceVariation)
+ } else {
+ return ""
+ }
+}
+
+type incomingTransitionContextImpl struct {
+ bp blueprint.IncomingTransitionContext
+}
+
+func (c *incomingTransitionContextImpl) Module() Module {
+ return c.bp.Module().(Module)
+}
+
+func (c *incomingTransitionContextImpl) Config() Config {
+ return c.bp.Config().(Config)
+}
+
+func (a *androidTransitionMutator) IncomingTransition(ctx blueprint.IncomingTransitionContext, incomingVariation string) string {
+ if _, ok := ctx.Module().(Module); ok {
+ return a.mutator.IncomingTransition(&incomingTransitionContextImpl{bp: ctx}, incomingVariation)
+ } else {
+ return ""
+ }
+}
+
+func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) {
+ if am, ok := ctx.Module().(Module); ok {
+ a.mutator.Mutate(bottomUpMutatorContextFactory(ctx, am, a.finalPhase, a.bazelConversionMode), variation)
+ }
+}
+
+func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) {
+ atm := &androidTransitionMutator{
+ finalPhase: x.finalPhase,
+ bazelConversionMode: x.bazelConversionMode,
+ mutator: m,
+ }
+ mutator := &mutator{
+ name: name,
+ transitionMutator: atm}
+ x.mutators = append(x.mutators, mutator)
+}
+
func (x *registerMutatorsContext) mutatorName(name string) string {
if x.bazelConversionMode {
return name + "_bp2build"
@@ -462,6 +649,8 @@
handle = blueprintCtx.RegisterBottomUpMutator(mutator.name, mutator.bottomUpMutator)
} else if mutator.topDownMutator != nil {
handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator)
+ } else if mutator.transitionMutator != nil {
+ blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
}
if mutator.parallel {
handle.Parallel()
@@ -521,6 +710,67 @@
t.createBazelTargetModule(bazelProps, commonAttrs, attrs, enabledProperty)
}
+var (
+ bazelAliasModuleProperties = bazel.BazelTargetModuleProperties{
+ Rule_class: "alias",
+ }
+)
+
+type bazelAliasAttributes struct {
+ Actual *bazel.LabelAttribute
+}
+
+func (t *topDownMutatorContext) CreateBazelTargetAliasInDir(
+ dir string,
+ name string,
+ actual bazel.Label) {
+ mod := t.Module()
+ attrs := &bazelAliasAttributes{
+ Actual: bazel.MakeLabelAttribute(actual.Label),
+ }
+ info := bp2buildInfo{
+ Dir: dir,
+ BazelProps: bazelAliasModuleProperties,
+ CommonAttrs: CommonAttributes{Name: name},
+ ConstraintAttrs: constraintAttributes{},
+ Attrs: attrs,
+ }
+ mod.base().addBp2buildInfo(info)
+}
+
+// ApexAvailableTags converts the apex_available property value of an ApexModule
+// module and returns it as a list of keyed tags.
+func ApexAvailableTags(mod Module) bazel.StringListAttribute {
+ attr := bazel.StringListAttribute{}
+ // Transform specific attributes into tags.
+ if am, ok := mod.(ApexModule); ok {
+ // TODO(b/218841706): hidl_interface has the apex_available prop, but it's
+ // defined directly as a prop and not via ApexModule, so this doesn't
+ // pick those props up.
+ apexAvailable := am.apexModuleBase().ApexAvailable()
+ // If a user does not specify apex_available in Android.bp, then soong provides a default.
+ // To avoid verbosity of BUILD files, remove this default from user-facing BUILD files.
+ if len(am.apexModuleBase().ApexProperties.Apex_available) == 0 {
+ apexAvailable = []string{}
+ }
+ attr.Value = ConvertApexAvailableToTags(apexAvailable)
+ }
+ return attr
+}
+
+func ConvertApexAvailableToTags(apexAvailable []string) []string {
+ if len(apexAvailable) == 0 {
+ // We need nil specifically to make bp2build not add the tags property at all,
+ // instead of adding it with an empty list
+ return nil
+ }
+ result := make([]string, 0, len(apexAvailable))
+ for _, a := range apexAvailable {
+ result = append(result, "apex_available="+a)
+ }
+ return result
+}
+
func (t *topDownMutatorContext) createBazelTargetModule(
bazelProps bazel.BazelTargetModuleProperties,
commonAttrs CommonAttributes,
@@ -553,29 +803,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 +879,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/mutator_test.go b/android/mutator_test.go
index 21eebd2..dbdfa33 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "reflect"
"strings"
"testing"
@@ -267,3 +268,22 @@
FixtureWithRootAndroidBp(`test {name: "foo"}`),
).RunTest(t)
}
+
+func TestConvertApexAvailableToTags(t *testing.T) {
+ input := []string{
+ "com.android.adbd",
+ "//apex_available:platform",
+ }
+ actual := ConvertApexAvailableToTags(input)
+ expected := []string{
+ "apex_available=com.android.adbd",
+ "apex_available=//apex_available:platform",
+ }
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("Expected: %v, actual: %v", expected, actual)
+ }
+
+ if ConvertApexAvailableToTags(nil) != nil {
+ t.Errorf("Expected providing nil to return nil")
+ }
+}
diff --git a/android/namespace.go b/android/namespace.go
index 1f821ff..ebf85a1 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -186,10 +186,10 @@
return namespace
}
-// A NamelessModule can never be looked up by name. It must still implement Name(), but the return
-// value doesn't have to be unique.
-type NamelessModule interface {
- Nameless()
+// A NamespacelessModule can never be looked up by name. It must still implement Name(), and the name
+// still has to be unique.
+type NamespacelessModule interface {
+ Namespaceless()
}
func (r *NameResolver) NewModule(ctx blueprint.NamespaceContext, moduleGroup blueprint.ModuleGroup, module blueprint.Module) (namespace blueprint.Namespace, errs []error) {
@@ -203,7 +203,7 @@
return nil, nil
}
- if _, ok := module.(NamelessModule); ok {
+ if _, ok := module.(NamespacelessModule); ok {
return nil, nil
}
@@ -225,6 +225,10 @@
return ns, nil
}
+func (r *NameResolver) NewSkippedModule(ctx blueprint.NamespaceContext, name string, skipInfo blueprint.SkippedModuleInfo) {
+ r.rootNamespace.moduleContainer.NewSkippedModule(ctx, name, skipInfo)
+}
+
func (r *NameResolver) AllModules() []blueprint.ModuleGroup {
childLists := [][]blueprint.ModuleGroup{}
totalCount := 0
@@ -241,6 +245,10 @@
return allModules
}
+func (r *NameResolver) SkippedModuleFromName(moduleName string, namespace blueprint.Namespace) (skipInfos []blueprint.SkippedModuleInfo, skipped bool) {
+ return r.rootNamespace.moduleContainer.SkippedModuleFromName(moduleName, namespace)
+}
+
// parses a fully-qualified path (like "//namespace_path:module_name") into a namespace name and a
// module name
func (r *NameResolver) parseFullyQualifiedName(name string) (namespaceName string, moduleName string, ok bool) {
@@ -300,7 +308,7 @@
for _, name := range namespace.importedNamespaceNames {
imp, ok := r.namespaceAt(name)
if !ok {
- return fmt.Errorf("namespace %v does not exist", name)
+ return fmt.Errorf("namespace %v does not exist; Some necessary modules may have been skipped by Soong. Check if PRODUCT_SOURCE_ROOT_DIRS is pruning necessary Android.bp files.", name)
}
namespace.visibleNamespaces = append(namespace.visibleNamespaces, imp)
}
@@ -317,8 +325,8 @@
namespace.id = strconv.Itoa(id)
}
-func (r *NameResolver) MissingDependencyError(depender string, dependerNamespace blueprint.Namespace, depName string) (err error) {
- text := fmt.Sprintf("%q depends on undefined module %q", depender, depName)
+func (r *NameResolver) MissingDependencyError(depender string, dependerNamespace blueprint.Namespace, depName string, guess []string) (err error) {
+ text := fmt.Sprintf("%q depends on undefined module %q.", depender, depName)
_, _, isAbs := r.parseFullyQualifiedName(depName)
if isAbs {
@@ -329,12 +337,18 @@
// determine which namespaces the module can be found in
foundInNamespaces := []string{}
+ skippedDepErrors := []error{}
for _, namespace := range r.sortedNamespaces.sortedItems() {
_, found := namespace.moduleContainer.ModuleFromName(depName, nil)
if found {
foundInNamespaces = append(foundInNamespaces, namespace.Path)
}
+ _, skipped := namespace.moduleContainer.SkippedModuleFromName(depName, nil)
+ if skipped {
+ skippedDepErrors = append(skippedDepErrors, namespace.moduleContainer.MissingDependencyError(depender, dependerNamespace, depName, nil))
+ }
}
+
if len(foundInNamespaces) > 0 {
// determine which namespaces are visible to dependerNamespace
dependerNs := dependerNamespace.(*Namespace)
@@ -346,6 +360,13 @@
text += fmt.Sprintf("\nModule %q is defined in namespace %q which can read these %v namespaces: %q", depender, dependerNs.Path, len(importedNames), importedNames)
text += fmt.Sprintf("\nModule %q can be found in these namespaces: %q", depName, foundInNamespaces)
}
+ for _, err := range skippedDepErrors {
+ text += fmt.Sprintf("\n%s", err.Error())
+ }
+
+ if len(guess) > 0 {
+ text += fmt.Sprintf("\nOr did you mean %q?", guess)
+ }
return fmt.Errorf(text)
}
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 7a387a0..ea51c6e 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -174,9 +174,10 @@
`,
}),
).
- ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir3/Android.bp:4:5: "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"]\E`)).
+Module "a" can be found in these namespaces: ["dir1" "dir2"]\E
+Or did you mean ["soong_namespace"]?`)).
RunTest(t)
}
@@ -421,9 +422,10 @@
`,
}),
).
- ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/subdir1/Android.bp:4:5: "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"]\E`)).
+Module "a" can be found in these namespaces: ["dir1"]\E
+Or did you mean ["soong_namespace"]?`)).
RunTest(t)
}
@@ -481,9 +483,10 @@
`,
}),
).
- ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir3/Android.bp:5:5: "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"]\E`)).
+Module "a" can be found in these namespaces: ["dir1"]\E
+Or did you mean ["b"]?`)).
RunTest(t)
}
diff --git a/android/neverallow.go b/android/neverallow.go
index 5f069b8..2139c3c 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -58,6 +58,8 @@
AddNeverAllowRules(createMakefileGoalRules()...)
AddNeverAllowRules(createInitFirstStageRules()...)
AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
+ AddNeverAllowRules(createBp2BuildRule())
+ AddNeverAllowRules(createCcStubsRule())
}
// Add a NeverAllow rule to the set of rules to apply.
@@ -65,8 +67,16 @@
neverallows = append(neverallows, rules...)
}
-func createIncludeDirsRules() []Rule {
- notInIncludeDir := []string{
+func createBp2BuildRule() Rule {
+ return NeverAllow().
+ With("bazel_module.bp2build_available", "true").
+ NotIn("soong_tests"). // only used in tests
+ Because("setting bp2build_available in Android.bp is not " +
+ "supported for custom conversion, use allowlists.go instead.")
+}
+
+var (
+ neverallowNotInIncludeDir = []string{
"art",
"art/libnativebridge",
"art/libnativeloader",
@@ -82,7 +92,7 @@
"external/vixl",
"external/wycheproof",
}
- noUseIncludeDir := []string{
+ neverallowNoUseIncludeDir = []string{
"frameworks/av/apex",
"frameworks/av/tools",
"frameworks/native/cmds",
@@ -94,10 +104,12 @@
"system/libfmq",
"system/libvintf",
}
+)
- rules := make([]Rule, 0, len(notInIncludeDir)+len(noUseIncludeDir))
+func createIncludeDirsRules() []Rule {
+ rules := make([]Rule, 0, len(neverallowNotInIncludeDir)+len(neverallowNoUseIncludeDir))
- for _, path := range notInIncludeDir {
+ for _, path := range neverallowNotInIncludeDir {
rule :=
NeverAllow().
WithMatcher("include_dirs", StartsWith(path+"/")).
@@ -107,7 +119,7 @@
rules = append(rules, rule)
}
- for _, path := range noUseIncludeDir {
+ for _, path := range neverallowNoUseIncludeDir {
rule := NeverAllow().In(path+"/").WithMatcher("include_dirs", isSetMatcherInstance).
Because("include_dirs is deprecated, all usages of them in '" + path + "' have been migrated" +
" to use alternate mechanisms and so can no longer be used.")
@@ -153,6 +165,7 @@
javaDeviceForHostProjectsAllowedList := []string{
"development/build",
"external/guava",
+ "external/kotlinx.coroutines",
"external/robolectric-shadows",
"external/robolectric",
"frameworks/layoutlib",
@@ -203,6 +216,17 @@
}
}
+func createCcStubsRule() Rule {
+ ccStubsImplementationInstallableProjectsAllowedList := []string{
+ "packages/modules/Virtualization/vm_payload",
+ }
+
+ return NeverAllow().
+ NotIn(ccStubsImplementationInstallableProjectsAllowedList...).
+ WithMatcher("stubs.implementation_installable", isSetMatcherInstance).
+ Because("implementation_installable can only be used in allowed projects.")
+}
+
func createUncompressDexRules() []Rule {
return []Rule{
NeverAllow().
@@ -213,11 +237,16 @@
}
func createMakefileGoalRules() []Rule {
+ allowlist := []string{
+ // libwifi_hal uses makefile_goal for its dependencies
+ "frameworks/opt/net/wifi/libwifi_hal",
+ }
return []Rule{
NeverAllow().
ModuleType("makefile_goal").
WithoutMatcher("product_out_path", Regexp("^boot[0-9a-zA-Z.-]*[.]img$")).
- Because("Only boot images may be imported as a makefile goal."),
+ NotIn(allowlist...).
+ Because("Only boot images may be imported as a makefile goal if not in allowed projects"),
}
}
@@ -514,7 +543,7 @@
s = append(s, fmt.Sprintf("properties matching: %s", r.props))
}
if len(r.directDeps) > 0 {
- s = append(s, fmt.Sprintf("dep(s): %q", SortedStringKeys(r.directDeps)))
+ s = append(s, fmt.Sprintf("dep(s): %q", SortedKeys(r.directDeps)))
}
if len(r.osClasses) > 0 {
s = append(s, fmt.Sprintf("os class(es): %q", r.osClasses))
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 86f1a37..5f5f9a1 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -324,7 +324,32 @@
`),
},
expectedErrors: []string{
- "Only boot images may be imported as a makefile goal.",
+ "Only boot images.* may be imported as a makefile goal",
+ },
+ },
+ {
+ name: "disallowed makefile_goal outside external",
+ fs: map[string][]byte{
+ "project/Android.bp": []byte(`
+ makefile_goal {
+ name: "foo",
+ product_out_path: "obj/EXE/foo",
+ }
+ `),
+ },
+ expectedErrors: []string{
+ "not in allowed projects",
+ },
+ },
+ {
+ name: "allow makefile_goal within external",
+ fs: map[string][]byte{
+ "frameworks/opt/net/wifi/libwifi_hal/Android.bp": []byte(`
+ makefile_goal {
+ name: "foo",
+ product_out_path: "obj/EXE/foo",
+ }
+ `),
},
},
// Tests for the rule prohibiting the use of framework
@@ -342,6 +367,22 @@
"framework can't be used when building against SDK",
},
},
+ // Test for the rule restricting use of implementation_installable
+ {
+ name: `"implementation_installable" outside allowed list`,
+ fs: map[string][]byte{
+ "Android.bp": []byte(`
+ cc_library {
+ name: "outside_allowed_list",
+ stubs: {
+ implementation_installable: true,
+ },
+ }`),
+ },
+ expectedErrors: []string{
+ `module "outside_allowed_list": violates neverallow`,
+ },
+ },
}
var prepareForNeverAllowTest = GroupFixturePreparers(
@@ -394,6 +435,10 @@
Platform struct {
Shared_libs []string
}
+
+ Stubs struct {
+ Implementation_installable *bool
+ }
}
type mockCcLibraryModule struct {
diff --git a/android/ninja_deps_test.go b/android/ninja_deps_test.go
index 947c257..d6afcc0 100644
--- a/android/ninja_deps_test.go
+++ b/android/ninja_deps_test.go
@@ -18,21 +18,6 @@
"testing"
)
-func init() {
- // This variable uses ExistentPathForSource on a PackageVarContext, which is a PathContext
- // that is not a PathGlobContext. That requires the deps to be stored in the Config.
- pctx.VariableFunc("test_ninja_deps_variable", func(ctx PackageVarContext) string {
- // Using ExistentPathForSource to look for a file that does not exist in a directory that
- // does exist (test_ninja_deps) from a PackageVarContext adds a dependency from build.ninja
- // to the directory.
- if ExistentPathForSource(ctx, "test_ninja_deps/does_not_exist").Valid() {
- return "true"
- } else {
- return "false"
- }
- })
-}
-
func testNinjaDepsSingletonFactory() Singleton {
return testNinjaDepsSingleton{}
}
@@ -40,33 +25,19 @@
type testNinjaDepsSingleton struct{}
func (testNinjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) {
- // Reference the test_ninja_deps_variable in a build statement so Blueprint is forced to
- // evaluate it.
- ctx.Build(pctx, BuildParams{
- Rule: Cp,
- Input: PathForTesting("foo"),
- Output: PathForOutput(ctx, "test_ninja_deps_out"),
- Args: map[string]string{
- "cpFlags": "${test_ninja_deps_variable}",
- },
- })
+ ctx.Config().addNinjaFileDeps("foo")
}
func TestNinjaDeps(t *testing.T) {
- fs := MockFS{
- "test_ninja_deps/exists": nil,
- }
-
result := GroupFixturePreparers(
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterSingletonType("test_ninja_deps_singleton", testNinjaDepsSingletonFactory)
ctx.RegisterSingletonType("ninja_deps_singleton", ninjaDepsSingletonFactory)
}),
- fs.AddToFixture(),
).RunTest(t)
// Verify that the ninja file has a dependency on the test_ninja_deps directory.
- if g, w := result.NinjaDeps, "test_ninja_deps"; !InList(w, g) {
+ if g, w := result.NinjaDeps, "foo"; !InList(w, g) {
t.Errorf("expected %q in %q", w, g)
}
}
diff --git a/android/onceper.go b/android/onceper.go
index 481cdea..fa415d1 100644
--- a/android/onceper.go
+++ b/android/onceper.go
@@ -79,6 +79,17 @@
return once.maybeWaitFor(key, v)
}
+// Peek returns the value previously computed with Once for a given key. If Once has not
+// been called for the given key Peek will return ok == false.
+func (once *OncePer) Peek(key OnceKey) (interface{}, bool) {
+ v, ok := once.values.Load(key)
+ if !ok {
+ return nil, false
+ }
+
+ return once.maybeWaitFor(key, v), true
+}
+
// OnceStringSlice is the same as Once, but returns the value cast to a []string
func (once *OncePer) OnceStringSlice(key OnceKey, value func() []string) []string {
return once.Once(key, func() interface{} { return value() }).([]string)
diff --git a/android/override_module.go b/android/override_module.go
index 51e74d4..2d30a85 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -28,6 +28,7 @@
// module based on it.
import (
+ "fmt"
"sort"
"sync"
@@ -48,6 +49,10 @@
// i.e. cases where an overriding module, too, is overridden by a prebuilt module.
setOverriddenByPrebuilt(overridden bool)
getOverriddenByPrebuilt() bool
+
+ // Directory containing the Blueprint definition of the overriding module
+ setModuleDir(string)
+ ModuleDir() string
}
// Base module struct for override module types
@@ -57,6 +62,8 @@
overridingProperties []interface{}
overriddenByPrebuilt bool
+
+ moduleDir string
}
type OverrideModuleProperties struct {
@@ -66,6 +73,14 @@
// TODO(jungjw): Add an optional override_name bool flag.
}
+func (o *OverrideModuleBase) setModuleDir(d string) {
+ o.moduleDir = d
+}
+
+func (o *OverrideModuleBase) ModuleDir() string {
+ return o.moduleDir
+}
+
func (o *OverrideModuleBase) getOverridingProperties() []interface{} {
return o.overridingProperties
}
@@ -108,6 +123,7 @@
override(ctx BaseModuleContext, o OverrideModule)
GetOverriddenBy() string
+ GetOverriddenByModuleDir() string
setOverridesProperty(overridesProperties *[]string)
@@ -117,7 +133,8 @@
}
type overridableModuleProperties struct {
- OverriddenBy string `blueprint:"mutated"`
+ OverriddenBy string `blueprint:"mutated"`
+ OverriddenByModuleDir string `blueprint:"mutated"`
}
// Base module struct for overridable module types
@@ -196,6 +213,7 @@
*b.overridesProperty = append(*b.overridesProperty, ctx.ModuleName())
}
b.overridableModuleProperties.OverriddenBy = o.Name()
+ b.overridableModuleProperties.OverriddenByModuleDir = o.ModuleDir()
}
// GetOverriddenBy returns the name of the override module that has overridden this module.
@@ -206,6 +224,10 @@
return b.overridableModuleProperties.OverriddenBy
}
+func (b *OverridableModuleBase) GetOverriddenByModuleDir() string {
+ return b.overridableModuleProperties.OverriddenByModuleDir
+}
+
func (b *OverridableModuleBase) OverridablePropertiesDepsMutator(ctx BottomUpMutatorContext) {
}
@@ -254,7 +276,9 @@
})
baseModule := ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)[0]
if o, ok := baseModule.(OverridableModule); ok {
- o.addOverride(ctx.Module().(OverrideModule))
+ overrideModule := ctx.Module().(OverrideModule)
+ overrideModule.setModuleDir(ctx.ModuleDir())
+ o.addOverride(overrideModule)
}
}
}
@@ -314,11 +338,35 @@
// ModuleNameWithPossibleOverride returns the name of the OverrideModule that overrides the current
// variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule
// or if this variant is not overridden.
-func ModuleNameWithPossibleOverride(ctx ModuleContext) string {
+func ModuleNameWithPossibleOverride(ctx BazelConversionContext) string {
if overridable, ok := ctx.Module().(OverridableModule); ok {
if o := overridable.GetOverriddenBy(); o != "" {
return o
}
}
- return ctx.ModuleName()
+ return ctx.OtherModuleName(ctx.Module())
+}
+
+// ModuleDirWithPossibleOverride returns the dir of the OverrideModule that overrides the current
+// variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule
+// or if this variant is not overridden.
+func moduleDirWithPossibleOverride(ctx BazelConversionContext) string {
+ if overridable, ok := ctx.Module().(OverridableModule); ok {
+ if o := overridable.GetOverriddenByModuleDir(); o != "" {
+ return o
+ }
+ }
+ return ctx.OtherModuleDir(ctx.Module())
+}
+
+// MaybeBp2buildLabelOfOverridingModule returns the bazel label of the
+// overriding module of an OverridableModule (e.g. override_apex label of a base
+// apex), or the module's label itself if not overridden.
+func MaybeBp2buildLabelOfOverridingModule(ctx BazelConversionContext) string {
+ moduleName := ModuleNameWithPossibleOverride(ctx)
+ moduleDir := moduleDirWithPossibleOverride(ctx)
+ if moduleDir == Bp2BuildTopLevel {
+ moduleDir = ""
+ }
+ return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
}
diff --git a/android/package.go b/android/package.go
index 878e4c4..7fbc700 100644
--- a/android/package.go
+++ b/android/package.go
@@ -15,6 +15,9 @@
package android
import (
+ "path/filepath"
+
+ "android/soong/bazel"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -37,12 +40,52 @@
Default_applicable_licenses []string
}
+type bazelPackageAttributes struct {
+ Default_visibility []string
+ Default_package_metadata bazel.LabelListAttribute
+}
+
type packageModule struct {
ModuleBase
+ BazelModuleBase
properties packageProperties
}
+var _ Bazelable = &packageModule{}
+
+func (p *packageModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+ defaultPackageMetadata := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, p.properties.Default_applicable_licenses))
+ // If METADATA file exists in the package, add it to package(default_package_metadata=) using a
+ // filegroup(name="default_metadata_file") which can be accessed later on each module in Bazel
+ // using attribute "applicable_licenses".
+ // Attribute applicable_licenses of filegroup "default_metadata_file" has to be set to [],
+ // otherwise Bazel reports cyclic reference error.
+ if existed, _, _ := ctx.Config().fs.Exists(filepath.Join(ctx.ModuleDir(), "METADATA")); existed {
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "filegroup",
+ },
+ CommonAttributes{Name: "default_metadata_file"},
+ &bazelFilegroupAttributes{
+ Srcs: bazel.MakeLabelListAttribute(BazelLabelForModuleSrc(ctx, []string{"METADATA"})),
+ Applicable_licenses: bazel.LabelListAttribute{Value: bazel.LabelList{Includes: []bazel.Label{}}, EmitEmptyList: true},
+ })
+ defaultPackageMetadata.Value.Add(&bazel.Label{Label: ":default_metadata_file"})
+ }
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "package",
+ },
+ CommonAttributes{},
+ &bazelPackageAttributes{
+ Default_package_metadata: defaultPackageMetadata,
+ // FIXME(asmundak): once b/221436821 is resolved
+ Default_visibility: []string{"//visibility:public"},
+ })
+}
+
func (p *packageModule) GenerateAndroidBuildActions(ModuleContext) {
// Nothing to do.
}
@@ -59,7 +102,7 @@
func PackageFactory() Module {
module := &packageModule{}
- module.AddProperties(&module.properties)
+ module.AddProperties(&module.properties, &module.commonProperties.BazelConversionStatus)
// The name is the relative path from build root to the directory containing this
// module. Set that name at the earliest possible moment that information is available
@@ -76,5 +119,7 @@
// its checking and parsing phases so make it the primary licenses property.
setPrimaryLicensesProperty(module, "default_applicable_licenses", &module.properties.Default_applicable_licenses)
+ InitBazelModule(module)
+
return module
}
diff --git a/android/package_ctx.go b/android/package_ctx.go
index f354db8..c348c82 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -48,7 +48,7 @@
var _ PathContext = &configErrorWrapper{}
var _ errorfContext = &configErrorWrapper{}
-var _ PackageVarContext = &configErrorWrapper{}
+var _ PackageVarContext = &variableFuncContextWrapper{}
var _ PackagePoolContext = &configErrorWrapper{}
var _ PackageRuleContext = &configErrorWrapper{}
@@ -62,21 +62,33 @@
e.config.addNinjaFileDeps(deps...)
}
-type PackageVarContext interface {
+type variableFuncContextWrapper struct {
+ configErrorWrapper
+ blueprint.VariableFuncContext
+}
+
+type PackagePoolContext interface {
PathContext
errorfContext
}
-type PackagePoolContext PackageVarContext
-type PackageRuleContext PackageVarContext
+type PackageRuleContext PackagePoolContext
+
+type PackageVarContext interface {
+ PackagePoolContext
+ PathGlobContext
+}
// VariableFunc wraps blueprint.PackageContext.VariableFunc, converting the interface{} config
// argument to a PackageVarContext.
func (p PackageContext) VariableFunc(name string,
f func(PackageVarContext) string) blueprint.Variable {
- return p.PackageContext.VariableFunc(name, func(config interface{}) (string, error) {
- ctx := &configErrorWrapper{p, config.(Config), nil}
+ return p.PackageContext.VariableFunc(name, func(bpctx blueprint.VariableFuncContext, config interface{}) (string, error) {
+ ctx := &variableFuncContextWrapper{
+ configErrorWrapper: configErrorWrapper{p, config.(Config), nil},
+ VariableFuncContext: bpctx,
+ }
ret := f(ctx)
if len(ctx.errors) > 0 {
return "", ctx.errors[0]
diff --git a/android/packaging.go b/android/packaging.go
index ecd84a2..c764a6d 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -238,11 +238,11 @@
// 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, specs map[string]PackagingSpec, dir ModuleOutPath) (entries []string) {
+func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
seenDir := make(map[string]bool)
- for _, k := range SortedStringKeys(specs) {
+ for _, k := range SortedKeys(specs) {
ps := specs[k]
- destPath := dir.Join(ctx, ps.relPathInPackage).String()
+ destPath := filepath.Join(dir.String(), ps.relPathInPackage)
destDir := filepath.Dir(destPath)
entries = append(entries, ps.relPathInPackage)
if _, ok := seenDir[destDir]; !ok {
diff --git a/android/paths.go b/android/paths.go
index 6163762..eaa6a8d 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -39,6 +38,7 @@
}
type PathGlobContext interface {
+ PathContext
GlobWithDeps(globPattern string, excludes []string) ([]string, error)
}
@@ -56,7 +56,6 @@
// EarlyModulePathContext is a subset of EarlyModuleContext methods required by the
// Path methods. These path methods can be called before any mutators have run.
type EarlyModulePathContext interface {
- PathContext
PathGlobContext
ModuleDir() string
@@ -375,7 +374,7 @@
// ExistentPathsForSources returns a list of Paths rooted from SrcDir, *not* rooted from the
// module's local source directory, that are found in the tree. If any are not found, they are
// omitted from the list, and dependencies are added so that we're re-run when they are added.
-func ExistentPathsForSources(ctx PathContext, paths []string) Paths {
+func ExistentPathsForSources(ctx PathGlobContext, paths []string) Paths {
ret := make(Paths, 0, len(paths))
for _, path := range paths {
p := ExistentPathForSource(ctx, path)
@@ -1033,9 +1032,6 @@
// SourcePath is a Path representing a file path rooted from SrcDir
type SourcePath struct {
basePath
-
- // The sources root, i.e. Config.SrcDir()
- srcDir string
}
func (p SourcePath) RelativeToTop() Path {
@@ -1054,13 +1050,14 @@
// code that is embedding ninja variables in paths
func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
p, err := validateSafePath(pathComponents...)
- ret := SourcePath{basePath{p, ""}, "."}
+ ret := SourcePath{basePath{p, ""}}
if err != nil {
return ret, err
}
// 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())
}
@@ -1070,13 +1067,14 @@
// pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
p, err := validatePath(pathComponents...)
- ret := SourcePath{basePath{p, ""}, "."}
+ ret := SourcePath{basePath{p, ""}}
if err != nil {
return ret, err
}
// 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())
}
@@ -1085,21 +1083,12 @@
// existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the
// path does not exist.
-func existsWithDependencies(ctx PathContext, path SourcePath) (exists bool, err error) {
+func existsWithDependencies(ctx PathGlobContext, path SourcePath) (exists bool, err error) {
var files []string
- if gctx, ok := ctx.(PathGlobContext); ok {
- // Use glob to produce proper dependencies, even though we only want
- // a single file.
- files, err = gctx.GlobWithDeps(path.String(), nil)
- } else {
- var result pathtools.GlobResult
- // We cannot add build statements in this context, so we fall back to
- // AddNinjaFileDeps
- result, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
- ctx.AddNinjaFileDeps(result.Deps...)
- files = result.Matches
- }
+ // Use glob to produce proper dependencies, even though we only want
+ // a single file.
+ files, err = ctx.GlobWithDeps(path.String(), nil)
if err != nil {
return false, fmt.Errorf("glob: %s", err.Error())
@@ -1122,7 +1111,7 @@
}
if modCtx, ok := ctx.(ModuleMissingDepsPathContext); ok && ctx.Config().AllowMissingDependencies() {
- exists, err := existsWithDependencies(ctx, path)
+ exists, err := existsWithDependencies(modCtx, path)
if err != nil {
reportPathError(ctx, err)
}
@@ -1156,7 +1145,7 @@
// rooted from the module's local source directory, if the path exists, or an empty OptionalPath if
// it doesn't exist. Dependencies are added so that the ninja file will be regenerated if the state
// of the path changes.
-func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPath {
+func ExistentPathForSource(ctx PathGlobContext, pathComponents ...string) OptionalPath {
path, err := pathForSource(ctx, pathComponents...)
if err != nil {
reportPathError(ctx, err)
@@ -1181,7 +1170,10 @@
}
func (p SourcePath) String() string {
- return filepath.Join(p.srcDir, p.path)
+ if p.path == "" {
+ return "."
+ }
+ return p.path
}
// Join creates a new SourcePath with paths... joined with the current path. The
@@ -1214,7 +1206,7 @@
// No need to put the error message into the returned path since it has been reported already.
return OptionalPath{}
}
- dir := filepath.Join(p.srcDir, p.path, relDir)
+ dir := filepath.Join(p.path, relDir)
// Use Glob so that we are run again if the directory is added.
if pathtools.IsGlob(dir) {
ReportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
@@ -1227,8 +1219,7 @@
if len(paths) == 0 {
return InvalidOptionalPath(dir + " does not exist")
}
- relPath := Rel(ctx, p.srcDir, paths[0])
- return OptionalPathForPath(PathForSource(ctx, relPath))
+ return OptionalPathForPath(PathForSource(ctx, paths[0]))
}
// OutputPath is a Path representing an intermediates file path rooted from the build directory
@@ -1487,41 +1478,6 @@
return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
}
-// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
-// reference abi dump for the given module. This is not guaranteed to be valid.
-func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName string,
- isNdk, isLlndkOrVndk, isGzip bool) OptionalPath {
-
- currentArchType := ctx.Arch().ArchType
- primaryArchType := ctx.Config().DevicePrimaryArchType()
- archName := currentArchType.String()
- if currentArchType != primaryArchType {
- archName += "_" + primaryArchType.String()
- }
-
- var dirName string
- if isNdk {
- dirName = "ndk"
- } else if isLlndkOrVndk {
- dirName = "vndk"
- } else {
- dirName = "platform" // opt-in libs
- }
-
- binderBitness := ctx.DeviceConfig().BinderBitness()
-
- var ext string
- if isGzip {
- ext = ".lsdump.gz"
- } else {
- ext = ".lsdump"
- }
-
- return ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName,
- version, binderBitness, archName, "source-based",
- fileName+ext)
-}
-
// PathForModuleOut returns a Path representing the paths... under the module's
// output directory.
func PathForModuleOut(ctx ModuleOutPathContext, paths ...string) ModuleOutPath {
@@ -1686,6 +1642,10 @@
}
}
+func (p InstallPath) Partition() string {
+ return p.partition
+}
+
// Join creates a new InstallPath with paths... joined with the current path. The
// provided paths... may not use '..' to escape from the current path.
func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath {
@@ -1749,16 +1709,22 @@
func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, debug bool,
pathComponents ...string) InstallPath {
- var partionPaths []string
+ var partitionPaths []string
if os.Class == Device {
- partionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
+ partitionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
} else {
osName := os.String()
- if os == Linux || os == LinuxMusl {
+ if os == Linux {
// instead of linux_glibc
osName = "linux"
}
+ if os == LinuxMusl && ctx.Config().UseHostMusl() {
+ // When using musl instead of glibc, use "linux" instead of "linux_musl". When cross
+ // compiling we will still use "linux_musl".
+ osName = "linux"
+ }
+
// SOONG_HOST_OUT is set to out/host/$(HOST_OS)-$(HOST_PREBUILT_ARCH)
// and HOST_PREBUILT_ARCH is forcibly set to x86 even on x86_64 hosts. We don't seem
// to have a plan to fix it (see the comment in build/make/core/envsetup.mk).
@@ -1768,21 +1734,21 @@
if os.Class == Host && (arch == X86_64 || arch == Common) {
archName = "x86"
}
- partionPaths = []string{"host", osName + "-" + archName, partition}
+ partitionPaths = []string{"host", osName + "-" + archName, partition}
}
if debug {
- partionPaths = append([]string{"debug"}, partionPaths...)
+ partitionPaths = append([]string{"debug"}, partitionPaths...)
}
- partionPath, err := validatePath(partionPaths...)
+ partitionPath, err := validatePath(partitionPaths...)
if err != nil {
reportPathError(ctx, err)
}
base := InstallPath{
- basePath: basePath{partionPath, ""},
+ basePath: basePath{partitionPath, ""},
soongOutDir: ctx.Config().soongOutDir,
- partitionDir: partionPath,
+ partitionDir: partitionPath,
partition: partition,
}
@@ -1901,10 +1867,14 @@
return ret
}
-// validateSafePath validates a path that we trust (may contain ninja variables).
-// Ensures that each path component does not attempt to leave its component.
-func validateSafePath(pathComponents ...string) (string, error) {
+// validatePathInternal ensures that a path does not leave its component, and
+// optionally doesn't contain Ninja variables.
+func validatePathInternal(allowNinjaVariables bool, pathComponents ...string) (string, error) {
for _, path := range pathComponents {
+ if !allowNinjaVariables && strings.Contains(path, "$") {
+ return "", fmt.Errorf("Path contains invalid character($): %s", path)
+ }
+
path := filepath.Clean(path)
if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
return "", fmt.Errorf("Path is outside directory: %s", path)
@@ -1916,16 +1886,18 @@
return filepath.Join(pathComponents...), nil
}
+// validateSafePath validates a path that we trust (may contain ninja
+// variables). Ensures that each path component does not attempt to leave its
+// component. Returns a joined version of each path component.
+func validateSafePath(pathComponents ...string) (string, error) {
+ return validatePathInternal(true, pathComponents...)
+}
+
// validatePath validates that a path does not include ninja variables, and that
// each path component does not attempt to leave its component. Returns a joined
// version of each path component.
func validatePath(pathComponents ...string) (string, error) {
- for _, path := range pathComponents {
- if strings.Contains(path, "$") {
- return "", fmt.Errorf("Path contains invalid character($): %s", path)
- }
- }
- return validateSafePath(pathComponents...)
+ return validatePathInternal(false, pathComponents...)
}
func PathForPhony(ctx PathContext, phony string) WritablePath {
@@ -1985,6 +1957,18 @@
return testPath{basePath{path: p, rel: p}}
}
+func PathForTestingWithRel(path, rel string) Path {
+ p, err := validateSafePath(path, rel)
+ if err != nil {
+ panic(err)
+ }
+ r, err := validatePath(rel)
+ if err != nil {
+ panic(err)
+ }
+ return testPath{basePath{path: p, rel: r}}
+}
+
// PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests.
func PathsForTesting(strs ...string) Paths {
p := make(Paths, len(strs))
@@ -2114,13 +2098,16 @@
// Writes a file to the output directory. Attempting to write directly to the output directory
// will fail due to the sandbox of the soong_build process.
+// Only writes the file if the file doesn't exist or if it has different contents, to prevent
+// updating the timestamp if no changes would be made. (This is better for incremental
+// performance.)
func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
absPath := absolutePath(path.String())
err := os.MkdirAll(filepath.Dir(absPath), 0777)
if err != nil {
return err
}
- return ioutil.WriteFile(absPath, data, perm)
+ return pathtools.WriteFileIfChanged(absPath, data, perm)
}
func RemoveAllOutputDir(path WritablePath) error {
diff --git a/android/phony.go b/android/phony.go
index 0adbb55..814a9e3 100644
--- a/android/phony.go
+++ b/android/phony.go
@@ -48,7 +48,7 @@
func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) {
p.phonyMap = getPhonyMap(ctx.Config())
- p.phonyList = SortedStringKeys(p.phonyMap)
+ p.phonyList = SortedKeys(p.phonyMap)
for _, phony := range p.phonyList {
p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony])
}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index e7f221b..9b5c0e9 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -122,6 +122,18 @@
return strings.TrimPrefix(name, "prebuilt_")
}
+// RemoveOptionalPrebuiltPrefixFromBazelLabel removes the "prebuilt_" prefix from the *target name* of a Bazel label.
+// This differs from RemoveOptionalPrebuiltPrefix in that it does not remove it from the start of the string, but
+// instead removes it from the target name itself.
+func RemoveOptionalPrebuiltPrefixFromBazelLabel(label string) string {
+ splitLabel := strings.Split(label, ":")
+ bazelModuleNameNoPrebuilt := RemoveOptionalPrebuiltPrefix(splitLabel[1])
+ return strings.Join([]string{
+ splitLabel[0],
+ bazelModuleNameNoPrebuilt,
+ }, ":")
+}
+
func (p *Prebuilt) Name(name string) string {
return PrebuiltNameFromSource(name)
}
diff --git a/android/proto.go b/android/proto.go
index c3759f8..09e50c8 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -15,10 +15,10 @@
package android
import (
- "android/soong/bazel"
- "regexp"
"strings"
+ "android/soong/bazel"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -27,14 +27,6 @@
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
@@ -163,13 +155,21 @@
// Bp2buildProtoInfo contains information necessary to pass on to language specific conversion.
type Bp2buildProtoInfo struct {
- Type *string
- Name string
+ Type *string
+ Name string
+ Proto_libs bazel.LabelList
}
-type protoAttrs struct {
+type ProtoAttrs struct {
Srcs bazel.LabelListAttribute
Strip_import_prefix *string
+ Deps bazel.LabelListAttribute
+}
+
+// For each package in the include_dirs property a proto_library target should
+// be added to the BUILD file in that package and a mapping should be added here
+var includeDirsToProtoDeps = map[string]string{
+ "external/protobuf/src": "//external/protobuf:libprotobuf-proto",
}
// Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
@@ -180,46 +180,74 @@
return info, false
}
- info.Name = m.Name() + "_proto"
- attrs := protoAttrs{
- Srcs: srcs,
- }
+ var protoLibraries bazel.LabelList
+ var directProtoSrcs bazel.LabelList
- for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) {
- for _, rawProps := range configToProps {
- var props *ProtoProperties
- var ok bool
- if props, ok = rawProps.(*ProtoProperties); !ok {
- ctx.ModuleErrorf("Could not cast ProtoProperties to expected type")
- }
- if axis == bazel.NoConfigAxis {
- info.Type = props.Proto.Type
-
- 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
- }
- } else if props.Proto.Type != info.Type && props.Proto.Type != nil {
- ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.")
- }
+ // For filegroups that should be converted to proto_library just collect the
+ // labels of converted proto_library targets.
+ for _, protoSrc := range srcs.Value.Includes {
+ src := protoSrc.OriginalModuleName
+ if fg, ok := ToFileGroupAsLibrary(ctx, src); ok &&
+ fg.ShouldConvertToProtoLibrary(ctx) {
+ protoLibraries.Add(&bazel.Label{
+ Label: fg.GetProtoLibraryLabel(ctx),
+ })
+ } else {
+ directProtoSrcs.Add(&protoSrc)
}
}
- ctx.CreateBazelTargetModule(
- bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
- CommonAttributes{Name: info.Name},
- &attrs)
+ info.Name = m.Name() + "_proto"
+
+ if len(directProtoSrcs.Includes) > 0 {
+ attrs := ProtoAttrs{
+ Srcs: bazel.MakeLabelListAttribute(directProtoSrcs),
+ }
+ attrs.Deps.Append(bazel.MakeLabelListAttribute(protoLibraries))
+
+ for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) {
+ for _, rawProps := range configToProps {
+ var props *ProtoProperties
+ var ok bool
+ if props, ok = rawProps.(*ProtoProperties); !ok {
+ ctx.ModuleErrorf("Could not cast ProtoProperties to expected type")
+ }
+ if axis == bazel.NoConfigAxis {
+ info.Type = props.Proto.Type
+
+ 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
+ }
+
+ for _, dir := range props.Proto.Include_dirs {
+ if dep, ok := includeDirsToProtoDeps[dir]; ok {
+ attrs.Deps.Add(bazel.MakeLabelAttribute(dep))
+ } else {
+ ctx.PropertyErrorf("Could not find the proto_library target for include dir", dir)
+ }
+ }
+ } else if props.Proto.Type != info.Type && props.Proto.Type != nil {
+ ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.")
+ }
+ }
+ }
+
+ tags := ApexAvailableTags(ctx.Module())
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
+ CommonAttributes{Name: info.Name, Tags: tags},
+ &attrs,
+ )
+
+ protoLibraries.Add(&bazel.Label{
+ Label: ":" + info.Name,
+ })
+ }
+
+ info.Proto_libs = protoLibraries
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 c8715b1..1a3db9d 100644
--- a/android/register.go
+++ b/android/register.go
@@ -35,7 +35,7 @@
// tests.
componentName() string
- // register registers this component in the supplied context.
+ // registers this component in the supplied context.
register(ctx *Context)
}
@@ -96,10 +96,11 @@
var preSingletons sortableComponents
type mutator struct {
- name string
- bottomUpMutator blueprint.BottomUpMutator
- topDownMutator blueprint.TopDownMutator
- parallel bool
+ name string
+ bottomUpMutator blueprint.BottomUpMutator
+ topDownMutator blueprint.TopDownMutator
+ transitionMutator blueprint.TransitionMutator
+ parallel bool
}
var _ sortableComponent = &mutator{}
@@ -123,7 +124,7 @@
return func() blueprint.Singleton {
singleton := factory()
if makevars, ok := singleton.(SingletonMakeVarsProvider); ok {
- registerSingletonMakeVarsProvider(ctx.config, makevars)
+ ctx.registerSingletonMakeVarsProvider(makevars)
}
return &singletonAdaptor{Singleton: singleton}
}
@@ -160,29 +161,38 @@
func NewContext(config Config) *Context {
ctx := &Context{blueprint.NewContext(), config}
ctx.SetSrcDir(absSrcDir)
+ ctx.AddIncludeTags(config.IncludeTags()...)
+ ctx.AddSourceRootDirs(config.SourceRootDirs()...)
return ctx
}
-func (ctx *Context) SetRunningAsBp2build() {
- ctx.config.runningAsBp2Build = true
+// Helper function to register the module types used in bp2build and
+// api_bp2build.
+func registerModuleTypes(ctx *Context) {
+ for _, t := range moduleTypes {
+ t.register(ctx)
+ }
+ // Required for SingletonModule types, even though we are not using them.
+ for _, t := range singletons {
+ t.register(ctx)
+ }
}
// RegisterForBazelConversion registers an alternate shadow pipeline of
// singletons, module types and mutators to register for converting Blueprint
// files to semantically equivalent BUILD files.
func (ctx *Context) RegisterForBazelConversion() {
- for _, t := range moduleTypes {
- t.register(ctx)
- }
-
- // Required for SingletonModule types, even though we are not using them.
- for _, t := range singletons {
- t.register(ctx)
- }
-
+ registerModuleTypes(ctx)
RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators)
}
+// RegisterForApiBazelConversion is similar to RegisterForBazelConversion except that
+// it only generates API targets in the generated workspace
+func (ctx *Context) RegisterForApiBazelConversion() {
+ registerModuleTypes(ctx)
+ RegisterMutatorsForApiBazelConversion(ctx, bp2buildPreArchMutators)
+}
+
// Register the pipeline of singletons, module types, and mutators for
// generating build.ninja and other files for Kati, from Android.bp files.
func (ctx *Context) Register() {
@@ -199,6 +209,14 @@
singletons.registerAll(ctx)
}
+func (ctx *Context) Config() Config {
+ return ctx.config
+}
+
+func (ctx *Context) registerSingletonMakeVarsProvider(makevars SingletonMakeVarsProvider) {
+ registerSingletonMakeVarsProvider(ctx.config, makevars)
+}
+
func collateGloballyRegisteredSingletons() sortableComponents {
allSingletons := append(sortableComponents(nil), singletons...)
allSingletons = append(allSingletons,
@@ -326,7 +344,7 @@
PreArchMutators(f)
}
-func (ctx *initRegistrationContext) HardCodedPreArchMutators(f RegisterMutatorFunc) {
+func (ctx *initRegistrationContext) HardCodedPreArchMutators(_ RegisterMutatorFunc) {
// Nothing to do as the mutators are hard code in preArch in mutator.go
}
diff --git a/android/sdk.go b/android/sdk.go
index bd2f5d1..63e0bbe 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -23,60 +23,9 @@
"github.com/google/blueprint/proptools"
)
-// sdkAwareWithoutModule is provided simply to improve code navigation with the IDE.
-type sdkAwareWithoutModule interface {
- // SdkMemberComponentName will return the name to use for a component of this module based on the
- // base name of this module.
- //
- // The baseName is the name returned by ModuleBase.BaseModuleName(), i.e. the name specified in
- // the name property in the .bp file so will not include the prebuilt_ prefix.
- //
- // The componentNameCreator is a func for creating the name of a component from the base name of
- // the module, e.g. it could just append ".component" to the name passed in.
- //
- // This is intended to be called by prebuilt modules that create component models. It is because
- // prebuilt module base names come in a variety of different forms:
- // * unversioned - this is the same as the source module.
- // * internal to an sdk - this is the unversioned name prefixed by the base name of the sdk
- // module.
- // * versioned - this is the same as the internal with the addition of an "@<version>" suffix.
- //
- // While this can be called from a source module in that case it will behave the same way as the
- // unversioned name and return the result of calling the componentNameCreator func on the supplied
- // base name.
- //
- // e.g. Assuming the componentNameCreator func simply appends ".component" to the name passed in
- // then this will work as follows:
- // * An unversioned name of "foo" will return "foo.component".
- // * An internal to the sdk name of "sdk_foo" will return "sdk_foo.component".
- // * A versioned name of "sdk_foo@current" will return "sdk_foo.component@current".
- //
- // Note that in the latter case the ".component" suffix is added before the version. Adding it
- // after would change the version.
- SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string
-
- sdkBase() *SdkBase
- MakeMemberOf(sdk SdkRef)
- IsInAnySdk() bool
-
- // IsVersioned determines whether the module is versioned, i.e. has a name of the form
- // <name>@<version>
- IsVersioned() bool
-
- ContainingSdk() SdkRef
- MemberName() string
-}
-
-// SdkAware is the interface that must be supported by any module to become a member of SDK or to be
-// built with SDK
-type SdkAware interface {
- Module
- sdkAwareWithoutModule
-}
-
// minApiLevelForSdkSnapshot provides access to the min_sdk_version for MinApiLevelForSdkSnapshot
type minApiLevelForSdkSnapshot interface {
- MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+ MinSdkVersion(ctx EarlyModuleContext) ApiLevel
}
// MinApiLevelForSdkSnapshot returns the ApiLevel of the min_sdk_version of the supplied module.
@@ -85,7 +34,7 @@
func MinApiLevelForSdkSnapshot(ctx EarlyModuleContext, module Module) ApiLevel {
minApiLevel := NoneApiLevel
if m, ok := module.(minApiLevelForSdkSnapshot); ok {
- minApiLevel = m.MinSdkVersion(ctx).ApiLevel
+ minApiLevel = m.MinSdkVersion(ctx)
}
if minApiLevel == NoneApiLevel {
// The default min API level is 1.
@@ -94,138 +43,6 @@
return minApiLevel
}
-// SdkRef refers to a version of an SDK
-type SdkRef struct {
- Name string
- Version string
-}
-
-// Unversioned determines if the SdkRef is referencing to the unversioned SDK module
-func (s SdkRef) Unversioned() bool {
- return s.Version == ""
-}
-
-// String returns string representation of this SdkRef for debugging purpose
-func (s SdkRef) String() string {
- if s.Name == "" {
- return "(No Sdk)"
- }
- if s.Unversioned() {
- return s.Name
- }
- return s.Name + string(SdkVersionSeparator) + s.Version
-}
-
-// SdkVersionSeparator is a character used to separate an sdk name and its version
-const SdkVersionSeparator = '@'
-
-// ParseSdkRef parses a `name@version` style string into a corresponding SdkRef struct
-func ParseSdkRef(ctx BaseModuleContext, str string, property string) SdkRef {
- tokens := strings.Split(str, string(SdkVersionSeparator))
- if len(tokens) < 1 || len(tokens) > 2 {
- ctx.PropertyErrorf(property, "%q does not follow name@version syntax", str)
- return SdkRef{Name: "invalid sdk name", Version: "invalid sdk version"}
- }
-
- name := tokens[0]
-
- var version string
- if len(tokens) == 2 {
- version = tokens[1]
- }
-
- return SdkRef{Name: name, Version: version}
-}
-
-type SdkRefs []SdkRef
-
-// Contains tells if the given SdkRef is in this list of SdkRef's
-func (refs SdkRefs) Contains(s SdkRef) bool {
- for _, r := range refs {
- if r == s {
- return true
- }
- }
- return false
-}
-
-type sdkProperties struct {
- // The SDK that this module is a member of. nil if it is not a member of any SDK
- ContainingSdk *SdkRef `blueprint:"mutated"`
-
- // Name of the module that this sdk member is representing
- Sdk_member_name *string
-}
-
-// SdkBase is a struct that is expected to be included in module types to implement the SdkAware
-// interface. InitSdkAwareModule should be called to initialize this struct.
-type SdkBase struct {
- properties sdkProperties
- module SdkAware
-}
-
-func (s *SdkBase) sdkBase() *SdkBase {
- return s
-}
-
-func (s *SdkBase) SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string {
- if s.MemberName() == "" {
- return componentNameCreator(baseName)
- } else {
- index := strings.LastIndex(baseName, "@")
- unversionedName := baseName[:index]
- unversionedComponentName := componentNameCreator(unversionedName)
- versionSuffix := baseName[index:]
- return unversionedComponentName + versionSuffix
- }
-}
-
-// MakeMemberOf sets this module to be a member of a specific SDK
-func (s *SdkBase) MakeMemberOf(sdk SdkRef) {
- s.properties.ContainingSdk = &sdk
-}
-
-// IsInAnySdk returns true if this module is a member of any SDK
-func (s *SdkBase) IsInAnySdk() bool {
- return s.properties.ContainingSdk != nil
-}
-
-// IsVersioned returns true if this module is versioned.
-func (s *SdkBase) IsVersioned() bool {
- return strings.Contains(s.module.Name(), "@")
-}
-
-// ContainingSdk returns the SDK that this module is a member of
-func (s *SdkBase) ContainingSdk() SdkRef {
- if s.properties.ContainingSdk != nil {
- return *s.properties.ContainingSdk
- }
- return SdkRef{Name: "", Version: ""}
-}
-
-// MemberName returns the name of the module that this SDK member is overriding
-func (s *SdkBase) MemberName() string {
- return proptools.String(s.properties.Sdk_member_name)
-}
-
-// InitSdkAwareModule initializes the SdkBase struct. This must be called by all modules including
-// SdkBase.
-func InitSdkAwareModule(m SdkAware) {
- base := m.sdkBase()
- base.module = m
- m.AddProperties(&base.properties)
-}
-
-// IsModuleInVersionedSdk returns true if the module is an versioned sdk.
-func IsModuleInVersionedSdk(module Module) bool {
- if s, ok := module.(SdkAware); ok {
- if !s.ContainingSdk().Unversioned() {
- return true
- }
- }
- return false
-}
-
// SnapshotBuilder provides support for generating the build rules which will build the snapshot.
type SnapshotBuilder interface {
// CopyToSnapshot generates a rule that will copy the src to the dest (which is a snapshot
@@ -593,7 +410,7 @@
Name() string
// Variants returns all the variants of this module depended upon by the SDK.
- Variants() []SdkAware
+ Variants() []Module
}
// SdkMemberDependencyTag is the interface that a tag must implement in order to allow the
@@ -725,7 +542,7 @@
// The sdk module code generates the snapshot as follows:
//
// * A properties struct of type SdkMemberProperties is created for each variant and
- // populated with information from the variant by calling PopulateFromVariant(SdkAware)
+ // populated with information from the variant by calling PopulateFromVariant(Module)
// on the struct.
//
// * An additional properties struct is created into which the common properties will be
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 2004c92..0ae8073 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -25,12 +25,15 @@
SdkVersion(ctx EarlyModuleContext) SdkSpec
// SystemModules returns the system_modules property of the current module, or an empty string if it is not set.
SystemModules() string
- // MinSdkVersion returns SdkSpec that corresponds to the min_sdk_version property of the current module,
+ // MinSdkVersion returns ApiLevel that corresponds to the min_sdk_version property of the current module,
// or from sdk_version if it is not set.
- MinSdkVersion(ctx EarlyModuleContext) SdkSpec
- // TargetSdkVersion returns the SdkSpec that corresponds to the target_sdk_version property of the current module,
+ MinSdkVersion(ctx EarlyModuleContext) ApiLevel
+ // ReplaceMaxSdkVersionPlaceholder returns Apilevel to replace the maxSdkVersion property of permission and
+ // uses-permission tags if it is set.
+ ReplaceMaxSdkVersionPlaceholder(ctx EarlyModuleContext) ApiLevel
+ // TargetSdkVersion returns the ApiLevel that corresponds to the target_sdk_version property of the current module,
// or from sdk_version if it is not set.
- TargetSdkVersion(ctx EarlyModuleContext) SdkSpec
+ TargetSdkVersion(ctx EarlyModuleContext) ApiLevel
}
// SdkKind represents a particular category of an SDK spec like public, system, test, etc.
@@ -41,12 +44,14 @@
SdkNone
SdkCore
SdkCorePlatform
+ SdkIntraCore // API surface provided by one core module to another
SdkPublic
SdkSystem
SdkTest
SdkModule
SdkSystemServer
SdkPrivate
+ SdkToolchain // API surface provided by ART to compile other API domains
)
// String returns the string representation of this SdkKind
@@ -66,15 +71,54 @@
return "core"
case SdkCorePlatform:
return "core_platform"
+ case SdkIntraCore:
+ return "intracore"
case SdkModule:
return "module-lib"
case SdkSystemServer:
return "system-server"
+ case SdkToolchain:
+ return "toolchain"
default:
return "invalid"
}
}
+// JavaLibraryName returns the soong module containing the Java APIs of that API surface.
+func (k SdkKind) JavaLibraryName(c Config) string {
+ name := k.DefaultJavaLibraryName()
+ return JavaApiLibraryName(c, name)
+}
+
+// JavaApiLibraryName returns the name of .txt equivalent of a java_library, but does
+// not check if either module exists.
+// TODO: Return .txt (single-tree or multi-tree equivalents) based on config
+func JavaApiLibraryName(c Config, name string) string {
+ if c.BuildFromTextStub() {
+ return name + ".from-text"
+ }
+ return name
+}
+
+func (k SdkKind) DefaultJavaLibraryName() string {
+ switch k {
+ case SdkPublic:
+ return "android_stubs_current"
+ case SdkSystem:
+ return "android_system_stubs_current"
+ case SdkTest:
+ return "android_test_stubs_current"
+ case SdkCore:
+ return "core.current.stubs"
+ case SdkModule:
+ return "android_module_lib_stubs_current"
+ case SdkSystemServer:
+ return "android_system_server_stubs_current"
+ default:
+ panic(fmt.Errorf("APIs of API surface %v cannot be provided by a single Soong module\n", k))
+ }
+}
+
// SdkSpec represents the kind and the version of an SDK for a module to build against
type SdkSpec struct {
Kind SdkKind
@@ -178,14 +222,7 @@
if ctx.DeviceSpecific() || ctx.SocSpecific() {
s = s.ForVendorPartition(ctx)
}
- if !s.ApiLevel.IsPreview() {
- return s.ApiLevel, nil
- }
- ret := ctx.Config().DefaultAppTargetSdk(ctx)
- if ret.IsPreview() {
- return FutureApiLevel, nil
- }
- return ret, nil
+ return s.ApiLevel.EffectiveVersion(ctx)
}
// EffectiveVersionString converts an SdkSpec into the concrete version string that the module
@@ -199,15 +236,12 @@
if ctx.DeviceSpecific() || ctx.SocSpecific() {
s = s.ForVendorPartition(ctx)
}
- if !s.ApiLevel.IsPreview() {
- return s.ApiLevel.String(), nil
- }
- return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
+ return s.ApiLevel.EffectiveVersionString(ctx)
}
var (
SdkSpecNone = SdkSpec{SdkNone, NoneApiLevel, "(no version)"}
- SdkSpecPrivate = SdkSpec{SdkPrivate, FutureApiLevel, ""}
+ SdkSpecPrivate = SdkSpec{SdkPrivate, PrivateApiLevel, ""}
SdkSpecCorePlatform = SdkSpec{SdkCorePlatform, FutureApiLevel, "core_platform"}
)
@@ -230,7 +264,7 @@
var kindString string
if sep == 0 {
- return SdkSpec{SdkInvalid, NoneApiLevel, str}
+ return SdkSpec{SdkInvalid, NewInvalidApiLevel(str), str}
} else if sep == -1 {
kindString = ""
} else {
@@ -258,7 +292,7 @@
apiLevel, err := ApiLevelFromUserWithConfig(config, versionString)
if err != nil {
- return SdkSpec{SdkInvalid, apiLevel, str}
+ return SdkSpec{SdkInvalid, NewInvalidApiLevel(versionString), str}
}
return SdkSpec{kind, apiLevel, str}
}
@@ -285,3 +319,18 @@
}
return true
}
+
+func init() {
+ RegisterMakeVarsProvider(pctx, javaSdkMakeVars)
+}
+
+// Export the name of the soong modules representing the various Java API surfaces.
+func javaSdkMakeVars(ctx MakeVarsContext) {
+ ctx.Strict("ANDROID_PUBLIC_STUBS", SdkPublic.JavaLibraryName(ctx.Config()))
+ ctx.Strict("ANDROID_SYSTEM_STUBS", SdkSystem.JavaLibraryName(ctx.Config()))
+ ctx.Strict("ANDROID_TEST_STUBS", SdkTest.JavaLibraryName(ctx.Config()))
+ ctx.Strict("ANDROID_MODULE_LIB_STUBS", SdkModule.JavaLibraryName(ctx.Config()))
+ ctx.Strict("ANDROID_SYSTEM_SERVER_STUBS", SdkSystemServer.JavaLibraryName(ctx.Config()))
+ // TODO (jihoonkang): Create a .txt equivalent for core.current.stubs
+ ctx.Strict("ANDROID_CORE_STUBS", SdkCore.JavaLibraryName(ctx.Config()))
+}
diff --git a/android/sdk_version_test.go b/android/sdk_version_test.go
index ec81782..ea99c4d 100644
--- a/android/sdk_version_test.go
+++ b/android/sdk_version_test.go
@@ -37,11 +37,11 @@
},
{
input: "_",
- expected: "invalid_(no version)",
+ expected: "invalid__",
},
{
input: "_31",
- expected: "invalid_(no version)",
+ expected: "invalid__31",
},
{
input: "system_R",
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index b25f248..5fa6012 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -20,7 +20,9 @@
import (
"fmt"
"path/filepath"
+ "reflect"
"strings"
+ "sync"
"text/scanner"
"github.com/google/blueprint"
@@ -31,12 +33,18 @@
)
func init() {
- RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
- RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
- RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
- RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
+ RegisterSoongConfigModuleBuildComponents(InitRegistrationContext)
}
+func RegisterSoongConfigModuleBuildComponents(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)
+}
+
+var PrepareForTestWithSoongConfigModuleBuildComponents = FixtureRegisterWithContext(RegisterSoongConfigModuleBuildComponents)
+
type soongConfigModuleTypeImport struct {
ModuleBase
properties soongConfigModuleTypeImportProperties
@@ -172,7 +180,7 @@
"soong_config_module_type_import_" + fmt.Sprintf("%p", m)
}
-func (*soongConfigModuleTypeImport) Nameless() {}
+func (*soongConfigModuleTypeImport) Namespaceless() {}
func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {}
// Create dummy modules for soong_config_module_type and soong_config_*_variable
@@ -280,9 +288,9 @@
}
func (m *soongConfigModuleTypeModule) Name() string {
- return m.properties.Name
+ return m.properties.Name + fmt.Sprintf("%p", m)
}
-func (*soongConfigModuleTypeModule) Nameless() {}
+func (*soongConfigModuleTypeModule) Namespaceless() {}
func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
type soongConfigStringVariableDummyModule struct {
@@ -315,15 +323,15 @@
}
func (m *soongConfigStringVariableDummyModule) Name() string {
- return m.properties.Name
+ return m.properties.Name + fmt.Sprintf("%p", m)
}
-func (*soongConfigStringVariableDummyModule) Nameless() {}
+func (*soongConfigStringVariableDummyModule) Namespaceless() {}
func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
func (m *soongConfigBoolVariableDummyModule) Name() string {
- return m.properties.Name
+ return m.properties.Name + fmt.Sprintf("%p", m)
}
-func (*soongConfigBoolVariableDummyModule) Nameless() {}
+func (*soongConfigBoolVariableDummyModule) Namespaceless() {}
func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
// importModuleTypes registers the module factories for a list of module types defined
@@ -382,15 +390,15 @@
defer r.Close()
mtDef, errs := soongconfig.Parse(r, from)
- if ctx.Config().runningAsBp2Build {
- ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(*mtDef)
- }
-
if len(errs) > 0 {
reportErrors(ctx, from, errs...)
return (map[string]blueprint.ModuleFactory)(nil)
}
+ if ctx.Config().BuildMode == Bp2build {
+ ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(mtDef)
+ }
+
globalModuleTypes := ctx.moduleFactories()
factories := make(map[string]blueprint.ModuleFactory)
@@ -398,7 +406,7 @@
for name, moduleType := range mtDef.ModuleTypes {
factory := globalModuleTypes[moduleType.BaseModuleType]
if factory != nil {
- factories[name] = configModuleFactory(factory, moduleType, ctx.Config().runningAsBp2Build)
+ factories[name] = configModuleFactory(factory, moduleType, ctx.Config().BuildMode == Bp2build)
} else {
reportErrors(ctx, from,
fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
@@ -416,13 +424,43 @@
// configModuleFactory takes an existing soongConfigModuleFactory and a
// ModuleType to create a new ModuleFactory that uses a custom loadhook.
func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType, bp2build bool) blueprint.ModuleFactory {
- conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
- if !conditionalFactoryProps.IsValid() {
- return factory
+ // Defer creation of conditional properties struct until the first call from the factory
+ // method. That avoids having to make a special call to the factory to create the properties
+ // structs from which the conditional properties struct is created. This is needed in order to
+ // allow singleton modules to be customized by soong_config_module_type as the
+ // SingletonModuleFactoryAdaptor factory registers a load hook for the singleton module
+ // everytime that it is called. Calling the factory twice causes a build failure as the load
+ // hook is called twice, the first time it updates the singleton module to indicate that it has
+ // been registered as a module, and the second time it fails because it thinks it has been
+ // registered again and a singleton module can only be registered once.
+ //
+ // This is an issue for singleton modules because:
+ // * Load hooks are registered on the module object and are only called when the module object
+ // is created by Blueprint while processing the Android.bp file.
+ // * The module factory for a singleton module returns the same module object each time it is
+ // called, and registers its load hook on that same module object.
+ // * When the module factory is called by Blueprint it then calls all the load hooks that have
+ // been registered for every call to that module factory.
+ //
+ // It is not an issue for normal modules because they return a new module object each time the
+ // factory is called and so any load hooks registered on module objects which are discarded will
+ // not be run.
+ once := &sync.Once{}
+ conditionalFactoryProps := reflect.Value{}
+ getConditionalFactoryProps := func(props []interface{}) reflect.Value {
+ once.Do(func() {
+ conditionalFactoryProps = soongconfig.CreateProperties(props, moduleType)
+ })
+ return conditionalFactoryProps
}
return func() (blueprint.Module, []interface{}) {
module, props := factory()
+ conditionalFactoryProps := getConditionalFactoryProps(props)
+ if !conditionalFactoryProps.IsValid() {
+ return module, props
+ }
+
conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
props = append(props, conditionalProps.Interface())
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index ceb8e45..cab3e2d 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -15,12 +15,10 @@
package android
import (
+ "fmt"
"testing"
)
-type soongConfigTestDefaultsModuleProperties struct {
-}
-
type soongConfigTestDefaultsModule struct {
ModuleBase
DefaultsModuleBase
@@ -53,6 +51,11 @@
func (t soongConfigTestModule) GenerateAndroidBuildActions(ModuleContext) {}
+var prepareForSoongConfigTestModule = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+ ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+})
+
func TestSoongConfigModule(t *testing.T) {
configBp := `
soong_config_module_type {
@@ -309,14 +312,8 @@
result := GroupFixturePreparers(
tc.preparer,
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)
- }),
+ PrepareForTestWithSoongConfigModuleBuildComponents,
+ prepareForSoongConfigTestModule,
fs.AddToFixture(),
FixtureWithRootAndroidBp(bp),
).RunTest(t)
@@ -371,14 +368,8 @@
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)
- }),
+ PrepareForTestWithSoongConfigModuleBuildComponents,
+ prepareForSoongConfigTestModule,
FixtureWithRootAndroidBp(bp),
).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
// TODO(b/171232169): improve the error message for non-existent properties
@@ -411,14 +402,8 @@
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)
- }),
+ PrepareForTestWithSoongConfigModuleBuildComponents,
+ prepareForSoongConfigTestModule,
FixtureWithRootAndroidBp(bp),
).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
// TODO(b/171232169): improve the error message for non-existent properties
@@ -426,10 +411,95 @@
})).RunTest(t)
}
-func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
- config := TestConfig(buildDir, nil, bp, fs)
+type soongConfigTestSingletonModule struct {
+ SingletonModuleBase
+ props soongConfigTestSingletonModuleProperties
+}
- config.TestProductVariables.VendorVars = vendorVars
+type soongConfigTestSingletonModuleProperties struct {
+ Fragments []struct {
+ Apex string
+ Module string
+ }
+}
- return config
+func soongConfigTestSingletonModuleFactory() SingletonModule {
+ m := &soongConfigTestSingletonModule{}
+ m.AddProperties(&m.props)
+ InitAndroidModule(m)
+ return m
+}
+
+func (t *soongConfigTestSingletonModule) GenerateAndroidBuildActions(ModuleContext) {}
+
+func (t *soongConfigTestSingletonModule) GenerateSingletonBuildActions(SingletonContext) {}
+
+var prepareForSoongConfigTestSingletonModule = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterSingletonModuleType("test_singleton", soongConfigTestSingletonModuleFactory)
+})
+
+func TestSoongConfigModuleSingletonModule(t *testing.T) {
+ bp := `
+ soong_config_module_type {
+ name: "acme_test_singleton",
+ module_type: "test_singleton",
+ config_namespace: "acme",
+ bool_variables: ["coyote"],
+ properties: ["fragments"],
+ }
+
+ acme_test_singleton {
+ name: "wiley",
+ fragments: [
+ {
+ apex: "com.android.acme",
+ module: "road-runner",
+ },
+ ],
+ soong_config_variables: {
+ coyote: {
+ fragments: [
+ {
+ apex: "com.android.acme",
+ module: "wiley",
+ },
+ ],
+ },
+ },
+ }
+ `
+
+ for _, test := range []struct {
+ coyote bool
+ expectedFragments string
+ }{
+ {
+ coyote: false,
+ expectedFragments: "[{Apex:com.android.acme Module:road-runner}]",
+ },
+ {
+ coyote: true,
+ expectedFragments: "[{Apex:com.android.acme Module:road-runner} {Apex:com.android.acme Module:wiley}]",
+ },
+ } {
+ t.Run(fmt.Sprintf("coyote:%t", test.coyote), func(t *testing.T) {
+ result := GroupFixturePreparers(
+ PrepareForTestWithSoongConfigModuleBuildComponents,
+ prepareForSoongConfigTestSingletonModule,
+ FixtureWithRootAndroidBp(bp),
+ FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.VendorVars = map[string]map[string]string{
+ "acme": {
+ "coyote": fmt.Sprintf("%t", test.coyote),
+ },
+ }
+ }),
+ ).RunTest(t)
+
+ // Make sure that the singleton was created.
+ result.SingletonForTests("test_singleton")
+ m := result.ModuleForTests("wiley", "").module.(*soongConfigTestSingletonModule)
+ AssertStringEquals(t, "fragments", test.expectedFragments, fmt.Sprintf("%+v", m.props.Fragments))
+ })
+ }
}
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 7d21b75..23c8afa 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -22,7 +22,6 @@
"strings"
"sync"
- "github.com/google/blueprint"
"github.com/google/blueprint/parser"
"github.com/google/blueprint/proptools"
@@ -241,12 +240,7 @@
// string vars, bool vars and value vars created by every
// soong_config_module_type in this build.
type Bp2BuildSoongConfigDefinitions struct {
- // 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
+ StringVars map[string]map[string]bool
BoolVars map[string]bool
ValueVars map[string]bool
}
@@ -256,7 +250,7 @@
// SoongConfigVariablesForBp2build extracts information from a
// SoongConfigDefinition that bp2build needs to generate constraint settings and
// values for, in order to migrate soong_config_module_type usages to Bazel.
-func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef SoongConfigDefinition) {
+func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef *SoongConfigDefinition) {
// In bp2build mode, this method is called concurrently in goroutines from
// loadhooks while parsing soong_config_module_type, so add a mutex to
// prevent concurrent map writes. See b/207572723
@@ -264,7 +258,7 @@
defer bp2buildSoongConfigVarsLock.Unlock()
if defs.StringVars == nil {
- defs.StringVars = make(map[string][]string)
+ defs.StringVars = make(map[string]map[string]bool)
}
if defs.BoolVars == nil {
defs.BoolVars = make(map[string]bool)
@@ -272,24 +266,29 @@
if defs.ValueVars == nil {
defs.ValueVars = make(map[string]bool)
}
- if defs.varCache == nil {
- defs.varCache = make(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{}
+
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 {
+ if _, keyInCache := varCache[key]; keyInCache {
continue
} else {
- defs.varCache[key] = true
+ varCache[key] = true
}
if strVar, ok := v.(*stringVariable); ok {
+ if _, ok := defs.StringVars[key]; !ok {
+ defs.StringVars[key] = make(map[string]bool, len(strVar.values))
+ }
for _, value := range strVar.values {
- defs.StringVars[key] = append(defs.StringVars[key], value)
+ defs.StringVars[key][value] = true
}
} else if _, ok := v.(*boolVariable); ok {
defs.BoolVars[key] = true
@@ -330,8 +329,13 @@
ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
ret += "\n\n"
+ stringVars := make(map[string][]string, len(defs.StringVars))
+ for k, v := range defs.StringVars {
+ stringVars[k] = sortedStringKeys(v)
+ }
+
ret += "soong_config_string_variables = "
- ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
+ ret += starlark_fmt.PrintStringListDict(stringVars, 0)
return ret
}
@@ -363,10 +367,9 @@
// },
// },
// }
-func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
+func CreateProperties(factoryProps []interface{}, moduleType *ModuleType) reflect.Value {
var fields []reflect.StructField
- _, factoryProps := factory()
affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
if affectablePropertiesType == nil {
return reflect.Value{}
@@ -642,9 +645,13 @@
// Extracts an interface from values containing the properties to apply based on config.
// If config does not match a value with a non-nil property set, the default value will be returned.
func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
+ configValue := config.String(s.variable)
+ if configValue != "" && !InList(configValue, s.values) {
+ return nil, fmt.Errorf("Soong config property %q must be one of %v, found %q", s.variable, s.values, configValue)
+ }
for j, v := range s.values {
f := values.Field(j)
- if config.String(s.variable) == v && !f.Elem().IsNil() {
+ if configValue == v && !f.Elem().IsNil() {
return f.Interface(), nil
}
}
@@ -861,3 +868,13 @@
}
var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
+
+// InList checks if the string belongs to the list
+func InList(s string, list []string) bool {
+ for _, s2 := range list {
+ if s2 == s {
+ return true
+ }
+ }
+ return false
+}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index a7800e8..a5fa349 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -303,6 +303,10 @@
Bool_var interface{}
}
+type stringSoongConfigVars struct {
+ String_var interface{}
+}
+
func Test_PropertiesToApply(t *testing.T) {
mt, _ := newModuleType(&ModuleTypeProperties{
Module_type: "foo",
@@ -365,6 +369,155 @@
}
}
+func Test_PropertiesToApply_String_Error(t *testing.T) {
+ mt, _ := newModuleType(&ModuleTypeProperties{
+ Module_type: "foo",
+ Config_namespace: "bar",
+ Variables: []string{"string_var"},
+ Properties: []string{"a", "b"},
+ })
+ mt.Variables = append(mt.Variables, &stringVariable{
+ baseVariable: baseVariable{
+ variable: "string_var",
+ },
+ values: []string{"a", "b", "c"},
+ })
+ stringVarPositive := &properties{
+ A: proptools.StringPtr("A"),
+ B: true,
+ }
+ conditionsDefault := &properties{
+ A: proptools.StringPtr("default"),
+ B: false,
+ }
+ actualProps := &struct {
+ Soong_config_variables stringSoongConfigVars
+ }{
+ Soong_config_variables: stringSoongConfigVars{
+ String_var: &boolVarProps{
+ A: stringVarPositive.A,
+ B: stringVarPositive.B,
+ Conditions_default: conditionsDefault,
+ },
+ },
+ }
+ props := reflect.ValueOf(actualProps)
+
+ _, err := PropertiesToApply(mt, props, Config(map[string]string{
+ "string_var": "x",
+ }))
+ expected := `Soong config property "string_var" must be one of [a b c], found "x"`
+ if err == nil {
+ t.Fatalf("Expected an error, got nil")
+ } else if err.Error() != expected {
+ t.Fatalf("Error message was not correct, expected %q, got %q", expected, err.Error())
+ }
+}
+
+func Test_Bp2BuildSoongConfigDefinitionsAddVars(t *testing.T) {
+ testCases := []struct {
+ desc string
+ defs []*SoongConfigDefinition
+ expected Bp2BuildSoongConfigDefinitions
+ }{
+ {
+ desc: "non-overlapping",
+ defs: []*SoongConfigDefinition{
+ &SoongConfigDefinition{
+ ModuleTypes: map[string]*ModuleType{
+ "a": &ModuleType{
+ ConfigNamespace: "foo",
+ Variables: []soongConfigVariable{
+ &stringVariable{
+ baseVariable: baseVariable{"string_var"},
+ values: []string{"a", "b", "c"},
+ },
+ },
+ },
+ },
+ },
+ &SoongConfigDefinition{
+ ModuleTypes: map[string]*ModuleType{
+ "b": &ModuleType{
+ ConfigNamespace: "foo",
+ Variables: []soongConfigVariable{
+ &stringVariable{
+ baseVariable: baseVariable{"string_var"},
+ values: []string{"a", "b", "c"},
+ },
+ &boolVariable{baseVariable: baseVariable{"bool_var"}},
+ &valueVariable{baseVariable: baseVariable{"variable_var"}},
+ },
+ },
+ },
+ },
+ },
+ expected: Bp2BuildSoongConfigDefinitions{
+ StringVars: map[string]map[string]bool{
+ "foo__string_var": map[string]bool{"a": true, "b": true, "c": true},
+ },
+ BoolVars: map[string]bool{"foo__bool_var": true},
+ ValueVars: map[string]bool{"foo__variable_var": true},
+ },
+ },
+ {
+ desc: "overlapping",
+ defs: []*SoongConfigDefinition{
+ &SoongConfigDefinition{
+ ModuleTypes: map[string]*ModuleType{
+ "a": &ModuleType{
+ ConfigNamespace: "foo",
+ Variables: []soongConfigVariable{
+ &stringVariable{
+ baseVariable: baseVariable{"string_var"},
+ values: []string{"a", "b", "c"},
+ },
+ },
+ },
+ },
+ },
+ &SoongConfigDefinition{
+ ModuleTypes: map[string]*ModuleType{
+ "b": &ModuleType{
+ ConfigNamespace: "foo",
+ Variables: []soongConfigVariable{
+ &stringVariable{
+ baseVariable: baseVariable{"string_var"},
+ values: []string{"b", "c", "d"},
+ },
+ &boolVariable{baseVariable: baseVariable{"bool_var"}},
+ &valueVariable{baseVariable: baseVariable{"variable_var"}},
+ },
+ },
+ },
+ },
+ },
+ expected: Bp2BuildSoongConfigDefinitions{
+ StringVars: map[string]map[string]bool{
+ "foo__string_var": map[string]bool{"a": true, "b": true, "c": true, "d": true},
+ },
+ BoolVars: map[string]bool{"foo__bool_var": true},
+ ValueVars: map[string]bool{"foo__variable_var": true},
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ actual := &Bp2BuildSoongConfigDefinitions{}
+ for _, d := range tc.defs {
+ func(def *SoongConfigDefinition) {
+ actual.AddVars(def)
+ }(d)
+ }
+ if !reflect.DeepEqual(*actual, tc.expected) {
+ t.Errorf("Expected %#v, got %#v", tc.expected, *actual)
+ }
+ })
+ }
+
+}
+
func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
testCases := []struct {
desc string
@@ -407,11 +560,11 @@
soong_config_string_variables = {}`}, {
desc: "only string vars",
defs: Bp2BuildSoongConfigDefinitions{
- StringVars: map[string][]string{
- "string_var": []string{
- "choice1",
- "choice2",
- "choice3",
+ StringVars: map[string]map[string]bool{
+ "string_var": map[string]bool{
+ "choice1": true,
+ "choice2": true,
+ "choice3": true,
},
},
},
@@ -435,15 +588,15 @@
"value_var_one": true,
"value_var_two": true,
},
- StringVars: map[string][]string{
- "string_var_one": []string{
- "choice1",
- "choice2",
- "choice3",
+ StringVars: map[string]map[string]bool{
+ "string_var_one": map[string]bool{
+ "choice1": true,
+ "choice2": true,
+ "choice3": true,
},
- "string_var_two": []string{
- "foo",
- "bar",
+ "string_var_two": map[string]bool{
+ "foo": true,
+ "bar": true,
},
},
},
@@ -463,8 +616,8 @@
"choice3",
],
"string_var_two": [
- "foo",
"bar",
+ "foo",
],
}`},
}
diff --git a/android/test_asserts.go b/android/test_asserts.go
index 064f656..4143f15 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -160,6 +160,7 @@
// the value of the expected bool. If the expectation does not hold it reports an error prefixed with
// the supplied message and including a reason for why it failed.
func AssertStringListContainsEquals(t *testing.T, message string, list []string, s string, expected bool) {
+ t.Helper()
if expected {
AssertStringListContains(t, message, list, s)
} else {
diff --git a/android/test_config.go b/android/test_config.go
new file mode 100644
index 0000000..28d9ec4
--- /dev/null
+++ b/android/test_config.go
@@ -0,0 +1,156 @@
+// 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 (
+ "encoding/json"
+ "os"
+ "path/filepath"
+ "runtime"
+
+ "github.com/google/blueprint/proptools"
+)
+
+// TestConfig returns a Config object for testing.
+func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+ envCopy := make(map[string]string)
+ for k, v := range env {
+ envCopy[k] = v
+ }
+
+ // Copy the real PATH value to the test environment, it's needed by
+ // NonHermeticHostSystemTool() used in x86_darwin_host.go
+ envCopy["PATH"] = os.Getenv("PATH")
+
+ config := &config{
+ productVariables: productVariables{
+ 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,
+ soongOutDir: filepath.Join(buildDir, "soong"),
+ captureBuild: true,
+ env: envCopy,
+
+ // Set testAllowNonExistentPaths so that test contexts don't need to specify every path
+ // passed to PathForSource or PathForModuleSrc.
+ TestAllowNonExistentPaths: true,
+
+ BazelContext: noopBazelContext{},
+ BuildMode: BazelProdMode,
+ mixedBuildDisabledModules: make(map[string]struct{}),
+ mixedBuildEnabledModules: make(map[string]struct{}),
+ bazelForceEnabledModules: make(map[string]struct{}),
+ }
+ config.deviceConfig = &deviceConfig{
+ config: config,
+ }
+ config.TestProductVariables = &config.productVariables
+
+ config.mockFileSystem(bp, fs)
+
+ determineBuildOS(config)
+
+ return Config{config}
+}
+
+func modifyTestConfigToSupportArchMutator(testConfig Config) {
+ config := testConfig.config
+
+ config.Targets = map[OsType][]Target{
+ Android: []Target{
+ {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
+ {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
+ },
+ config.BuildOS: []Target{
+ {config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
+ {config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
+ },
+ }
+
+ // Make the CommonOS OsType available for all products.
+ config.Targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}
+
+ if runtime.GOOS == "darwin" {
+ config.Targets[config.BuildOS] = config.Targets[config.BuildOS][:1]
+ }
+
+ config.BuildOSTarget = config.Targets[config.BuildOS][0]
+ config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
+ config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
+ config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
+ config.TestProductVariables.DeviceArch = proptools.StringPtr("arm64")
+ config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a")
+ config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm")
+ config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon")
+}
+
+// ModifyTestConfigForMusl takes a Config returned by TestConfig and changes the host targets from glibc to musl.
+func ModifyTestConfigForMusl(config Config) {
+ delete(config.Targets, config.BuildOS)
+ config.productVariables.HostMusl = boolPtr(true)
+ determineBuildOS(config.config)
+ config.Targets[config.BuildOS] = []Target{
+ {config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
+ {config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
+ }
+
+ config.BuildOSTarget = config.Targets[config.BuildOS][0]
+ config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
+}
+
+func modifyTestConfigForMuslArm64HostCross(config Config) {
+ config.Targets[LinuxMusl] = append(config.Targets[LinuxMusl],
+ Target{config.BuildOS, Arch{ArchType: Arm64}, NativeBridgeDisabled, "", "", true})
+}
+
+// TestArchConfig returns a Config object suitable for using for tests that
+// need to run the arch mutator.
+func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+ testConfig := TestConfig(buildDir, env, bp, fs)
+ modifyTestConfigToSupportArchMutator(testConfig)
+ return testConfig
+}
+
+// CreateTestConfiguredJarList is a function to create ConfiguredJarList for tests.
+func CreateTestConfiguredJarList(list []string) ConfiguredJarList {
+ // Create the ConfiguredJarList in as similar way as it is created at runtime by marshalling to
+ // a json list of strings and then unmarshalling into a ConfiguredJarList instance.
+ b, err := json.Marshal(list)
+ if err != nil {
+ panic(err)
+ }
+
+ var jarList ConfiguredJarList
+ err = json.Unmarshal(b, &jarList)
+ if err != nil {
+ panic(err)
+ }
+
+ return jarList
+}
diff --git a/android/test_suites.go b/android/test_suites.go
index 55e1da7..b570b23 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -57,7 +57,7 @@
func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) WritablePath {
var installedPaths InstallPaths
- for _, module := range SortedStringKeys(files) {
+ for _, module := range SortedKeys(files) {
installedPaths = append(installedPaths, files[module]...)
}
testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases", false)
diff --git a/android/testing.go b/android/testing.go
index f5210d1..2a9c658 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -203,6 +203,10 @@
ctx.PreArchMutators(f)
}
+func (ctx *TestContext) ModuleProvider(m blueprint.Module, p blueprint.ProviderKey) interface{} {
+ return ctx.Context.ModuleProvider(m, p)
+}
+
func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) {
ctx.preDeps = append(ctx.preDeps, f)
}
@@ -215,8 +219,8 @@
ctx.finalDeps = append(ctx.finalDeps, f)
}
-func (ctx *TestContext) RegisterBp2BuildConfig(config bp2BuildConversionAllowlist) {
- ctx.config.bp2buildPackageConfig = config
+func (ctx *TestContext) RegisterBp2BuildConfig(config Bp2BuildConversionAllowlist) {
+ ctx.config.Bp2buildPackageConfig = config
}
// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
@@ -459,10 +463,16 @@
// RegisterForBazelConversion prepares a test context for bp2build conversion.
func (ctx *TestContext) RegisterForBazelConversion() {
- ctx.SetRunningAsBp2build()
+ ctx.config.BuildMode = Bp2build
RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch)
}
+// RegisterForApiBazelConversion prepares a test context for API bp2build conversion.
+func (ctx *TestContext) RegisterForApiBazelConversion() {
+ ctx.config.BuildMode = ApiBp2build
+ RegisterMutatorsForApiBazelConversion(ctx.Context, ctx.bp2buildPreArch)
+}
+
func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
// This function adapts the old style ParseFileList calls that are spread throughout the tests
// to the new style that takes a config.
@@ -803,6 +813,20 @@
return path.RelativeToTop()
}
+func allOutputs(p BuildParams) []string {
+ outputs := append(WritablePaths(nil), p.Outputs...)
+ outputs = append(outputs, p.ImplicitOutputs...)
+ if p.Output != nil {
+ outputs = append(outputs, p.Output)
+ }
+ return outputs.Strings()
+}
+
+// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
+func (p TestingBuildParams) AllOutputs() []string {
+ return allOutputs(p.BuildParams)
+}
+
// baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
type baseTestingComponent struct {
config Config
@@ -944,12 +968,7 @@
func (b baseTestingComponent) allOutputs() []string {
var outputFullPaths []string
for _, p := range b.provider.BuildParamsForTests() {
- outputs := append(WritablePaths(nil), p.Outputs...)
- outputs = append(outputs, p.ImplicitOutputs...)
- if p.Output != nil {
- outputs = append(outputs, p.Output)
- }
- outputFullPaths = append(outputFullPaths, outputs.Strings()...)
+ outputFullPaths = append(outputFullPaths, allOutputs(p)...)
}
return outputFullPaths
}
@@ -1079,7 +1098,7 @@
}
}
if !found {
- t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
+ t.Errorf("could not match the expected error regex %q (checked %d error(s))", pattern, len(errs))
for i, err := range errs {
t.Errorf("errs[%d] = %q", i, err)
}
@@ -1115,7 +1134,13 @@
config.katiEnabled = true
}
+func SetTrimmedApexEnabledForTests(config Config) {
+ config.productVariables.TrimmedApex = new(bool)
+ *config.productVariables.TrimmedApex = true
+}
+
func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
+ t.Helper()
var p AndroidMkEntriesProvider
var ok bool
if p, ok = mod.(AndroidMkEntriesProvider); !ok {
@@ -1130,10 +1155,11 @@
}
func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) AndroidMkData {
+ t.Helper()
var p AndroidMkDataProvider
var ok bool
if p, ok = mod.(AndroidMkDataProvider); !ok {
- t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
+ t.Fatalf("module does not implement AndroidMkDataProvider: " + mod.Name())
}
data := p.AndroidMk()
data.fillInData(ctx, mod)
diff --git a/apex/constants.go b/android/updatable_modules.go
similarity index 95%
rename from apex/constants.go
rename to android/updatable_modules.go
index c68edb7..6d0eeb7 100644
--- a/apex/constants.go
+++ b/android/updatable_modules.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package apex
+package android
// This file contains branch specific constants. They are stored in a separate
// file to minimise the potential of merge conflicts between branches when
@@ -33,4 +33,4 @@
// * AOSP - xx9990000
// * x-mainline-prod - xx9990000
// * master - 990090000
-const defaultManifestVersion = "339990000"
+const DefaultUpdatableModuleVersion = "340090000"
diff --git a/android/util.go b/android/util.go
index 47c4583..08a3521 100644
--- a/android/util.go
+++ b/android/util.go
@@ -26,12 +26,31 @@
// CopyOf returns a new slice that has the same contents as s.
func CopyOf(s []string) []string {
- return append([]string(nil), s...)
+ // If the input is nil, return nil and not an empty list
+ if s == nil {
+ return s
+ }
+ return append([]string{}, s...)
+}
+
+// Concat returns a new slice concatenated from the two input slices. It does not change the input
+// slices.
+func Concat[T any](s1, s2 []T) []T {
+ res := make([]T, 0, len(s1)+len(s2))
+ res = append(res, s1...)
+ res = append(res, s2...)
+ return res
}
// JoinWithPrefix prepends the prefix to each string in the list and
// returns them joined together with " " as separator.
func JoinWithPrefix(strs []string, prefix string) string {
+ return JoinWithPrefixAndSeparator(strs, prefix, " ")
+}
+
+// JoinWithPrefixAndSeparator prepends the prefix to each string in the list and
+// returns them joined together with the given separator.
+func JoinWithPrefixAndSeparator(strs []string, prefix string, sep string) string {
if len(strs) == 0 {
return ""
}
@@ -40,47 +59,40 @@
buf.WriteString(prefix)
buf.WriteString(strs[0])
for i := 1; i < len(strs); i++ {
- buf.WriteString(" ")
+ buf.WriteString(sep)
buf.WriteString(prefix)
buf.WriteString(strs[i])
}
return buf.String()
}
-// JoinWithSuffix appends the suffix to each string in the list and
-// returns them joined together with given separator.
-func JoinWithSuffix(strs []string, suffix string, separator string) string {
- if len(strs) == 0 {
- return ""
- }
-
- var buf strings.Builder
- buf.WriteString(strs[0])
- buf.WriteString(suffix)
- for i := 1; i < len(strs); i++ {
- buf.WriteString(separator)
- buf.WriteString(strs[i])
- buf.WriteString(suffix)
- }
- return buf.String()
+// SortedStringKeys returns the keys of the given map in the ascending order.
+//
+// Deprecated: Use SortedKeys instead.
+func SortedStringKeys[V any](m map[string]V) []string {
+ return SortedKeys(m)
}
-// 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))
- }
- if v.Len() == 0 {
+type Ordered interface {
+ ~string |
+ ~float32 | ~float64 |
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
+}
+
+// SortedKeys returns the keys of the given map in the ascending order.
+func SortedKeys[T Ordered, V any](m map[T]V) []T {
+ if len(m) == 0 {
return nil
}
- iter := v.MapRange()
- s := make([]string, 0, v.Len())
- for iter.Next() {
- s = append(s, iter.Key().String())
+ ret := make([]T, 0, len(m))
+ for k := range m {
+ ret = append(ret, k)
}
- sort.Strings(s)
- return s
+ sort.Slice(ret, func(i, j int) bool {
+ return ret[i] < ret[j]
+ })
+ return ret
}
// stringValues returns the values of the given string-valued map in randomized map order.
@@ -130,6 +142,38 @@
return IndexList(s, list) != -1
}
+func setFromList[T comparable](l []T) map[T]bool {
+ m := make(map[T]bool, len(l))
+ for _, t := range l {
+ m[t] = true
+ }
+ return m
+}
+
+// ListSetDifference checks if the two lists contain the same elements. It returns
+// a boolean which is true if there is a difference, and then returns lists of elements
+// that are in l1 but not l2, and l2 but not l1.
+func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) {
+ listsDiffer := false
+ diff1 := []T{}
+ diff2 := []T{}
+ m1 := setFromList(l1)
+ m2 := setFromList(l2)
+ for t := range m1 {
+ if _, ok := m2[t]; !ok {
+ diff1 = append(diff1, t)
+ listsDiffer = true
+ }
+ }
+ for t := range m2 {
+ if _, ok := m1[t]; !ok {
+ diff2 = append(diff2, t)
+ listsDiffer = true
+ }
+ }
+ return listsDiffer, diff1, diff2
+}
+
// Returns true if the given string s is prefixed with any string in the given prefix list.
func HasAnyPrefix(s string, prefixList []string) bool {
for _, prefix := range prefixList {
@@ -236,6 +280,8 @@
// FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
// each. It modifies the slice contents in place, and returns a subslice of the original slice.
func FirstUniqueStrings(list []string) []string {
+ // Do not moodify the input in-place, operate on a copy instead.
+ list = CopyOf(list)
// 128 was chosen based on BenchmarkFirstUniqueStrings results.
if len(list) > 128 {
return firstUniqueStringsMap(list)
@@ -292,6 +338,7 @@
// SortedUniqueStrings returns what the name says
func SortedUniqueStrings(list []string) []string {
+ // FirstUniqueStrings creates a copy of `list`, so the input remains untouched.
unique := FirstUniqueStrings(list)
sort.Strings(unique)
return unique
diff --git a/android/util_test.go b/android/util_test.go
index 9b9253b..a2ef589 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -381,6 +381,14 @@
}
}
+func TestCopyOfEmptyAndNil(t *testing.T) {
+ emptyList := []string{}
+ copyOfEmptyList := CopyOf(emptyList)
+ AssertBoolEquals(t, "Copy of an empty list should be an empty list and not nil", true, copyOfEmptyList != nil)
+ copyOfNilList := CopyOf(nil)
+ AssertBoolEquals(t, "Copy of a nil list should be a nil list and not an empty list", true, copyOfNilList == nil)
+}
+
func ExampleCopyOf() {
a := []string{"1", "2", "3"}
b := CopyOf(a)
@@ -641,42 +649,34 @@
}
}
-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"},
- },
- }
+func testSortedKeysHelper[K Ordered, V any](t *testing.T, name string, input map[K]V, expected []K) {
+ t.Helper()
+ t.Run(name, func(t *testing.T) {
+ actual := SortedKeys(input)
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("expected %v, got %v", expected, actual)
+ }
+ })
+}
- 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 TestSortedKeys(t *testing.T) {
+ testSortedKeysHelper(t, "simple", map[string]string{
+ "b": "bar",
+ "a": "foo",
+ }, []string{
+ "a",
+ "b",
+ })
+ testSortedKeysHelper(t, "ints", map[int]interface{}{
+ 10: nil,
+ 5: nil,
+ }, []int{
+ 5,
+ 10,
+ })
+
+ testSortedKeysHelper(t, "nil", map[string]string(nil), nil)
+ testSortedKeysHelper(t, "empty", map[string]string{}, nil)
}
func TestSortedStringValues(t *testing.T) {
diff --git a/android/variable.go b/android/variable.go
index d6a64d8..aaf0606 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -64,6 +64,12 @@
Enabled *bool `android:"arch_variant"`
} `android:"arch_variant"`
+ // similar to `Unbundled_build`, but `Always_use_prebuilt_sdks` means that it uses prebuilt
+ // sdk specifically.
+ Always_use_prebuilt_sdks struct {
+ Enabled *bool `android:"arch_variant"`
+ } `android:"arch_variant"`
+
Malloc_not_svelte struct {
Cflags []string `android:"arch_variant"`
Shared_libs []string `android:"arch_variant"`
@@ -193,6 +199,7 @@
Platform_sdk_extension_version *int `json:",omitempty"`
Platform_base_sdk_extension_version *int `json:",omitempty"`
Platform_version_active_codenames []string `json:",omitempty"`
+ Platform_version_all_preview_codenames []string `json:",omitempty"`
Platform_vndk_version *string `json:",omitempty"`
Platform_systemsdk_versions []string `json:",omitempty"`
Platform_security_patch *string `json:",omitempty"`
@@ -200,6 +207,7 @@
Platform_min_supported_target_sdk_version *string `json:",omitempty"`
Platform_base_os *string `json:",omitempty"`
Platform_version_last_stable *string `json:",omitempty"`
+ Platform_version_known_codenames *string `json:",omitempty"`
DeviceName *string `json:",omitempty"`
DeviceProduct *string `json:",omitempty"`
@@ -210,6 +218,7 @@
DeviceVndkVersion *string `json:",omitempty"`
DeviceCurrentApiLevelForVendorModules *string `json:",omitempty"`
DeviceSystemSdkVersions []string `json:",omitempty"`
+ DeviceMaxPageSizeSupported *string `json:",omitempty"`
RecoverySnapshotVersion *string `json:",omitempty"`
@@ -248,7 +257,8 @@
AAPTPreferredConfig *string `json:",omitempty"`
AAPTPrebuiltDPI []string `json:",omitempty"`
- DefaultAppCertificate *string `json:",omitempty"`
+ DefaultAppCertificate *string `json:",omitempty"`
+ MainlineSepolicyDevCertificates *string `json:",omitempty"`
AppsDefaultVersionName *string `json:",omitempty"`
@@ -297,6 +307,8 @@
MemtagHeapAsyncIncludePaths []string `json:",omitempty"`
MemtagHeapSyncIncludePaths []string `json:",omitempty"`
+ HWASanIncludePaths []string `json:",omitempty"`
+
VendorPath *string `json:",omitempty"`
OdmPath *string `json:",omitempty"`
ProductPath *string `json:",omitempty"`
@@ -334,8 +346,7 @@
NamespacesToExport []string `json:",omitempty"`
- AfdoAdditionalProfileDirs []string `json:",omitempty"`
- PgoAdditionalProfileDirs []string `json:",omitempty"`
+ PgoAdditionalProfileDirs []string `json:",omitempty"`
VndkUseCoreVariant *bool `json:",omitempty"`
VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
@@ -352,6 +363,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"`
@@ -377,6 +390,7 @@
Ndk_abis *bool `json:",omitempty"`
+ TrimmedApex *bool `json:",omitempty"`
Flatten_apex *bool `json:",omitempty"`
ForceApexSymlinkOptimization *bool `json:",omitempty"`
CompressedApex *bool `json:",omitempty"`
@@ -426,8 +440,13 @@
ShippingApiLevel *string `json:",omitempty"`
+ BuildBrokenClangAsFlags bool `json:",omitempty"`
+ BuildBrokenClangCFlags bool `json:",omitempty"`
+ BuildBrokenClangProperty bool `json:",omitempty"`
+ BuildBrokenDepfile *bool `json:",omitempty"`
BuildBrokenEnforceSyspropOwner bool `json:",omitempty"`
BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"`
+ BuildBrokenUsesSoongPython2Modules bool `json:",omitempty"`
BuildBrokenVendorPropertyNamespace bool `json:",omitempty"`
BuildBrokenInputDirModules []string `json:",omitempty"`
@@ -443,6 +462,17 @@
SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"`
GenerateAidlNdkPlatformBackend bool `json:",omitempty"`
+
+ IgnorePrefer32OnDevice bool `json:",omitempty"`
+
+ IncludeTags []string `json:",omitempty"`
+ SourceRootDirs []string `json:",omitempty"`
+
+ AfdoProfiles []string `json:",omitempty"`
+
+ ProductManufacturer string `json:",omitempty"`
+ ProductBrand string `json:",omitempty"`
+ BuildVersionTags []string `json:",omitempty"`
}
func boolPtr(v bool) *bool {
@@ -461,12 +491,14 @@
*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_version_all_preview_codenames: []string{"S"},
+ Platform_vndk_version: stringPtr("S"),
HostArch: stringPtr("x86_64"),
HostSecondaryArch: stringPtr("x86"),
@@ -480,6 +512,7 @@
DeviceSecondaryArchVariant: stringPtr("armv8-a"),
DeviceSecondaryCpuVariant: stringPtr("generic"),
DeviceSecondaryAbi: []string{"armeabi-v7a", "armeabi"},
+ DeviceMaxPageSizeSupported: stringPtr("4096"),
AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
AAPTPreferredConfig: stringPtr("xhdpi"),
@@ -490,6 +523,7 @@
Malloc_zero_contents: boolPtr(true),
Malloc_pattern_fill_contents: boolPtr(false),
Safestack: boolPtr(false),
+ TrimmedApex: boolPtr(false),
BootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
@@ -583,6 +617,9 @@
// "acme__board__soc_a", "acme__board__soc_b", and
// "acme__board__conditions_default"
FullConfig string
+
+ // keeps track of whether this product variable is nested under an arch variant
+ OuterAxis bazel.ConfigurationAxis
}
func (p *ProductConfigProperty) AlwaysEmit() bool {
@@ -591,11 +628,11 @@
func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
if p.Namespace == "" {
- return bazel.ProductVariableConfigurationAxis(p.FullConfig)
+ return bazel.ProductVariableConfigurationAxis(p.FullConfig, p.OuterAxis)
} else {
// Soong config variables can be uniquely identified by the namespace
// (e.g. acme, android) and the product variable name (e.g. board, size)
- return bazel.ProductVariableConfigurationAxis(p.Namespace + "__" + p.Name)
+ return bazel.ProductVariableConfigurationAxis(p.Namespace+"__"+p.Name, bazel.NoConfigAxis)
}
}
@@ -640,9 +677,8 @@
type ProductConfigProperties map[string]map[ProductConfigProperty]interface{}
// ProductVariableProperties returns a ProductConfigProperties containing only the properties which
-// have been set for the module in the given context.
-func ProductVariableProperties(ctx BazelConversionPathContext) ProductConfigProperties {
- module := ctx.Module()
+// have been set for the given module.
+func ProductVariableProperties(ctx ArchVariantContext, module Module) ProductConfigProperties {
moduleBase := module.base()
productConfigProperties := ProductConfigProperties{}
@@ -654,9 +690,11 @@
moduleBase.variableProperties,
"",
"",
- &productConfigProperties)
+ &productConfigProperties,
+ bazel.ConfigurationAxis{},
+ )
- for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
+ for axis, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
for config, props := range configToProps {
// GetArchVariantProperties is creating an instance of the requested type
// and productVariablesValues expects an interface, so no need to cast
@@ -665,7 +703,8 @@
props,
"",
config,
- &productConfigProperties)
+ &productConfigProperties,
+ axis)
}
}
}
@@ -678,130 +717,17 @@
namespacedVariableProp,
namespace,
"",
- &productConfigProperties)
+ &productConfigProperties,
+ bazel.NoConfigAxis)
}
}
}
- productConfigProperties.zeroValuesForNamespacedVariables()
-
return productConfigProperties
}
-// zeroValuesForNamespacedVariables ensures that selects that contain __only__
-// conditions default values have zero values set for the other non-default
-// values for that select statement.
-//
-// If the ProductConfigProperties map contains these items, as parsed from the .bp file:
-//
-// library_linking_strategy: {
-// prefer_static: {
-// static_libs: [
-// "lib_a",
-// "lib_b",
-// ],
-// },
-// conditions_default: {
-// shared_libs: [
-// "lib_a",
-// "lib_b",
-// ],
-// },
-// },
-//
-// Static_libs {Library_linking_strategy ANDROID prefer_static} [lib_a lib_b]
-// Shared_libs {Library_linking_strategy ANDROID conditions_default} [lib_a lib_b]
-//
-// We need to add this:
-//
-// Shared_libs {Library_linking_strategy ANDROID prefer_static} []
-//
-// so that the following gets generated for the "dynamic_deps" attribute,
-// instead of putting lib_a and lib_b directly into dynamic_deps without a
-// select:
-//
-// dynamic_deps = select({
-// "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
-// "//conditions:default": [
-// "//foo/bar:lib_a",
-// "//foo/bar:lib_b",
-// ],
-// }),
-func (props *ProductConfigProperties) zeroValuesForNamespacedVariables() {
- // A map of product config properties to the zero values of their respective
- // property value.
- zeroValues := make(map[ProductConfigProperty]interface{})
-
- // A map of prop names (e.g. cflags) to product config properties where the
- // (prop name, ProductConfigProperty) tuple contains a non-conditions_default key.
- //
- // e.g.
- //
- // prefer_static: {
- // static_libs: [
- // "lib_a",
- // "lib_b",
- // ],
- // },
- // conditions_default: {
- // shared_libs: [
- // "lib_a",
- // "lib_b",
- // ],
- // },
- //
- // The tuple of ("static_libs", prefer_static) would be in this map.
- hasNonDefaultValue := make(map[string]map[ProductConfigProperty]bool)
-
- // Iterate over all added soong config variables.
- for propName, v := range *props {
- for p, intf := range v {
- if p.Namespace == "" {
- // If there's no namespace, this isn't a soong config variable,
- // i.e. this is a product variable. product variables have no
- // conditions_defaults, so skip them.
- continue
- }
- if p.FullConfig == bazel.ConditionsDefaultConfigKey {
- // Skip conditions_defaults.
- continue
- }
- if hasNonDefaultValue[propName] == nil {
- hasNonDefaultValue[propName] = make(map[ProductConfigProperty]bool)
- hasNonDefaultValue[propName][p] = false
- }
- // Create the zero value of the variable.
- if _, exists := zeroValues[p]; !exists {
- zeroValue := reflect.Zero(reflect.ValueOf(intf).Type()).Interface()
- if zeroValue == nil {
- panic(fmt.Errorf("Expected non-nil zero value for product/config variable %+v\n", intf))
- }
- zeroValues[p] = zeroValue
- }
- hasNonDefaultValue[propName][p] = true
- }
- }
-
- for propName := range *props {
- for p, zeroValue := range zeroValues {
- // Ignore variables that already have a non-default value for that axis
- if exists, _ := hasNonDefaultValue[propName][p]; !exists {
- // fmt.Println(propName, p.Namespace, p.Name, p.FullConfig, zeroValue)
- // Insert the zero value for this propname + product config value.
- props.AddProductConfigProperty(
- propName,
- p.Namespace,
- p.Name,
- p.FullConfig,
- zeroValue,
- )
- }
- }
- }
-}
-
func (p *ProductConfigProperties) AddProductConfigProperty(
- propertyName, namespace, productVariableName, config string, property interface{}) {
+ propertyName, namespace, productVariableName, config string, property interface{}, outerAxis bazel.ConfigurationAxis) {
if (*p)[propertyName] == nil {
(*p)[propertyName] = make(map[ProductConfigProperty]interface{})
}
@@ -810,6 +736,7 @@
Namespace: namespace, // e.g. acme, android
Name: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
FullConfig: config, // e.g. size, feature1-x86, size__conditions_default
+ OuterAxis: outerAxis,
}
if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" {
@@ -860,7 +787,7 @@
return v, true
}
-func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value) {
+func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value, outerAxis bazel.ConfigurationAxis) {
// variableValues can either be a product_variables or
// soong_config_variables struct.
//
@@ -924,14 +851,16 @@
for j := 0; j < variableValue.NumField(); j++ {
property := variableValue.Field(j)
+ // e.g. Asflags, Cflags, Enabled, etc.
+ propertyName := variableValue.Type().Field(j).Name
+ // config can also be "conditions_default".
+ config := proptools.PropertyNameForField(propertyName)
+
// If the property wasn't set, no need to pass it along
if property.IsZero() {
continue
}
- // e.g. Asflags, Cflags, Enabled, etc.
- propertyName := variableValue.Type().Field(j).Name
-
if v, ok := maybeExtractConfigVarProp(property); ok {
// The field is a struct, which is used by:
// 1) soong_config_string_variables
@@ -951,13 +880,14 @@
// static_libs: ...
// }
field := v
+ // Iterate over fields of this struct prop.
for k := 0; k < field.NumField(); k++ {
- // Iterate over fields of this struct prop.
- if field.Field(k).IsZero() {
+ // For product variables, zero values are irrelevant; however, for soong config variables,
+ // empty values are relevant because there can also be a conditions default which is not
+ // applied for empty variables.
+ if field.Field(k).IsZero() && namespace == "" {
continue
}
- // config can also be "conditions_default".
- config := proptools.PropertyNameForField(propertyName)
actualPropertyName := field.Type().Field(k).Name
productConfigProperties.AddProductConfigProperty(
@@ -965,7 +895,8 @@
namespace, // e.g. acme, android
productVariableName, // e.g. size, feature1, FEATURE2, board
config,
- field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"]
+ field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"],
+ outerAxis,
)
}
} else if property.Kind() != reflect.Interface {
@@ -979,6 +910,7 @@
productVariableName,
config,
property.Interface(),
+ outerAxis,
)
}
}
@@ -989,14 +921,14 @@
// product_variables and soong_config_variables to structs that can be generated
// as select statements.
func productVariableValues(
- fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties) {
+ fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties, outerAxis bazel.ConfigurationAxis) {
if suffix != "" {
suffix = "-" + suffix
}
// variableValues represent the product_variables or soong_config_variables struct.
variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
- productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues)
+ productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues, outerAxis)
}
func VariableMutator(mctx BottomUpMutatorContext) {
diff --git a/android/visibility.go b/android/visibility.go
index b209599..3130135 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -47,7 +47,6 @@
// the dependency. If it cannot then an error is reported.
//
// TODO(b/130631145) - Make visibility work properly with prebuilts.
-// TODO(b/130796911) - Make visibility work properly with defaults.
// Patterns for the values that can be specified in visibility property.
const (
@@ -155,7 +154,11 @@
}
func isAncestor(p1 string, p2 string) bool {
- return strings.HasPrefix(p2+"/", p1+"/")
+ // Equivalent to strings.HasPrefix(p2+"/", p1+"/"), but without the string copies
+ // The check for a trailing slash is so that we don't consider sibling
+ // directories with common prefixes to be ancestors, e.g. "fooo/bar" should not be
+ // a descendant of "foo".
+ return strings.HasPrefix(p2, p1) && (len(p2) == len(p1) || p2[len(p1)] == '/')
}
func (r subpackagesRule) String() string {
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 280dae8..61058df 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -166,7 +166,7 @@
}
} else {
llvmStrip := config.ClangPath(ctx, "bin/llvm-strip")
- llvmLib := config.ClangPath(ctx, "lib64/libc++.so.1")
+ llvmLib := config.ClangPath(ctx, "lib/x86_64-unknown-linux-gnu/libc++.so.1")
for _, strip := range s.properties.Strip_files {
cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib)
if !ctx.Windows() {
diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go
index 9391117..be36859 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -234,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])
@@ -279,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/apex/Android.bp b/apex/Android.bp
index 312aadb..61d7fb2 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -14,6 +14,7 @@
"soong-cc",
"soong-filesystem",
"soong-java",
+ "soong-multitree",
"soong-provenance",
"soong-python",
"soong-rust",
@@ -25,9 +26,10 @@
"apex_sdk_member.go",
"apex_singleton.go",
"builder.go",
- "constants.go",
+ "bp2build.go",
"deapexer.go",
"key.go",
+ "metadata.go",
"prebuilt.go",
"testing.go",
"vndk.go",
@@ -36,6 +38,8 @@
"apex_test.go",
"bootclasspath_fragment_test.go",
"classpath_element_test.go",
+ "dexpreopt_bootjars_test.go",
+ "metadata_test.go",
"platform_bootclasspath_test.go",
"systemserver_classpath_fragment_test.go",
"vndk_test.go",
diff --git a/apex/OWNERS b/apex/OWNERS
deleted file mode 100644
index 8e4ba5c..0000000
--- a/apex/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file * = jiyong@google.com
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 938c8ed..684833d 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -23,8 +23,7 @@
"android/soong/android"
"android/soong/cc"
"android/soong/java"
-
- "github.com/google/blueprint/proptools"
+ "android/soong/rust"
)
func (a *apexBundle) AndroidMk() android.AndroidMkData {
@@ -64,21 +63,26 @@
}
// Return the full module name for a dependency module, which appends the apex module name unless re-using a system lib.
-func (a *apexBundle) fullModuleName(apexBundleName string, fi *apexFile) string {
- linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
-
+func (a *apexBundle) fullModuleName(apexBundleName string, linkToSystemLib bool, fi *apexFile) string {
if linkToSystemLib {
return fi.androidMkModuleName
}
return fi.androidMkModuleName + "." + apexBundleName + a.suffix
}
-func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string,
+// androidMkForFiles generates Make definitions for the contents of an
+// apexBundle (apexBundle#filesInfo). The filesInfo structure can either be
+// populated by Soong for unconverted APEXes, or Bazel in mixed mode. Use
+// apexFile#isBazelPrebuilt to differentiate.
+func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, moduleDir string,
apexAndroidMkData android.AndroidMkData) []string {
- // apexBundleName comes from the 'name' property; apexName comes from 'apex_name' property.
+ // apexBundleName comes from the 'name' property or soong module.
+ // apexName comes from 'name' property of apex_manifest.
// An apex is installed to /system/apex/<apexBundleName> and is activated at /apex/<apexName>
// In many cases, the two names are the same, but could be different in general.
+ // However, symbol files for apex files are installed under /apex/<apexBundleName> to avoid
+ // conflicts between two apexes with the same apexName.
moduleNames := []string{}
apexType := a.properties.ApexType
@@ -89,31 +93,11 @@
return moduleNames
}
- // b/162366062. Prevent GKI APEXes to emit make rules to avoid conflicts.
- if strings.HasPrefix(apexName, "com.android.gki.") && apexType != flattenedApex {
- return moduleNames
- }
-
- // 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>
- // as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files
- // for the overriding VNDK APEXes.
- symbolFilesNotNeeded := a.vndkApex && len(a.overridableProperties.Overrides) > 0
- if symbolFilesNotNeeded && apexType != flattenedApex {
- return moduleNames
- }
-
- // Avoid creating duplicate build rules for multi-installed APEXes.
- if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) {
- return moduleNames
- }
-
seenDataOutPaths := make(map[string]bool)
for _, fi := range a.filesInfo {
linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
-
- moduleName := a.fullModuleName(apexBundleName, &fi)
+ moduleName := a.fullModuleName(apexBundleName, linkToSystemLib, &fi)
// This name will be added to LOCAL_REQUIRED_MODULES of the APEX. We need to be
// arch-specific otherwise we will end up installing both ABIs even when only
@@ -134,29 +118,28 @@
continue
}
- fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle.files")
if fi.moduleDir != "" {
fmt.Fprintln(w, "LOCAL_PATH :=", fi.moduleDir)
} else {
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
}
fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
+
if fi.module != nil && fi.module.Owner() != "" {
fmt.Fprintln(w, "LOCAL_MODULE_OWNER :=", fi.module.Owner())
}
- // /apex/<apex_name>/{lib|framework|...}
- pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
+ // /apex/<apexBundleName>/{lib|framework|...}
+ pathForSymbol := filepath.Join("$(PRODUCT_OUT)", "apex", apexBundleName, fi.installDir)
var modulePath string
if apexType == flattenedApex {
- // /system/apex/<name>/{lib|framework|...}
+ // /system/apex/<apexBundleName>/{lib|framework|...}
modulePath = filepath.Join(a.installDir.String(), apexBundleName, fi.installDir)
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
- if a.primaryApexType && !symbolFilesNotNeeded {
- fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
+ if a.primaryApexType {
+ fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathForSymbol)
}
- if len(fi.symlinks) > 0 {
- fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS :=", strings.Join(fi.symlinks, " "))
- }
+ android.AndroidMkEmitAssignList(w, "LOCAL_MODULE_SYMLINKS", fi.symlinks)
newDataPaths := []android.DataPath{}
for _, path := range fi.dataPaths {
dataOutPath := modulePath + ":" + path.SrcPath.Rel()
@@ -165,16 +148,10 @@
seenDataOutPaths[dataOutPath] = true
}
}
- if len(newDataPaths) > 0 {
- fmt.Fprintln(w, "LOCAL_TEST_DATA :=", strings.Join(android.AndroidMkDataPaths(newDataPaths), " "))
- }
-
- if fi.module != nil && len(fi.module.NoticeFiles()) > 0 {
- fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(fi.module.NoticeFiles().Strings(), " "))
- }
+ android.AndroidMkEmitAssignList(w, "LOCAL_TEST_DATA", android.AndroidMkDataPaths(newDataPaths))
} else {
- modulePath = pathWhenActivated
- fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
+ modulePath = pathForSymbol
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
// We don't need to have notice file for the individual modules in it. Otherwise,
@@ -186,6 +163,7 @@
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake())
if fi.module != nil {
+ // This apexFile's module comes from Soong
archStr := fi.module.Target().Arch.ArchType.String()
host := false
switch fi.module.Target().Os.Class {
@@ -213,6 +191,9 @@
fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
}
+ } else if fi.isBazelPrebuilt && fi.arch != "" {
+ // This apexFile comes from Bazel
+ fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", fi.arch)
}
if fi.jacocoReportClassesFile != nil {
fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", fi.jacocoReportClassesFile.String())
@@ -240,9 +221,7 @@
// we will have foo.apk.apk
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.stem(), ".apk"))
if app, ok := fi.module.(*java.AndroidApp); ok {
- if jniCoverageOutputs := app.JniCoverageOutputs(); len(jniCoverageOutputs) > 0 {
- fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(jniCoverageOutputs.Strings(), " "))
- }
+ android.AndroidMkEmitAssignList(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE", app.JniCoverageOutputs().Strings())
if jniLibSymbols := app.JNISymbolsInstalls(modulePath); len(jniLibSymbols) > 0 {
fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_SYMBOLS :=", jniLibSymbols.String())
}
@@ -258,13 +237,21 @@
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
case nativeSharedLib, nativeExecutable, nativeTest:
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
- if ccMod, ok := fi.module.(*cc.Module); ok {
- if ccMod.UnstrippedOutputFile() != nil {
- fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String())
- }
- ccMod.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
- if ccMod.CoverageOutputFile().Valid() {
- fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String())
+ if fi.isBazelPrebuilt {
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", fi.unstrippedBuiltFile)
+ } else {
+ if ccMod, ok := fi.module.(*cc.Module); ok {
+ if ccMod.UnstrippedOutputFile() != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String())
+ }
+ ccMod.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
+ if ccMod.CoverageOutputFile().Valid() {
+ fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String())
+ }
+ } else if rustMod, ok := fi.module.(*rust.Module); ok {
+ if rustMod.UnstrippedOutputFile() != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", rustMod.UnstrippedOutputFile().String())
+ }
}
}
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk")
@@ -279,7 +266,7 @@
}
for _, name := range commonProperties {
if value, ok := apexAndroidMkData.Entries.EntryMap[name]; ok {
- fmt.Fprintln(w, name+" := "+strings.Join(value, " "))
+ android.AndroidMkEmitAssignList(w, name, value)
}
}
@@ -289,9 +276,7 @@
for _, o := range a.overridableProperties.Overrides {
patterns = append(patterns, "%."+o+a.suffix)
}
- if len(patterns) > 0 {
- fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
- }
+ android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", patterns)
}
// File_contexts of flattened APEXes should be merged into file_contexts.bin
@@ -310,13 +295,6 @@
}
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
@@ -328,16 +306,9 @@
targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
}
-
- if len(required) > 0 {
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(required, " "))
- }
- if len(targetRequired) > 0 {
- fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES +=", strings.Join(targetRequired, " "))
- }
- if len(hostRequired) > 0 {
- fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
- }
+ android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.makeModulesToInstall, required)
+ android.AndroidMkEmitAssignList(w, "LOCAL_TARGET_REQUIRED_MODULES", targetRequired)
+ android.AndroidMkEmitAssignList(w, "LOCAL_HOST_REQUIRED_MODULES", hostRequired)
}
func (a *apexBundle) androidMkForType() android.AndroidMkData {
@@ -346,13 +317,12 @@
moduleNames := []string{}
apexType := a.properties.ApexType
if a.installable() {
- apexName := proptools.StringDefault(a.properties.Apex_name, name)
- moduleNames = a.androidMkForFiles(w, name, apexName, moduleDir, data)
+ moduleNames = a.androidMkForFiles(w, name, moduleDir, data)
}
if apexType == flattenedApex {
// Only image APEXes can be flattened.
- fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle.flat")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
data.Entries.WriteLicenseVariables(w)
@@ -360,7 +330,7 @@
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
} else {
- fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
data.Entries.WriteLicenseVariables(w)
@@ -387,13 +357,11 @@
}
for _, name := range commonProperties {
if value, ok := data.Entries.EntryMap[name]; ok {
- fmt.Fprintln(w, name+" := "+strings.Join(value, " "))
+ android.AndroidMkEmitAssignList(w, name, value)
}
}
- if len(a.overridableProperties.Overrides) > 0 {
- fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(a.overridableProperties.Overrides, " "))
- }
+ android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", a.overridableProperties.Overrides)
a.writeRequiredModules(w, moduleNames)
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
@@ -401,10 +369,7 @@
if apexType == imageApex {
fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).BUNDLE :=", a.bundleModuleFile.String())
}
- if len(a.lintReports) > 0 {
- fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).LINT_REPORTS :=",
- strings.Join(a.lintReports.Strings(), " "))
- }
+ android.AndroidMkEmitAssignList(w, "ALL_MODULES.$(my_register_name).LINT_REPORTS", a.lintReports.Strings())
if a.installedFilesFile != nil {
goal := "checkbuild"
diff --git a/apex/apex.go b/apex/apex.go
index 132b2b2..c1c9e5c 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -23,6 +23,8 @@
"sort"
"strings"
+ "android/soong/bazel/cquery"
+
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools"
@@ -34,6 +36,7 @@
prebuilt_etc "android/soong/etc"
"android/soong/filesystem"
"android/soong/java"
+ "android/soong/multitree"
"android/soong/python"
"android/soong/rust"
"android/soong/sh"
@@ -45,11 +48,11 @@
func registerApexBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("apex", BundleFactory)
- ctx.RegisterModuleType("apex_test", testApexBundleFactory)
+ ctx.RegisterModuleType("apex_test", TestApexBundleFactory)
ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
- ctx.RegisterModuleType("apex_defaults", defaultsFactory)
+ 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)
@@ -77,6 +80,7 @@
ctx.BottomUp("apex", apexMutator).Parallel()
ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+ ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
// Register after apex_info mutator so that it can use ApexVariationName
ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel()
}
@@ -90,27 +94,40 @@
// a default one is automatically generated.
AndroidManifest *string `android:"path"`
- // Canonical name of this APEX bundle. Used to determine the path to the activated APEX on
- // device (/apex/<apex_name>). If unspecified, follows the name property.
- Apex_name *string
-
// Determines the file contexts file for setting the security contexts to files in this APEX
// bundle. For platform APEXes, this should points to a file under /system/sepolicy Default:
// /system/sepolicy/apex/<module_name>_file_contexts.
File_contexts *string `android:"path"`
- // Path to the canned fs config file for customizing file's uid/gid/mod/capabilities. The
- // format is /<path_or_glob> <uid> <gid> <mode> [capabilities=0x<cap>], where path_or_glob is a
- // path or glob pattern for a file or set of files, uid/gid are numerial values of user ID
- // and group ID, mode is octal value for the file mode, and cap is hexadecimal value for the
- // capability. If this property is not set, or a file is missing in the file, default config
- // is used.
+ // By default, file_contexts is amended by force-labelling / and /apex_manifest.pb as system_file
+ // to avoid mistakes. When set as true, no force-labelling.
+ Use_file_contexts_as_is *bool
+
+ // Path to the canned fs config file for customizing file's
+ // uid/gid/mod/capabilities. The content of this file is appended to the
+ // default config, so that the custom entries are preferred. The format is
+ // /<path_or_glob> <uid> <gid> <mode> [capabilities=0x<cap>], where
+ // path_or_glob is a path or glob pattern for a file or set of files,
+ // uid/gid are numerial values of user ID and group ID, mode is octal value
+ // for the file mode, and cap is hexadecimal value for the capability.
Canned_fs_config *string `android:"path"`
ApexNativeDependencies
Multilib apexMultilibProperties
+ // List of runtime resource overlays (RROs) that are embedded inside this APEX.
+ Rros []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
+
// List of sh binaries that are embedded inside this APEX bundle.
Sh_binaries []string
@@ -120,6 +137,10 @@
// List of filesystem images that are embedded inside this APEX bundle.
Filesystems []string
+ // The minimum SDK version that this APEX must support at minimum. This is usually set to
+ // the SDK version that the APEX was first introduced.
+ Min_sdk_version *string
+
// Whether this APEX is considered updatable or not. When set to true, this will enforce
// additional rules for making sure that the APEX is truly updatable. To be updatable,
// min_sdk_version should be set as well. This will also disable the size optimizations like
@@ -145,16 +166,6 @@
// Should be only used in non-system apexes (e.g. vendor: true). Default is false.
Use_vndk_as_stable *bool
- // Whether this is multi-installed APEX should skip installing symbol files.
- // Multi-installed APEXes share the same apex_name and are installed at the same time.
- // Default is false.
- //
- // Should be set to true for all multi-installed APEXes except the singular
- // default version within the multi-installed group.
- // Only the default version can install symbol files in $(PRODUCT_OUT}/apex,
- // or else conflicting build rules may be created.
- Multi_install_skip_symbol_files *bool
-
// 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
@@ -336,21 +347,9 @@
// List of prebuilt files that are embedded inside this APEX bundle.
Prebuilts []string
- // List of runtime resource overlays (RROs) that are embedded inside this APEX.
- Rros []string
-
// 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
@@ -383,9 +382,8 @@
// Default: false.
Compressible *bool
- // The minimum SDK version that this APEX must support at minimum. This is usually set to
- // the SDK version that the APEX was first introduced.
- Min_sdk_version *string
+ // Trim against a specific Dynamic Common Lib APEX
+ Trim_against *string
}
type apexBundle struct {
@@ -393,8 +391,8 @@
android.ModuleBase
android.DefaultableModuleBase
android.OverridableModuleBase
- android.SdkBase
android.BazelModuleBase
+ multitree.ExportableModuleBase
// Properties
properties apexBundleProperties
@@ -436,8 +434,8 @@
// GenerateAndroidBuildActions.
filesInfo []apexFile
- // List of other module names that should be installed when this APEX gets installed.
- requiredDeps []string
+ // List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES).
+ makeModulesToInstall []string
///////////////////////////////////////////////////////////////////////////////////////////
// Outputs (final and intermediates)
@@ -451,9 +449,6 @@
// Processed file_contexts files
fileContexts android.WritablePath
- // 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
@@ -488,8 +483,6 @@
// Optional list of lint report zip files for apexes that contain java or app modules
lintReports android.Paths
- prebuiltFileToDelete string
-
isCompressed bool
// Path of API coverage generate file
@@ -517,6 +510,21 @@
shBinary
)
+var (
+ classes = map[string]apexFileClass{
+ "app": app,
+ "appSet": appSet,
+ "etc": etc,
+ "goBinary": goBinary,
+ "javaSharedLib": javaSharedLib,
+ "nativeExecutable": nativeExecutable,
+ "nativeSharedLib": nativeSharedLib,
+ "nativeTest": nativeTest,
+ "pyBinary": pyBinary,
+ "shBinary": shBinary,
+ }
+)
+
// apexFile represents a file in an APEX bundle. This is created during the first half of
// GenerateAndroidBuildActions by traversing the dependencies of the APEX. Then in the second half
// of the function, this is used to create commands that copies the files into a staging directory,
@@ -526,6 +534,7 @@
// buildFile is put in the installDir inside the APEX.
builtFile android.Path
installDir string
+ partition string
customStem string
symlinks []string // additional symlinks
@@ -550,6 +559,10 @@
multilib string
+ isBazelPrebuilt bool
+ unstrippedBuiltFile android.Path
+ arch string
+
// TODO(jiyong): remove this
module android.Module
}
@@ -565,6 +578,7 @@
}
if module != nil {
ret.moduleDir = ctx.OtherModuleDir(module)
+ ret.partition = module.PartitionTag(ctx.DeviceConfig())
ret.requiredModuleNames = module.RequiredModuleNames()
ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
@@ -675,6 +689,7 @@
androidAppTag = &dependencyTag{name: "androidApp", payload: true}
bpfTag = &dependencyTag{name: "bpf", payload: true}
certificateTag = &dependencyTag{name: "certificate"}
+ dclaTag = &dependencyTag{name: "dcla"}
executableTag = &dependencyTag{name: "executable", payload: true}
fsTag = &dependencyTag{name: "filesystem", payload: true}
bcpfTag = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
@@ -733,12 +748,14 @@
}
}
-// getImageVariation returns the image variant name for this apexBundle. In most cases, it's simply
-// android.CoreVariation, but gets complicated for the vendor APEXes and the VNDK APEX.
-func (a *apexBundle) getImageVariation(ctx android.BottomUpMutatorContext) string {
- deviceConfig := ctx.DeviceConfig()
+// getImageVariationPair returns a pair for the image variation name as its
+// prefix and suffix. The prefix indicates whether it's core/vendor/product and the
+// suffix indicates the vndk version when it's vendor or product.
+// getImageVariation can simply join the result of this function to get the
+// image variation name.
+func (a *apexBundle) getImageVariationPair(deviceConfig android.DeviceConfig) (string, string) {
if a.vndkApex {
- return cc.VendorVariationPrefix + a.vndkVersion(deviceConfig)
+ return cc.VendorVariationPrefix, a.vndkVersion(deviceConfig)
}
var prefix string
@@ -756,10 +773,17 @@
vndkVersion = deviceConfig.PlatformVndkVersion()
}
if vndkVersion != "" {
- return prefix + vndkVersion
+ return prefix, vndkVersion
}
- return android.CoreVariation // The usual case
+ return android.CoreVariation, "" // The usual case
+}
+
+// getImageVariation returns the image variant name for this apexBundle. In most cases, it's simply
+// android.CoreVariation, but gets complicated for the vendor APEXes and the VNDK APEX.
+func (a *apexBundle) getImageVariation(ctx android.BottomUpMutatorContext) string {
+ prefix, vndkVersion := a.getImageVariationPair(ctx.DeviceConfig())
+ return prefix + vndkVersion
}
func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -847,6 +871,10 @@
// Common-arch dependencies come next
commonVariation := ctx.Config().AndroidCommonTarget.Variations()
+ ctx.AddFarVariationDependencies(commonVariation, rroTag, a.properties.Rros...)
+ 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...)
}
@@ -860,10 +888,6 @@
commonVariation := ctx.Config().AndroidCommonTarget.Variations()
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
@@ -899,6 +923,33 @@
}
}
+func apexDCLADepsMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Config().ApexTrimEnabled() {
+ return
+ }
+ if a, ok := mctx.Module().(*apexBundle); ok && a.overridableProperties.Trim_against != nil {
+ commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+ mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(a.overridableProperties.Trim_against))
+ } else if o, ok := mctx.Module().(*OverrideApex); ok {
+ for _, p := range o.GetProperties() {
+ properties, ok := p.(*overridableProperties)
+ if !ok {
+ continue
+ }
+ if properties.Trim_against != nil {
+ commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+ mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(properties.Trim_against))
+ }
+ }
+ }
+}
+
+type DCLAInfo struct {
+ ProvidedLibs []string
+}
+
+var DCLAInfoProvider = blueprint.NewMutatorProvider(DCLAInfo{}, "apex_info")
+
type ApexBundleInfo struct {
Contents *android.ApexContents
}
@@ -943,6 +994,9 @@
if !useVndk {
mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
}
+ if a.minSdkVersionValue(mctx) != "" {
+ mctx.PropertyErrorf("use_vndk_as_stable", "not supported when min_sdk_version is set")
+ }
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())
@@ -1008,7 +1062,7 @@
// This is the main part of this mutator. Mark the collected dependencies that they need to
// be built for this apexBundle.
- apexVariationName := proptools.StringDefault(a.properties.Apex_name, mctx.ModuleName()) // could be com.android.foo
+ apexVariationName := mctx.ModuleName() // could be com.android.foo
a.properties.ApexVariationName = apexVariationName
apexInfo := android.ApexInfo{
ApexVariationName: apexVariationName,
@@ -1026,6 +1080,12 @@
child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
return true
})
+
+ if a.dynamic_common_lib_apex() {
+ mctx.SetProvider(DCLAInfoProvider, DCLAInfo{
+ ProvidedLibs: a.properties.Native_shared_libs,
+ })
+ }
}
type ApexInfoMutator interface {
@@ -1397,7 +1457,7 @@
var _ android.DepIsInSameApex = (*apexBundle)(nil)
// Implements android.DepInInSameApex
-func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+func (a *apexBundle) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
// direct deps of an APEX bundle are all part of the APEX bundle
// TODO(jiyong): shouldn't we look into the payload field of the dependencyTag?
return true
@@ -1422,6 +1482,21 @@
}
}
+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
@@ -1507,6 +1582,19 @@
return proptools.BoolDefault(a.properties.Dynamic_common_lib_apex, false)
}
+// See the list of libs to trim
+func (a *apexBundle) libs_to_trim(ctx android.ModuleContext) []string {
+ dclaModules := ctx.GetDirectDepsWithTag(dclaTag)
+ if len(dclaModules) > 1 {
+ panic(fmt.Errorf("expected exactly at most one dcla dependency, got %d", len(dclaModules)))
+ }
+ if len(dclaModules) > 0 {
+ DCLAInfo := ctx.OtherModuleProvider(dclaModules[0], DCLAInfoProvider).(DCLAInfo)
+ return DCLAInfo.ProvidedLibs
+ }
+ return []string{}
+}
+
// These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
// members) can be sanitized, either forcibly, or by the global configuration. For some of the
// sanitizers, extra dependencies can be forcibly added as well.
@@ -1517,19 +1605,19 @@
}
}
-func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool {
+func (a *apexBundle) IsSanitizerEnabled(config android.Config, sanitizerName string) bool {
if android.InList(sanitizerName, a.properties.SanitizerNames) {
return true
}
// Then follow the global setting
- globalSanitizerNames := []string{}
+ var globalSanitizerNames []string
if a.Host() {
- globalSanitizerNames = ctx.Config().SanitizeHost()
+ globalSanitizerNames = config.SanitizeHost()
} else {
- arches := ctx.Config().SanitizeDeviceArch()
+ arches := config.SanitizeDeviceArch()
if len(arches) == 0 || android.InList(a.Arch().ArchType.Name, arches) {
- globalSanitizerNames = ctx.Config().SanitizeDevice()
+ globalSanitizerNames = config.SanitizeDevice()
}
}
return android.InList(sanitizerName, globalSanitizerNames)
@@ -1571,7 +1659,6 @@
if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
}
- dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
// Special case for Bionic libs and other libs installed with them. This is to
// prevent those libs from being included in the search path
@@ -1584,8 +1671,12 @@
// loading of them, which isn't supported.
dirInApex = filepath.Join(dirInApex, "bionic")
}
+ // This needs to go after the runtime APEX handling because otherwise we would get
+ // weird paths like lib64/rel_install_path/bionic rather than
+ // lib64/bionic/rel_install_path.
+ dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
- fileToCopy := ccMod.OutputFile().Path()
+ fileToCopy := android.OutputFileForModule(ctx, ccMod, "")
androidMkModuleName := ccMod.BaseModuleName() + ccMod.Properties.SubName
return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, ccMod)
}
@@ -1596,7 +1687,7 @@
dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
}
dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
- fileToCopy := cc.OutputFile().Path()
+ fileToCopy := android.OutputFileForModule(ctx, cc, "")
androidMkModuleName := cc.BaseModuleName() + cc.Properties.SubName
af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, cc)
af.symlinks = cc.Symlinks()
@@ -1609,7 +1700,7 @@
if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
}
- fileToCopy := rustm.OutputFile().Path()
+ fileToCopy := android.OutputFileForModule(ctx, rustm, "")
androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, rustm)
return af
@@ -1628,12 +1719,12 @@
if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
}
- fileToCopy := rustm.OutputFile().Path()
+ fileToCopy := android.OutputFileForModule(ctx, rustm, "")
androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
}
-func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
+func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.PythonBinaryModule) apexFile {
dirInApex := "bin"
fileToCopy := py.HostToolPath().Path()
return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
@@ -1645,6 +1736,7 @@
// NB: Since go binaries are static we don't need the module for anything here, which is
// good since the go tool is a blueprint.Module not an android.Module like we would
// normally use.
+ //
return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil)
}
@@ -1708,6 +1800,18 @@
return af
}
+func apexFileForJavaModuleProfile(ctx android.BaseModuleContext, module javaModule) *apexFile {
+ if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
+ if profilePathOnHost := dexpreopter.OutputProfilePathOnHost(); profilePathOnHost != nil {
+ dirInApex := "javalib"
+ af := newApexFile(ctx, profilePathOnHost, module.BaseModuleName()+"-profile", dirInApex, etc, nil)
+ af.customStem = module.Stem() + ".jar.prof"
+ return &af
+ }
+ }
+ return nil
+}
+
// androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
// the same way.
type androidApp interface {
@@ -1839,450 +1943,142 @@
}
}
-// Creates build rules for an APEX. It consists of the following major steps:
-//
-// 1) do some validity checks such as apex_available, min_sdk_version, etc.
-// 2) traverse the dependency tree to collect apexFile structs from them.
-// 3) some fields in apexBundle struct are configured
-// 4) generate the build rules to create the APEX. This is mostly done in builder.go.
-func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- ////////////////////////////////////////////////////////////////////////////////////////////
- // 1) do some validity checks such as apex_available, min_sdk_version, etc.
- a.checkApexAvailability(ctx)
- a.checkUpdatable(ctx)
- a.CheckMinSdkVersion(ctx)
- a.checkStaticLinkingToStubLibraries(ctx)
- a.checkStaticExecutables(ctx)
- if len(a.properties.Tests) > 0 && !a.testApex {
- ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
+var _ android.MixedBuildBuildable = (*apexBundle)(nil)
+
+func (a *apexBundle) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+ return a.properties.ApexType == imageApex
+}
+
+func (a *apexBundle) QueueBazelCall(ctx android.BaseModuleContext) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(a.GetBazelLabel(ctx, a), cquery.GetApexInfo, android.GetConfigKey(ctx))
+}
+
+// GetBazelLabel returns the bazel label of this apexBundle, or the label of the
+// override_apex module overriding this apexBundle. An apexBundle can be
+// overridden by different override_apex modules (e.g. Google or Go variants),
+// which is handled by the overrides mutators.
+func (a *apexBundle) GetBazelLabel(ctx android.BazelConversionPathContext, module blueprint.Module) string {
+ if _, ok := ctx.Module().(android.OverridableModule); ok {
+ return android.MaybeBp2buildLabelOfOverridingModule(ctx)
+ }
+ return a.BazelModuleBase.GetBazelLabel(ctx, a)
+}
+
+func (a *apexBundle) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+ if !a.commonBuildActions(ctx) {
return
}
- ////////////////////////////////////////////////////////////////////////////////////////////
- // 2) traverse the dependency tree to collect apexFile structs from them.
+ a.setApexTypeAndSuffix(ctx)
+ a.setPayloadFsType(ctx)
+ a.setSystemLibLink(ctx)
- // all the files that will be included in this APEX
- var filesInfo []apexFile
+ if a.properties.ApexType != zipApex {
+ a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType)
+ }
- // native lib dependencies
- var provideNativeLibs []string
- var requireNativeLibs []string
-
- handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
-
- // Collect the module directory for IDE info in java/jdeps.go.
- a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
-
- // TODO(jiyong): do this using WalkPayloadDeps
- // TODO(jiyong): make this clean!!!
- ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
- depTag := ctx.OtherModuleDependencyTag(child)
- if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
- return false
- }
- if mod, ok := child.(android.Module); ok && !mod.Enabled() {
- return false
- }
- depName := ctx.OtherModuleName(child)
- if _, isDirectDep := parent.(*apexBundle); isDirectDep {
- switch depTag {
- case sharedLibTag, jniLibTag:
- isJniLib := depTag == jniLibTag
- if c, ok := child.(*cc.Module); ok {
- fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
- fi.isJniLib = isJniLib
- filesInfo = append(filesInfo, fi)
- // Collect the list of stub-providing libs except:
- // - VNDK libs are only for vendors
- // - bootstrap bionic libs are treated as provided by system
- if c.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
- provideNativeLibs = append(provideNativeLibs, fi.stem())
- }
- return true // track transitive dependencies
- } else if r, ok := child.(*rust.Module); ok {
- fi := apexFileForRustLibrary(ctx, r)
- fi.isJniLib = isJniLib
- filesInfo = append(filesInfo, fi)
- return true // track transitive dependencies
- } else {
- propertyName := "native_shared_libs"
- if isJniLib {
- propertyName = "jni_libs"
- }
- ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
- }
- case executableTag:
- if cc, ok := child.(*cc.Module); ok {
- filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
- return true // track transitive dependencies
- } else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
- filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
- } else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
- filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
- } else if rust, ok := child.(*rust.Module); ok {
- filesInfo = append(filesInfo, apexFileForRustExecutable(ctx, rust))
- return true // track transitive dependencies
- } else {
- ctx.PropertyErrorf("binaries", "%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, nor (host) bootstrap_go_binary", depName)
- }
- case shBinaryTag:
- if sh, ok := child.(*sh.ShBinary); ok {
- filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
- } else {
- ctx.PropertyErrorf("sh_binaries", "%q is not a sh_binary module", depName)
- }
- case bcpfTag:
- {
- bcpfModule, ok := child.(*java.BootclasspathFragmentModule)
- if !ok {
- ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
- return false
- }
-
- filesToAdd := apexBootclasspathFragmentFiles(ctx, child)
- filesInfo = append(filesInfo, filesToAdd...)
- for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
- a.requiredDeps = append(a.requiredDeps, makeModuleName)
- }
- return true
- }
- case sscpfTag:
- {
- if _, ok := child.(*java.SystemServerClasspathModule); !ok {
- ctx.PropertyErrorf("systemserverclasspath_fragments", "%q is not a systemserverclasspath_fragment module", depName)
- return false
- }
- if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
- filesInfo = append(filesInfo, *af)
- }
- return true
- }
- case javaLibTag:
- switch child.(type) {
- case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
- af := apexFileForJavaModule(ctx, child.(javaModule))
- if !af.ok() {
- ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
- return false
- }
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- default:
- ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
- }
- case androidAppTag:
- if ap, ok := child.(*java.AndroidApp); ok {
- filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
- return true // track transitive dependencies
- } else if ap, ok := child.(*java.AndroidAppImport); ok {
- filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
- } else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
- filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
- } else if ap, ok := child.(*java.AndroidAppSet); ok {
- appDir := "app"
- if ap.Privileged() {
- appDir = "priv-app"
- }
- // 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 {
- ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
- }
- case rroTag:
- if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
- filesInfo = append(filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
- } else {
- ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
- }
- case bpfTag:
- if bpfProgram, ok := child.(bpf.BpfModule); ok {
- filesToCopy, _ := bpfProgram.OutputFiles("")
- apex_sub_dir := bpfProgram.SubDir()
- for _, bpfFile := range filesToCopy {
- filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
- }
- } else {
- ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
- }
- case fsTag:
- if fs, ok := child.(filesystem.Filesystem); ok {
- filesInfo = append(filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
- } else {
- ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
- }
- case prebuiltTag:
- if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
- filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
- } else {
- ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
- }
- case compatConfigTag:
- if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok {
- filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName))
- } else {
- ctx.PropertyErrorf("compat_configs", "%q is not a platform_compat_config module", depName)
- }
- case testTag:
- if ccTest, ok := child.(*cc.Module); ok {
- if ccTest.IsTestPerSrcAllTestsVariation() {
- // Multiple-output test module (where `test_per_src: true`).
- //
- // `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
- // We do not add this variation to `filesInfo`, as it has no output;
- // however, we do add the other variations of this module as indirect
- // dependencies (see below).
- } else {
- // Single-output test module (where `test_per_src: false`).
- af := apexFileForExecutable(ctx, ccTest)
- af.class = nativeTest
- filesInfo = append(filesInfo, af)
- }
- return true // track transitive dependencies
- } else {
- ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
- }
- case keyTag:
- if key, ok := child.(*apexKey); ok {
- a.privateKeyFile = key.privateKeyFile
- a.publicKeyFile = key.publicKeyFile
- } else {
- ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
- }
- return false
- case certificateTag:
- if dep, ok := child.(*java.AndroidAppCertificate); ok {
- a.containerCertificateFile = dep.Certificate.Pem
- a.containerPrivateKeyFile = dep.Certificate.Key
- } else {
- ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
- }
- case android.PrebuiltDepTag:
- // If the prebuilt is force disabled, remember to delete the prebuilt file
- // that might have been installed in the previous builds
- if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
- a.prebuiltFileToDelete = prebuilt.InstallFilename()
- }
- }
- } else if !a.vndkApex {
- // indirect dependencies
- if am, ok := child.(android.ApexModule); ok {
- // We cannot use a switch statement on `depTag` here as the checked
- // tags used below are private (e.g. `cc.sharedDepTag`).
- if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
- if cc, ok := child.(*cc.Module); ok {
- if cc.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && cc.IsVndk() {
- requireNativeLibs = append(requireNativeLibs, ":vndk")
- return false
- }
- af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
- af.transitiveDep = true
-
- // Always track transitive dependencies for host.
- if a.Host() {
- filesInfo = append(filesInfo, af)
- return true
- }
-
- abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
- if !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
- // If the dependency is a stubs lib, don't include it in this APEX,
- // but make sure that the lib is installed on the device.
- // In case no APEX is having the lib, the lib is installed to the system
- // partition.
- //
- // Always include if we are a host-apex however since those won't have any
- // system libraries.
- if !am.DirectlyInAnyApex() {
- // we need a module name for Make
- name := cc.ImplementationModuleNameForMake(ctx) + cc.Properties.SubName
- if !android.InList(name, a.requiredDeps) {
- a.requiredDeps = append(a.requiredDeps, name)
- }
- }
- requireNativeLibs = append(requireNativeLibs, af.stem())
- // Don't track further
- return false
- }
-
- // If the dep is not considered to be in the same
- // apex, don't add it to filesInfo so that it is not
- // included in this APEX.
- // TODO(jiyong): move this to at the top of the
- // else-if clause for the indirect dependencies.
- // Currently, that's impossible because we would
- // like to record requiredNativeLibs even when
- // DepIsInSameAPex is false. We also shouldn't do
- // this for host.
- //
- // TODO(jiyong): explain why the same module is passed in twice.
- // Switching the first am to parent breaks lots of tests.
- if !android.IsDepInSameApex(ctx, am, am) {
- return false
- }
-
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- } else if rm, ok := child.(*rust.Module); ok {
- af := apexFileForRustLibrary(ctx, rm)
- af.transitiveDep = true
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- }
- } else if cc.IsTestPerSrcDepTag(depTag) {
- if cc, ok := child.(*cc.Module); ok {
- af := apexFileForExecutable(ctx, cc)
- // Handle modules created as `test_per_src` variations of a single test module:
- // use the name of the generated test binary (`fileToCopy`) instead of the name
- // of the original test module (`depName`, shared by all `test_per_src`
- // variations of that module).
- af.androidMkModuleName = filepath.Base(af.builtFile.String())
- // these are not considered transitive dep
- af.transitiveDep = false
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- }
- } else if cc.IsHeaderDepTag(depTag) {
- // nothing
- } else if java.IsJniDepTag(depTag) {
- // Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
- return false
- } else if java.IsXmlPermissionsFileDepTag(depTag) {
- if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
- filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
- }
- } else if rust.IsDylibDepTag(depTag) {
- if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
- af := apexFileForRustLibrary(ctx, rustm)
- af.transitiveDep = true
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- }
- } else if rust.IsRlibDepTag(depTag) {
- // Rlib is statically linked, but it might have shared lib
- // dependencies. Track them.
- return true
- } else if java.IsBootclasspathFragmentContentDepTag(depTag) {
- // Add the contents of the bootclasspath fragment to the apex.
- switch child.(type) {
- case *java.Library, *java.SdkLibrary:
- javaModule := child.(javaModule)
- af := apexFileForBootclasspathFragmentContentModule(ctx, parent, javaModule)
- if !af.ok() {
- ctx.PropertyErrorf("bootclasspath_fragments", "bootclasspath_fragment content %q is not configured to be compiled into dex", depName)
- return false
- }
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- default:
- ctx.PropertyErrorf("bootclasspath_fragments", "bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
- }
- } else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) {
- // Add the contents of the systemserverclasspath fragment to the apex.
- switch child.(type) {
- case *java.Library, *java.SdkLibrary:
- af := apexFileForJavaModule(ctx, child.(javaModule))
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- default:
- ctx.PropertyErrorf("systemserverclasspath_fragments", "systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
- }
- } else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
- // nothing
- } else if depTag == android.DarwinUniversalVariantTag {
- // nothing
- } else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
- ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
- }
- }
- }
- return false
- })
- if a.privateKeyFile == nil {
- ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key))
+ bazelCtx := ctx.Config().BazelContext
+ outputs, err := bazelCtx.GetApexInfo(a.GetBazelLabel(ctx, a), android.GetConfigKey(ctx))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
return
}
-
- // Remove duplicates in filesInfo
- removeDup := func(filesInfo []apexFile) []apexFile {
- encountered := make(map[string]apexFile)
- for _, f := range filesInfo {
- dest := filepath.Join(f.installDir, f.builtFile.Base())
- if e, ok := encountered[dest]; !ok {
- encountered[dest] = f
- } else {
- // If a module is directly included and also transitively depended on
- // consider it as directly included.
- e.transitiveDep = e.transitiveDep && f.transitiveDep
- encountered[dest] = e
- }
- }
- var result []apexFile
- for _, v := range encountered {
- result = append(result, v)
- }
- return result
- }
- filesInfo = removeDup(filesInfo)
-
- // Sort to have consistent build rules
- sort.Slice(filesInfo, func(i, j int) bool {
- // Sort by destination path so as to ensure consistent ordering even if the source of the files
- // changes.
- return filesInfo[i].path() < filesInfo[j].path()
- })
-
- ////////////////////////////////////////////////////////////////////////////////////////////
- // 3) some fields in apexBundle struct are configured
a.installDir = android.PathForModuleInstall(ctx, "apex")
- a.filesInfo = filesInfo
- // Set suffix and primaryApexType depending on the ApexType
- buildFlattenedAsDefault := ctx.Config().FlattenApex()
- switch a.properties.ApexType {
+ // Set the output file to .apex or .capex depending on the compression configuration.
+ a.setCompression(ctx)
+ if a.isCompressed {
+ a.outputApexFile = android.PathForBazelOutRelative(ctx, ctx.ModuleDir(), outputs.SignedCompressedOutput)
+ } else {
+ a.outputApexFile = android.PathForBazelOutRelative(ctx, ctx.ModuleDir(), outputs.SignedOutput)
+ }
+ a.outputFile = a.outputApexFile
+
+ if len(outputs.TidyFiles) > 0 {
+ tidyFiles := android.PathsForBazelOut(ctx, outputs.TidyFiles)
+ a.outputFile = android.AttachValidationActions(ctx, a.outputFile, tidyFiles)
+ }
+
+ // TODO(b/257829940): These are used by the apex_keys_text singleton; would probably be a clearer
+ // interface if these were set in a provider rather than the module itself
+ a.publicKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyInfo[0])
+ a.privateKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyInfo[1])
+ a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[0])
+ a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[1])
+
+ // Ensure ApexMkInfo.install_to_system make module names are installed as
+ // part of a bundled build.
+ a.makeModulesToInstall = append(a.makeModulesToInstall, outputs.MakeModulesToInstall...)
+
+ apexType := a.properties.ApexType
+ switch apexType {
case imageApex:
- if buildFlattenedAsDefault {
- a.suffix = imageApexSuffix
- } else {
- a.suffix = ""
- a.primaryApexType = true
+ a.bundleModuleFile = android.PathForBazelOut(ctx, outputs.BundleFile)
+ a.nativeApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.SymbolsUsedByApex))
+ a.nativeApisBackedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.BackingLibs))
+ // TODO(b/239084755): Generate the java api using.xml file from Bazel.
+ a.javaApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.JavaSymbolsUsedByApex))
+ a.installedFilesFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.InstalledFiles))
+ installSuffix := imageApexSuffix
+ if a.isCompressed {
+ installSuffix = imageCapexSuffix
+ }
+ a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
+ a.compatSymlinks.Paths()...)
+ default:
+ panic(fmt.Errorf("internal error: unexpected apex_type for the ProcessBazelQueryResponse: %v", a.properties.ApexType))
+ }
- if ctx.Config().InstallExtraFlattenedApexes() {
- a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+ // filesInfo in mixed mode must retrieve all information about the apex's
+ // contents completely from the Starlark providers. It should never rely on
+ // Android.bp information, as they might not exist for fully migrated
+ // dependencies.
+ //
+ // Prevent accidental writes to filesInfo in the earlier parts Soong by
+ // asserting it to be nil.
+ if a.filesInfo != nil {
+ panic(
+ fmt.Errorf("internal error: filesInfo must be nil for an apex handled by Bazel. " +
+ "Did something else set filesInfo before this line of code?"))
+ }
+ for _, f := range outputs.PayloadFilesInfo {
+ fileInfo := apexFile{
+ isBazelPrebuilt: true,
+
+ builtFile: android.PathForBazelOut(ctx, f["built_file"]),
+ unstrippedBuiltFile: android.PathForBazelOut(ctx, f["unstripped_built_file"]),
+ androidMkModuleName: f["make_module_name"],
+ installDir: f["install_dir"],
+ class: classes[f["class"]],
+ customStem: f["basename"],
+ moduleDir: f["package"],
+ }
+
+ arch := f["arch"]
+ fileInfo.arch = arch
+ if len(arch) > 0 {
+ fileInfo.multilib = "lib32"
+ if strings.HasSuffix(arch, "64") {
+ fileInfo.multilib = "lib64"
}
}
- case zipApex:
- if proptools.String(a.properties.Payload_type) == "zip" {
- a.suffix = ""
- a.primaryApexType = true
- } else {
- a.suffix = zipApexSuffix
- }
- case flattenedApex:
- if buildFlattenedAsDefault {
- a.suffix = ""
- a.primaryApexType = true
- } else {
- a.suffix = flattenedSuffix
- }
- }
- switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
- case ext4FsType:
- a.payloadFsType = ext4
- case f2fsFsType:
- a.payloadFsType = f2fs
- case erofsFsType:
- a.payloadFsType = erofs
- default:
- ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs, erofs]", *a.properties.Payload_fs_type)
+ a.filesInfo = append(a.filesInfo, fileInfo)
}
+}
+func (a *apexBundle) setCompression(ctx android.ModuleContext) {
+ if a.properties.ApexType != imageApex {
+ a.isCompressed = false
+ } else if a.testOnlyShouldForceCompression() {
+ a.isCompressed = true
+ } else {
+ a.isCompressed = ctx.Config().ApexCompressionEnabled() && a.isCompressable()
+ }
+}
+
+func (a *apexBundle) setSystemLibLink(ctx android.ModuleContext) {
// Optimization. If we are building bundled APEX, for the files that are gathered due to the
// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
// the same library in the system partition, thus effectively sharing the same libraries
@@ -2309,14 +2105,515 @@
if ctx.Host() {
a.linkToSystemLib = false
}
+}
+func (a *apexBundle) setPayloadFsType(ctx android.ModuleContext) {
+ switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
+ case ext4FsType:
+ a.payloadFsType = ext4
+ case f2fsFsType:
+ a.payloadFsType = f2fs
+ case erofsFsType:
+ a.payloadFsType = erofs
+ default:
+ ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs, erofs]", *a.properties.Payload_fs_type)
+ }
+}
+
+func (a *apexBundle) setApexTypeAndSuffix(ctx android.ModuleContext) {
+ // Set suffix and primaryApexType depending on the ApexType
+ buildFlattenedAsDefault := ctx.Config().FlattenApex()
+ switch a.properties.ApexType {
+ case imageApex:
+ if buildFlattenedAsDefault {
+ a.suffix = imageApexSuffix
+ } else {
+ a.suffix = ""
+ a.primaryApexType = true
+
+ if ctx.Config().InstallExtraFlattenedApexes() {
+ a.makeModulesToInstall = append(a.makeModulesToInstall, a.Name()+flattenedSuffix)
+ }
+ }
+ case zipApex:
+ if proptools.String(a.properties.Payload_type) == "zip" {
+ a.suffix = ""
+ a.primaryApexType = true
+ } else {
+ a.suffix = zipApexSuffix
+ }
+ case flattenedApex:
+ if buildFlattenedAsDefault {
+ a.suffix = ""
+ a.primaryApexType = true
+ } else {
+ a.suffix = flattenedSuffix
+ }
+ }
+}
+
+func (a apexBundle) isCompressable() bool {
+ return proptools.BoolDefault(a.overridableProperties.Compressible, false) && !a.testApex
+}
+
+func (a *apexBundle) commonBuildActions(ctx android.ModuleContext) bool {
+ a.checkApexAvailability(ctx)
+ a.checkUpdatable(ctx)
+ a.CheckMinSdkVersion(ctx)
+ a.checkStaticLinkingToStubLibraries(ctx)
+ a.checkStaticExecutables(ctx)
+ if len(a.properties.Tests) > 0 && !a.testApex {
+ ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
+ return false
+ }
+ return true
+}
+
+type visitorContext struct {
+ // all the files that will be included in this APEX
+ filesInfo []apexFile
+
+ // native lib dependencies
+ provideNativeLibs []string
+ requireNativeLibs []string
+
+ handleSpecialLibs bool
+
+ // if true, raise error on duplicate apexFile
+ checkDuplicate bool
+}
+
+func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) {
+ encountered := make(map[string]apexFile)
+ for _, f := range vctx.filesInfo {
+ dest := filepath.Join(f.installDir, f.builtFile.Base())
+ if e, ok := encountered[dest]; !ok {
+ encountered[dest] = f
+ } else {
+ if vctx.checkDuplicate && f.builtFile.String() != e.builtFile.String() {
+ mctx.ModuleErrorf("apex file %v is provided by two different files %v and %v",
+ dest, e.builtFile, f.builtFile)
+ return
+ }
+ // If a module is directly included and also transitively depended on
+ // consider it as directly included.
+ e.transitiveDep = e.transitiveDep && f.transitiveDep
+ encountered[dest] = e
+ }
+ }
+ vctx.filesInfo = vctx.filesInfo[:0]
+ for _, v := range encountered {
+ vctx.filesInfo = append(vctx.filesInfo, v)
+ }
+ sort.Slice(vctx.filesInfo, func(i, j int) bool {
+ // Sort by destination path so as to ensure consistent ordering even if the source of the files
+ // changes.
+ return vctx.filesInfo[i].path() < vctx.filesInfo[j].path()
+ })
+}
+
+func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent blueprint.Module) bool {
+ depTag := ctx.OtherModuleDependencyTag(child)
+ if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+ return false
+ }
+ if mod, ok := child.(android.Module); ok && !mod.Enabled() {
+ return false
+ }
+ depName := ctx.OtherModuleName(child)
+ if _, isDirectDep := parent.(*apexBundle); isDirectDep {
+ switch depTag {
+ case sharedLibTag, jniLibTag:
+ isJniLib := depTag == jniLibTag
+ switch ch := child.(type) {
+ case *cc.Module:
+ fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+ fi.isJniLib = isJniLib
+ vctx.filesInfo = append(vctx.filesInfo, fi)
+ // Collect the list of stub-providing libs except:
+ // - VNDK libs are only for vendors
+ // - bootstrap bionic libs are treated as provided by system
+ if ch.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(ch.BaseModuleName(), ctx.Config()) {
+ vctx.provideNativeLibs = append(vctx.provideNativeLibs, fi.stem())
+ }
+ return true // track transitive dependencies
+ case *rust.Module:
+ fi := apexFileForRustLibrary(ctx, ch)
+ fi.isJniLib = isJniLib
+ vctx.filesInfo = append(vctx.filesInfo, fi)
+ return true // track transitive dependencies
+ default:
+ propertyName := "native_shared_libs"
+ if isJniLib {
+ propertyName = "jni_libs"
+ }
+ ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
+ }
+ case executableTag:
+ switch ch := child.(type) {
+ case *cc.Module:
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
+ return true // track transitive dependencies
+ case *python.PythonBinaryModule:
+ if ch.HostToolPath().Valid() {
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForPyBinary(ctx, ch))
+ }
+ case bootstrap.GoBinaryTool:
+ if a.Host() {
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForGoBinary(ctx, depName, ch))
+ }
+ case *rust.Module:
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch))
+ return true // track transitive dependencies
+ default:
+ ctx.PropertyErrorf("binaries",
+ "%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, nor (host) bootstrap_go_binary", depName)
+ }
+ case shBinaryTag:
+ if csh, ok := child.(*sh.ShBinary); ok {
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForShBinary(ctx, csh))
+ } else {
+ ctx.PropertyErrorf("sh_binaries", "%q is not a sh_binary module", depName)
+ }
+ case bcpfTag:
+ _, ok := child.(*java.BootclasspathFragmentModule)
+ if !ok {
+ ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
+ return false
+ }
+
+ vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
+ return true
+ case sscpfTag:
+ if _, ok := child.(*java.SystemServerClasspathModule); !ok {
+ ctx.PropertyErrorf("systemserverclasspath_fragments",
+ "%q is not a systemserverclasspath_fragment module", depName)
+ return false
+ }
+ if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
+ vctx.filesInfo = append(vctx.filesInfo, *af)
+ }
+ return true
+ case javaLibTag:
+ switch child.(type) {
+ case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
+ af := apexFileForJavaModule(ctx, child.(javaModule))
+ if !af.ok() {
+ ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
+ return false
+ }
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ return true // track transitive dependencies
+ default:
+ ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
+ }
+ case androidAppTag:
+ switch ap := child.(type) {
+ case *java.AndroidApp:
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+ return true // track transitive dependencies
+ case *java.AndroidAppImport:
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+ case *java.AndroidTestHelperApp:
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+ case *java.AndroidAppSet:
+ appDir := "app"
+ if ap.Privileged() {
+ appDir = "priv-app"
+ }
+ // 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
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ default:
+ ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
+ }
+ case rroTag:
+ if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
+ } else {
+ ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
+ }
+ case bpfTag:
+ if bpfProgram, ok := child.(bpf.BpfModule); ok {
+ filesToCopy, _ := bpfProgram.OutputFiles("")
+ apex_sub_dir := bpfProgram.SubDir()
+ for _, bpfFile := range filesToCopy {
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
+ }
+ } else {
+ ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
+ }
+ case fsTag:
+ if fs, ok := child.(filesystem.Filesystem); ok {
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
+ } else {
+ ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
+ }
+ case prebuiltTag:
+ if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+ } else {
+ ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
+ }
+ case compatConfigTag:
+ if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok {
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName))
+ } else {
+ ctx.PropertyErrorf("compat_configs", "%q is not a platform_compat_config module", depName)
+ }
+ case testTag:
+ if ccTest, ok := child.(*cc.Module); ok {
+ if ccTest.IsTestPerSrcAllTestsVariation() {
+ // Multiple-output test module (where `test_per_src: true`).
+ //
+ // `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
+ // We do not add this variation to `filesInfo`, as it has no output;
+ // however, we do add the other variations of this module as indirect
+ // dependencies (see below).
+ } else {
+ // Single-output test module (where `test_per_src: false`).
+ af := apexFileForExecutable(ctx, ccTest)
+ af.class = nativeTest
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ }
+ return true // track transitive dependencies
+ } else {
+ ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
+ }
+ case keyTag:
+ if key, ok := child.(*apexKey); ok {
+ a.privateKeyFile = key.privateKeyFile
+ a.publicKeyFile = key.publicKeyFile
+ } else {
+ ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
+ }
+ case certificateTag:
+ if dep, ok := child.(*java.AndroidAppCertificate); ok {
+ a.containerCertificateFile = dep.Certificate.Pem
+ a.containerPrivateKeyFile = dep.Certificate.Key
+ } else {
+ ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
+ }
+ }
+ return false
+ }
+
+ if a.vndkApex {
+ return false
+ }
+
+ // indirect dependencies
+ am, ok := child.(android.ApexModule)
+ if !ok {
+ return false
+ }
+ // We cannot use a switch statement on `depTag` here as the checked
+ // tags used below are private (e.g. `cc.sharedDepTag`).
+ if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
+ if ch, ok := child.(*cc.Module); ok {
+ if ch.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && ch.IsVndk() {
+ vctx.requireNativeLibs = append(vctx.requireNativeLibs, ":vndk")
+ return false
+ }
+ af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+ af.transitiveDep = true
+
+ // Always track transitive dependencies for host.
+ if a.Host() {
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ return true
+ }
+
+ abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+ if !abInfo.Contents.DirectlyInApex(depName) && (ch.IsStubs() || ch.HasStubsVariants()) {
+ // If the dependency is a stubs lib, don't include it in this APEX,
+ // but make sure that the lib is installed on the device.
+ // In case no APEX is having the lib, the lib is installed to the system
+ // partition.
+ //
+ // Always include if we are a host-apex however since those won't have any
+ // system libraries.
+ //
+ // Skip the dependency in unbundled builds where the device image is not
+ // being built.
+ if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() && !ctx.Config().UnbundledBuild() {
+ // we need a module name for Make
+ name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
+ if !android.InList(name, a.makeModulesToInstall) {
+ a.makeModulesToInstall = append(a.makeModulesToInstall, name)
+ }
+ }
+ vctx.requireNativeLibs = append(vctx.requireNativeLibs, af.stem())
+ // Don't track further
+ return false
+ }
+
+ // If the dep is not considered to be in the same
+ // apex, don't add it to filesInfo so that it is not
+ // included in this APEX.
+ // TODO(jiyong): move this to at the top of the
+ // else-if clause for the indirect dependencies.
+ // Currently, that's impossible because we would
+ // like to record requiredNativeLibs even when
+ // DepIsInSameAPex is false. We also shouldn't do
+ // this for host.
+ //
+ // TODO(jiyong): explain why the same module is passed in twice.
+ // Switching the first am to parent breaks lots of tests.
+ if !android.IsDepInSameApex(ctx, am, am) {
+ return false
+ }
+
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ return true // track transitive dependencies
+ } else if rm, ok := child.(*rust.Module); ok {
+ af := apexFileForRustLibrary(ctx, rm)
+ af.transitiveDep = true
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ return true // track transitive dependencies
+ }
+ } else if cc.IsTestPerSrcDepTag(depTag) {
+ if ch, ok := child.(*cc.Module); ok {
+ af := apexFileForExecutable(ctx, ch)
+ // Handle modules created as `test_per_src` variations of a single test module:
+ // use the name of the generated test binary (`fileToCopy`) instead of the name
+ // of the original test module (`depName`, shared by all `test_per_src`
+ // variations of that module).
+ af.androidMkModuleName = filepath.Base(af.builtFile.String())
+ // these are not considered transitive dep
+ af.transitiveDep = false
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ return true // track transitive dependencies
+ }
+ } else if cc.IsHeaderDepTag(depTag) {
+ // nothing
+ } else if java.IsJniDepTag(depTag) {
+ // Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
+ } else if java.IsXmlPermissionsFileDepTag(depTag) {
+ if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+ vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+ }
+ } else if rust.IsDylibDepTag(depTag) {
+ if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
+ af := apexFileForRustLibrary(ctx, rustm)
+ af.transitiveDep = true
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ return true // track transitive dependencies
+ }
+ } else if rust.IsRlibDepTag(depTag) {
+ // Rlib is statically linked, but it might have shared lib
+ // dependencies. Track them.
+ return true
+ } else if java.IsBootclasspathFragmentContentDepTag(depTag) {
+ // Add the contents of the bootclasspath fragment to the apex.
+ switch child.(type) {
+ case *java.Library, *java.SdkLibrary:
+ javaModule := child.(javaModule)
+ af := apexFileForBootclasspathFragmentContentModule(ctx, parent, javaModule)
+ if !af.ok() {
+ ctx.PropertyErrorf("bootclasspath_fragments",
+ "bootclasspath_fragment content %q is not configured to be compiled into dex", depName)
+ return false
+ }
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ return true // track transitive dependencies
+ default:
+ ctx.PropertyErrorf("bootclasspath_fragments",
+ "bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+ }
+ } else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) {
+ // Add the contents of the systemserverclasspath fragment to the apex.
+ switch child.(type) {
+ case *java.Library, *java.SdkLibrary:
+ af := apexFileForJavaModule(ctx, child.(javaModule))
+ vctx.filesInfo = append(vctx.filesInfo, af)
+ if profileAf := apexFileForJavaModuleProfile(ctx, child.(javaModule)); profileAf != nil {
+ vctx.filesInfo = append(vctx.filesInfo, *profileAf)
+ }
+ return true // track transitive dependencies
+ default:
+ ctx.PropertyErrorf("systemserverclasspath_fragments",
+ "systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+ }
+ } else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
+ // nothing
+ } else if depTag == android.DarwinUniversalVariantTag {
+ // nothing
+ } else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
+ ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
+ }
+ return false
+}
+
+func (a *apexBundle) shouldCheckDuplicate(ctx android.ModuleContext) bool {
+ // TODO(b/263308293) remove this
+ if a.properties.IsCoverageVariant {
+ return false
+ }
+ // TODO(b/263308515) remove this
+ if a.testApex {
+ return false
+ }
+ // TODO(b/263309864) remove this
+ if a.Host() {
+ return false
+ }
+ if a.Device() && ctx.DeviceConfig().DeviceArch() == "" {
+ return false
+ }
+ return true
+}
+
+// Creates build rules for an APEX. It consists of the following major steps:
+//
+// 1) do some validity checks such as apex_available, min_sdk_version, etc.
+// 2) traverse the dependency tree to collect apexFile structs from them.
+// 3) some fields in apexBundle struct are configured
+// 4) generate the build rules to create the APEX. This is mostly done in builder.go.
+func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 1) do some validity checks such as apex_available, min_sdk_version, etc.
+ if !a.commonBuildActions(ctx) {
+ return
+ }
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 2) traverse the dependency tree to collect apexFile structs from them.
+ // Collect the module directory for IDE info in java/jdeps.go.
+ a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
+
+ // TODO(jiyong): do this using WalkPayloadDeps
+ // TODO(jiyong): make this clean!!!
+ vctx := visitorContext{
+ handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case),
+ checkDuplicate: a.shouldCheckDuplicate(ctx),
+ }
+ ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) })
+ vctx.normalizeFileInfo(ctx)
+ if a.privateKeyFile == nil {
+ ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key))
+ return
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 3) some fields in apexBundle struct are configured
+ a.installDir = android.PathForModuleInstall(ctx, "apex")
+ a.filesInfo = vctx.filesInfo
+
+ a.setApexTypeAndSuffix(ctx)
+ a.setPayloadFsType(ctx)
+ a.setSystemLibLink(ctx)
if a.properties.ApexType != zipApex {
a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType)
}
////////////////////////////////////////////////////////////////////////////////////////////
// 4) generate the build rules to create the APEX. This is done in builder.go.
- a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
+ a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs)
if a.properties.ApexType == flattenedApex {
a.buildFlattenedApex(ctx)
} else {
@@ -2351,25 +2648,13 @@
bootclasspathFragmentInfo := ctx.OtherModuleProvider(module, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo)
var filesToAdd []apexFile
- // Add the boot image files, e.g. .art, .oat and .vdex files.
- if bootclasspathFragmentInfo.ShouldInstallBootImageInApex() {
- for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() {
- dirInApex := filepath.Join("javalib", arch.String())
- for _, f := range files {
- androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
- // TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil
- af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil)
- filesToAdd = append(filesToAdd, af)
- }
- }
- }
-
// Add classpaths.proto config.
if af := apexClasspathFragmentProtoFile(ctx, module); af != nil {
filesToAdd = append(filesToAdd, *af)
}
- if pathInApex := bootclasspathFragmentInfo.ProfileInstallPathInApex(); pathInApex != "" {
+ pathInApex := bootclasspathFragmentInfo.ProfileInstallPathInApex()
+ if pathInApex != "" {
pathOnHost := bootclasspathFragmentInfo.ProfilePathOnHost()
tempPath := android.PathForModuleOut(ctx, "boot_image_profile", pathInApex)
@@ -2450,9 +2735,9 @@
android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
- android.InitSdkAwareModule(module)
android.InitOverridableModule(module, &module.overridableProperties.Overrides)
android.InitBazelModule(module)
+ multitree.InitExportableModule(module)
return module
}
@@ -2464,7 +2749,7 @@
// apex_test is an APEX for testing. The difference from the ordinary apex module type is that
// certain compatibility checks such as apex_available are not done for apex_test.
-func testApexBundleFactory() android.Module {
+func TestApexBundleFactory() android.Module {
bundle := newApexBundle()
bundle.testApex = true
return bundle
@@ -2482,14 +2767,9 @@
}
// apex_defaults provides defaultable properties to other apex modules.
-func defaultsFactory() android.Module {
- return DefaultsFactory()
-}
-
-func DefaultsFactory(props ...interface{}) android.Module {
+func DefaultsFactory() android.Module {
module := &Defaults{}
- module.AddProperties(props...)
module.AddProperties(
&apexBundleProperties{},
&apexTargetBundleProperties{},
@@ -2504,24 +2784,120 @@
type OverrideApex struct {
android.ModuleBase
android.OverrideModuleBase
+ android.BazelModuleBase
}
-func (o *OverrideApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (o *OverrideApex) GenerateAndroidBuildActions(_ android.ModuleContext) {
// All the overrides happen in the base module.
}
// 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, commonAttrs := convertWithBp2build(a, ctx)
+
+ // We just want the name, not module reference.
+ baseApexName := strings.TrimPrefix(baseApexModuleName, ":")
+ attrs.Base_apex_name = &baseApexName
+
+ 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 {
+ // If overridableProperties.Certificate is nil, clear this out as
+ // well with zeroed structs, so the override_apex does not use the
+ // base apex's certificate.
+ attrs.Certificate = bazel.LabelAttribute{}
+ attrs.Certificate_name = bazel.StringAttribute{}
+ } else {
+ attrs.Certificate, attrs.Certificate_name = android.BazelStringOrLabelFromProp(ctx, overridableProperties.Certificate)
+ }
+
+ // Prebuilts
+ if overridableProperties.Prebuilts != nil {
+ 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
+ }
+
+ // Logging parent
+ if overridableProperties.Logging_parent != "" {
+ attrs.Logging_parent = &overridableProperties.Logging_parent
+ }
+ }
+
+ commonAttrs.Name = o.Name()
+
+ ctx.CreateBazelTargetModule(props, commonAttrs, &attrs)
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////////
// Vality check routines
//
@@ -2532,7 +2908,7 @@
var _ android.ModuleWithMinSdkVersionCheck = (*apexBundle)(nil)
-// Entures that min_sdk_version of the included modules are equal or less than the min_sdk_version
+// Ensures that min_sdk_version of the included modules are equal or less than the min_sdk_version
// of this apexBundle.
func (a *apexBundle) CheckMinSdkVersion(ctx android.ModuleContext) {
if a.testApex || a.vndkApex {
@@ -2548,25 +2924,23 @@
// 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.overridableProperties.Min_sdk_version))
- isMinSdkSet := a.overridableProperties.Min_sdk_version != nil
- isOverrideValueHigher := overrideApiLevel.CompareTo(originalMinApiLevel) > 0
- if overrideMinSdkValue != "" && isMinSdkSet && isOverrideValueHigher {
- return overrideMinSdkValue
+ minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+ if minApiLevel.IsNone() {
+ return ""
}
- return proptools.String(a.overridableProperties.Min_sdk_version)
+ overrideMinSdkValue := ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride()
+ overrideApiLevel := minSdkVersionFromValue(ctx, overrideMinSdkValue)
+ if !overrideApiLevel.IsNone() && overrideApiLevel.CompareTo(minApiLevel) > 0 {
+ minApiLevel = overrideApiLevel
+ }
+
+ return minApiLevel.String()
}
// 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: a.minSdkVersionValue(ctx),
- }
+func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ return a.minSdkVersion(ctx)
}
// Returns apex's min_sdk_version ApiLevel, honoring overrides
@@ -2640,8 +3014,8 @@
if a.UsePlatformApis() {
ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs")
}
- if a.SocSpecific() || a.DeviceSpecific() {
- ctx.PropertyErrorf("updatable", "vendor APEXes are not updatable")
+ if proptools.Bool(a.properties.Use_vndk_as_stable) {
+ ctx.PropertyErrorf("use_vndk_as_stable", "updatable APEXes can't use external VNDK libs")
}
if a.FutureUpdatable() {
ctx.PropertyErrorf("future_updatable", "Already updatable. Remove `future_updatable: true:`")
@@ -2760,7 +3134,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",
},
@@ -2771,9 +3145,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.overridableProperties.Java_libs...)
- dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Bootclasspath_fragments...)
- dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Systemserverclasspath_fragments...)
+ 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.Paths = append(dpInfo.Paths, a.modulePaths...)
}
@@ -2847,61 +3221,7 @@
// Module separator
//
m["com.android.btservices"] = []string{
- "bluetooth-protos-lite",
- "internal_include_headers",
- "libaudio-a2dp-hw-utils",
- "libaudio-hearing-aid-hw-utils",
- "libbluetooth",
- "libbluetooth-types",
- "libbluetooth-types-header",
- "libbluetooth_gd",
- "libbluetooth_headers",
- "libbluetooth_jni",
- "libbt-audio-hal-interface",
- "libbt-bta",
- "libbt-common",
- "libbt-hci",
- "libbt-platform-protos-lite",
- "libbt-protos-lite",
- "libbt-sbc-decoder",
- "libbt-sbc-encoder",
- "libbt-stack",
- "libbt-utils",
- "libbtcore",
- "libbtdevice",
- "libbte",
- "libbtif",
- "libchrome",
- }
- //
- // Module separator
- //
- m["com.android.bluetooth"] = []string{
- "bluetooth-protos-lite",
- "internal_include_headers",
- "libaudio-a2dp-hw-utils",
- "libaudio-hearing-aid-hw-utils",
- "libbluetooth",
- "libbluetooth-types",
- "libbluetooth-types-header",
- "libbluetooth_gd",
- "libbluetooth_headers",
- "libbluetooth_jni",
- "libbt-audio-hal-interface",
- "libbt-bta",
- "libbt-common",
- "libbt-hci",
- "libbt-platform-protos-lite",
- "libbt-protos-lite",
- "libbt-sbc-decoder",
- "libbt-sbc-encoder",
- "libbt-stack",
- "libbt-utils",
- "libbtcore",
- "libbtdevice",
- "libbte",
- "libbtif",
- "libchrome",
+ // empty
}
//
// Module separator
@@ -2963,33 +3283,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",
@@ -3162,11 +3455,11 @@
// Adding code to the bootclasspath in new packages will cause issues on module update.
func qBcpPackages() map[string][]string {
return map[string][]string{
- "conscrypt": []string{
+ "conscrypt": {
"android.net.ssl",
"com.android.org.conscrypt",
},
- "updatable-media": []string{
+ "updatable-media": {
"android.media",
},
}
@@ -3176,32 +3469,32 @@
// Adding code to the bootclasspath in new packages will cause issues on module update.
func rBcpPackages() map[string][]string {
return map[string][]string{
- "framework-mediaprovider": []string{
+ "framework-mediaprovider": {
"android.provider",
},
- "framework-permission": []string{
+ "framework-permission": {
"android.permission",
"android.app.role",
"com.android.permission",
"com.android.role",
},
- "framework-sdkextensions": []string{
+ "framework-sdkextensions": {
"android.os.ext",
},
- "framework-statsd": []string{
+ "framework-statsd": {
"android.app",
"android.os",
"android.util",
"com.android.internal.statsd",
"com.android.server.stats",
},
- "framework-wifi": []string{
+ "framework-wifi": {
"com.android.server.wifi",
"com.android.wifi.x",
"android.hardware.wifi",
"android.net.wifi",
},
- "framework-tethering": []string{
+ "framework-tethering": {
"android.net",
},
}
@@ -3213,9 +3506,11 @@
Manifest bazel.LabelAttribute
Android_manifest bazel.LabelAttribute
File_contexts bazel.LabelAttribute
+ Canned_fs_config bazel.LabelAttribute
Key bazel.LabelAttribute
- Certificate bazel.LabelAttribute
- Min_sdk_version *string
+ Certificate bazel.LabelAttribute // used when the certificate prop is a module
+ Certificate_name bazel.StringAttribute // used when the certificate prop is a string
+ Min_sdk_version bazel.StringAttribute
Updatable bazel.BoolAttribute
Installable bazel.BoolAttribute
Binaries bazel.LabelListAttribute
@@ -3223,6 +3518,10 @@
Native_shared_libs_32 bazel.LabelListAttribute
Native_shared_libs_64 bazel.LabelListAttribute
Compressible bazel.BoolAttribute
+ Package_name *string
+ Logging_parent *string
+ Tests bazel.LabelListAttribute
+ Base_apex_name *string
}
type convertedNativeSharedLibs struct {
@@ -3230,17 +3529,25 @@
Native_shared_libs_64 bazel.LabelListAttribute
}
+const (
+ minSdkVersionPropName = "Min_sdk_version"
+)
+
// ConvertWithBp2build performs bp2build conversion of an apex
func (a *apexBundle) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- // We do not convert apex_test modules at this time
- if ctx.ModuleType() != "apex" {
+ // We only convert apex and apex_test modules at this time
+ if ctx.ModuleType() != "apex" && ctx.ModuleType() != "apex_test" {
return
}
+ attrs, props, commonAttrs := convertWithBp2build(a, ctx)
+ commonAttrs.Name = a.Name()
+ ctx.CreateBazelTargetModule(props, commonAttrs, &attrs)
+}
+
+func convertWithBp2build(a *apexBundle, ctx android.TopDownMutatorContext) (bazelApexBundleAttributes, bazel.BazelTargetModuleProperties, android.CommonAttributes) {
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 {
@@ -3248,15 +3555,35 @@
}
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))
}
+ var cannedFsConfigAttribute bazel.LabelAttribute
+ if a.properties.Canned_fs_config != nil {
+ cannedFsConfigAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.Canned_fs_config))
+ }
+
+ productVariableProps := android.ProductVariableProperties(ctx, a)
// 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.overridableProperties.Min_sdk_version != nil {
- minSdkVersion = a.overridableProperties.Min_sdk_version
+ var minSdkVersion bazel.StringAttribute
+ if a.properties.Min_sdk_version != nil {
+ minSdkVersion.SetValue(*a.properties.Min_sdk_version)
+ }
+ if props, ok := productVariableProps[minSdkVersionPropName]; ok {
+ for c, p := range props {
+ if val, ok := p.(*string); ok {
+ minSdkVersion.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val)
+ }
+ }
}
var keyLabelAttribute bazel.LabelAttribute
@@ -3264,16 +3591,19 @@
keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Key))
}
- var certificateLabelAttribute bazel.LabelAttribute
- if a.overridableProperties.Certificate != nil {
- certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Certificate))
- }
+ // Certificate
+ certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableProperties.Certificate)
nativeSharedLibs := &convertedNativeSharedLibs{
Native_shared_libs_32: bazel.LabelListAttribute{},
Native_shared_libs_64: bazel.LabelListAttribute{},
}
- compileMultilib := "both"
+
+ // https://cs.android.com/android/platform/superproject/+/master:build/soong/android/arch.go;l=698;drc=f05b0d35d2fbe51be9961ce8ce8031f840295c68
+ // https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/apex.go;l=2549;drc=ec731a83e3e2d80a1254e32fd4ad7ef85e262669
+ // In Soong, decodeMultilib, used to get multilib, return "first" if defaultMultilib is set to "common".
+ // Since apex sets defaultMultilib to be "common", equivalent compileMultilib in bp2build for apex should be "first"
+ compileMultilib := "first"
if a.CompileMultilib() != nil {
compileMultilib = *a.CompileMultilib()
}
@@ -3292,6 +3622,12 @@
binaries := android.BazelLabelForModuleDeps(ctx, a.properties.ApexNativeDependencies.Binaries)
binariesLabelListAttribute := bazel.MakeLabelListAttribute(binaries)
+ var testsAttrs bazel.LabelListAttribute
+ if a.testApex && len(a.properties.ApexNativeDependencies.Tests) > 0 {
+ tests := android.BazelLabelForModuleDeps(ctx, a.properties.ApexNativeDependencies.Tests)
+ testsAttrs = bazel.MakeLabelListAttribute(tests)
+ }
+
var updatableAttribute bazel.BoolAttribute
if a.properties.Updatable != nil {
updatableAttribute.Value = a.properties.Updatable
@@ -3307,13 +3643,25 @@
compressibleAttribute.Value = a.overridableProperties.Compressible
}
- attrs := &bazelApexBundleAttributes{
+ var packageName *string
+ if a.overridableProperties.Package_name != "" {
+ packageName = &a.overridableProperties.Package_name
+ }
+
+ var loggingParent *string
+ if a.overridableProperties.Logging_parent != "" {
+ loggingParent = &a.overridableProperties.Logging_parent
+ }
+
+ attrs := bazelApexBundleAttributes{
Manifest: manifestLabelAttribute,
Android_manifest: androidManifestLabelAttribute,
File_contexts: fileContextsLabelAttribute,
+ Canned_fs_config: cannedFsConfigAttribute,
Min_sdk_version: minSdkVersion,
Key: keyLabelAttribute,
- Certificate: certificateLabelAttribute,
+ Certificate: certificate,
+ Certificate_name: certificateName,
Updatable: updatableAttribute,
Installable: installableAttribute,
Native_shared_libs_32: nativeSharedLibs.Native_shared_libs_32,
@@ -3321,14 +3669,22 @@
Binaries: binariesLabelListAttribute,
Prebuilts: prebuiltsLabelListAttribute,
Compressible: compressibleAttribute,
+ Package_name: packageName,
+ Logging_parent: loggingParent,
+ Tests: testsAttrs,
}
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)
+ commonAttrs := android.CommonAttributes{}
+ if a.testApex {
+ commonAttrs.Testonly = proptools.BoolPtr(true)
+ }
+
+ return attrs, props, commonAttrs
}
// The following conversions are based on this table where the rows are the compile_multilib
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 6faed70..1581949 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -55,17 +55,17 @@
touch ${out};
else
echo -e "\n******************************";
- echo "ERROR: go/apex-allowed-deps-error";
+ echo "ERROR: go/apex-allowed-deps-error contains more information";
echo "******************************";
echo "Detected changes to allowed dependencies in updatable modules.";
echo "To fix and update packages/modules/common/build/allowed_deps.txt, please run:";
echo -e "$$ (croot && packages/modules/common/build/update-apex-allowed-deps.sh)\n";
echo "When submitting the generated CL, you must include the following information";
echo "in the commit message if you are adding a new dependency:";
- echo "Apex-Size-Increase:";
- echo "Previous-Platform-Support:";
- echo "Aosp-First:";
- echo "Test-Info:";
+ echo "Apex-Size-Increase: Expected binary size increase for affected APEXes (or the size of the .jar / .so file of the new library)";
+ echo "Previous-Platform-Support: Are the maintainers of the new dependency committed to supporting previous platform releases?";
+ echo "Aosp-First: Is the new dependency being developed AOSP-first or internal?";
+ echo "Test-Info: What’s the testing strategy for the new dependency? Does it have its own tests, and are you adding integration tests? How/when are the tests run?";
echo "You do not need OWNERS approval to submit the change, but mainline-modularization@";
echo "will periodically review additions and may require changes.";
echo -e "******************************\n";
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 83adeca..139b77e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "os"
"path"
"path/filepath"
"reflect"
@@ -26,6 +25,7 @@
"strings"
"testing"
+ "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -146,7 +146,7 @@
android.PrepareForTestWithAndroidBuildComponents,
bpf.PrepareForTestWithBpf,
cc.PrepareForTestWithCcBuildComponents,
- java.PrepareForTestWithJavaDefaultModules,
+ java.PrepareForTestWithDexpreopt,
prebuilt_etc.PrepareForTestWithPrebuiltEtc,
rust.PrepareForTestWithRustDefaultModules,
sh.PrepareForTestWithShBuildComponents,
@@ -443,7 +443,6 @@
srcs: ["mylib.cpp"],
system_shared_libs: [],
stl: "none",
- notice: "custom_notice",
static_libs: ["libstatic"],
// TODO: remove //apex_available:platform
apex_available: [
@@ -467,7 +466,6 @@
srcs: ["mylib.cpp"],
system_shared_libs: [],
stl: "none",
- notice: "custom_notice_for_static_lib",
// TODO: remove //apex_available:platform
apex_available: [
"//apex_available:platform",
@@ -619,7 +617,7 @@
java_libs: ["myjar"],
apps: ["AppFoo"],
rros: ["rro"],
- bpfs: ["bpf", "netd_test"],
+ bpfs: ["bpf", "netdTest"],
updatable: false,
}
@@ -673,8 +671,8 @@
}
bpf {
- name: "netd_test",
- srcs: ["netd_test.c"],
+ name: "netdTest",
+ srcs: ["netdTest.c"],
sub_dir: "netd",
}
@@ -687,7 +685,7 @@
"overlay/blue/rro.apk",
"etc/bpf/bpf.o",
"etc/bpf/bpf2.o",
- "etc/bpf/netd/netd_test.o",
+ "etc/bpf/netd/netdTest.o",
})
}
@@ -786,6 +784,43 @@
}
}
+func TestFileContexts(t *testing.T) {
+ for _, useFileContextsAsIs := range []bool{true, false} {
+ prop := ""
+ if useFileContextsAsIs {
+ prop = "use_file_contexts_as_is: true,\n"
+ }
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ file_contexts: "file_contexts",
+ updatable: false,
+ vendor: true,
+ `+prop+`
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `, withFiles(map[string][]byte{
+ "file_contexts": nil,
+ }))
+
+ rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("file_contexts")
+ forceLabellingCommand := "apex_manifest\\\\.pb u:object_r:system_file:s0"
+ if useFileContextsAsIs {
+ android.AssertStringDoesNotContain(t, "should force-label",
+ rule.RuleParams.Command, forceLabellingCommand)
+ } else {
+ android.AssertStringDoesContain(t, "shouldn't force-label",
+ rule.RuleParams.Command, forceLabellingCommand)
+ }
+ }
+}
+
func TestBasicZipApex(t *testing.T) {
ctx := testApex(t, `
apex {
@@ -949,8 +984,10 @@
// mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
// ensureNotContains(t, mylib2Cflags, "-include ")
- // Ensure that genstub is invoked with --apex
- ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"])
+ // Ensure that genstub for platform-provided lib is invoked with --systemapi
+ ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
+ // Ensure that genstub for apex-provided lib is invoked with --apex
+ ensureContains(t, ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex")
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"lib64/mylib.so",
@@ -964,7 +1001,7 @@
// Ensure that stub dependency from a rust module is not included
ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
// The rust module is linked to the stub cc library
- rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
+ rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustLink").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")
@@ -1040,7 +1077,7 @@
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").Rule("rustc").Args["linkFlags"]
+ rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustLink").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")
}
@@ -1134,8 +1171,8 @@
mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylib2Cflags, "-include ")
- // Ensure that genstub is invoked with --apex
- ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"])
+ // Ensure that genstub is invoked with --systemapi
+ ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"], "--systemapi")
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"lib64/mylib.so",
@@ -1890,13 +1927,13 @@
expectNoLink("libx", "shared_apex10000", "libz", "shared")
}
-func TestApexMinSdkVersion_crtobjectInVendorApex(t *testing.T) {
+func TestApexMinSdkVersion_InVendorApex(t *testing.T) {
ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
- updatable: false,
+ updatable: true,
vendor: true,
min_sdk_version: "29",
}
@@ -1909,20 +1946,34 @@
cc_library {
name: "mylib",
+ srcs: ["mylib.cpp"],
vendor_available: true,
- system_shared_libs: [],
- stl: "none",
- apex_available: [ "myapex" ],
min_sdk_version: "29",
+ shared_libs: ["libbar"],
+ }
+
+ cc_library {
+ name: "libbar",
+ stubs: { versions: ["29", "30"] },
+ llndk: { symbol_file: "libbar.map.txt" },
}
`)
vendorVariant := "android_vendor.29_arm64_armv8-a"
- // First check that the correct variant of crtbegin_so is used.
- ldRule := ctx.ModuleForTests("mylib", vendorVariant+"_shared_apex29").Rule("ld")
- crtBegin := names(ldRule.Args["crtBegin"])
- ensureListContains(t, crtBegin, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"crtbegin_so/"+vendorVariant+"_apex29/crtbegin_so.o")
+ mylib := ctx.ModuleForTests("mylib", vendorVariant+"_shared_myapex")
+
+ // Ensure that mylib links with "current" LLNDK
+ libFlags := names(mylib.Rule("ld").Args["libFlags"])
+ ensureListContains(t, libFlags, "out/soong/.intermediates/libbar/"+vendorVariant+"_shared_current/libbar.so")
+
+ // Ensure that mylib is targeting 29
+ ccRule := ctx.ModuleForTests("mylib", vendorVariant+"_static_apex29").Output("obj/mylib.o")
+ ensureContains(t, ccRule.Args["cFlags"], "-target aarch64-linux-android29")
+
+ // Ensure that the correct variant of crtbegin_so is used.
+ crtBegin := mylib.Rule("ld").Args["crtBegin"]
+ ensureContains(t, crtBegin, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"crtbegin_so/"+vendorVariant+"_apex29/crtbegin_so.o")
// Ensure that the crtbegin_so used by the APEX is targeting 29
cflags := ctx.ModuleForTests("crtbegin_so", vendorVariant+"_apex29").Rule("cc").Args["cFlags"]
@@ -2128,6 +2179,34 @@
min_sdk_version: "30",
}
`)
+
+ // Skip check for modules compiling against core API surface
+ testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: ["libfoo"],
+ min_sdk_version: "29",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "libfoo",
+ srcs: ["Foo.java"],
+ apex_available: [
+ "myapex",
+ ],
+ // Compile against core API surface
+ sdk_version: "core_current",
+ min_sdk_version: "30",
+ }
+ `)
+
}
func TestApexMinSdkVersion_Okay(t *testing.T) {
@@ -2190,6 +2269,38 @@
`)
}
+func TestApexMinSdkVersion_MinApiForArch(t *testing.T) {
+ // Tests that an apex dependency with min_sdk_version higher than the
+ // min_sdk_version of the apex is allowed as long as the dependency's
+ // min_sdk_version is less than or equal to the api level that the
+ // architecture was introduced in. In this case, arm64 didn't exist
+ // until api level 21, so the arm64 code will never need to run on
+ // an api level 20 device, even if other architectures of the apex
+ // will.
+ testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo"],
+ min_sdk_version: "20",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "libfoo",
+ srcs: ["mylib.cpp"],
+ apex_available: ["myapex"],
+ min_sdk_version: "21",
+ stl: "none",
+ }
+ `)
+}
+
func TestJavaStableSdkVersion(t *testing.T) {
testCases := []struct {
name string
@@ -2964,7 +3075,7 @@
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += libc.vendor libm.vendor libdl.vendor\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++.vendor.myapex:64 mylib.vendor.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex libc.vendor libm.vendor libdl.vendor\n")
}
func TestAndroidMkWritesCommonProperties(t *testing.T) {
@@ -3297,17 +3408,14 @@
// non-APEX variant does not have __ANDROID_APEX__ defined
mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
- // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
+ // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=10000")
- // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
+ // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex29").Rule("cc").Args["cFlags"]
ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=29")
// When a cc_library sets use_apex_name_macro: true each apex gets a unique variant and
// each variant defines additional macros to distinguish which apex variant it is built for
@@ -3316,19 +3424,17 @@
mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- // recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
+ // recovery variant does not set __ANDROID_APEX__
mylibCFlags = ctx.ModuleForTests("mylib3", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
// non-APEX variant does not have __ANDROID_APEX__ defined
mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- // recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
+ // recovery variant does not set __ANDROID_APEX__
mylibCFlags = ctx.ModuleForTests("mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
}
func TestHeaderLibsDependency(t *testing.T) {
@@ -3388,11 +3494,34 @@
isLink bool
}
+func (f fileInApex) String() string {
+ return f.src + ":" + f.path
+}
+
+func (f fileInApex) match(expectation string) bool {
+ parts := strings.Split(expectation, ":")
+ if len(parts) == 1 {
+ match, _ := path.Match(parts[0], f.path)
+ return match
+ }
+ if len(parts) == 2 {
+ matchSrc, _ := path.Match(parts[0], f.src)
+ matchDst, _ := path.Match(parts[1], f.path)
+ return matchSrc && matchDst
+ }
+ panic("invalid expected file specification: " + expectation)
+}
+
func getFiles(t *testing.T, ctx *android.TestContext, moduleName, variant string) []fileInApex {
t.Helper()
- apexRule := ctx.ModuleForTests(moduleName, variant).Rule("apexRule")
+ module := ctx.ModuleForTests(moduleName, variant)
+ apexRule := module.MaybeRule("apexRule")
+ apexDir := "/image.apex/"
+ if apexRule.Rule == nil {
+ apexRule = module.Rule("zipApexRule")
+ apexDir = "/image.zipapex/"
+ }
copyCmds := apexRule.Args["copy_commands"]
- imageApexDir := "/image.apex/"
var ret []fileInApex
for _, cmd := range strings.Split(copyCmds, "&&") {
cmd = strings.TrimSpace(cmd)
@@ -3423,33 +3552,33 @@
t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
}
if dst != "" {
- index := strings.Index(dst, imageApexDir)
+ index := strings.Index(dst, apexDir)
if index == -1 {
- t.Fatal("copyCmds should copy a file to image.apex/", cmd)
+ t.Fatal("copyCmds should copy a file to "+apexDir, cmd)
}
- dstFile := dst[index+len(imageApexDir):]
+ dstFile := dst[index+len(apexDir):]
ret = append(ret, fileInApex{path: dstFile, src: src, isLink: isLink})
}
}
return ret
}
-func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) {
+func assertFileListEquals(t *testing.T, expectedFiles []string, actualFiles []fileInApex) {
t.Helper()
var failed bool
var surplus []string
filesMatched := make(map[string]bool)
- for _, file := range getFiles(t, ctx, moduleName, variant) {
- mactchFound := false
- for _, expected := range files {
- if matched, _ := path.Match(expected, file.path); matched {
+ for _, file := range actualFiles {
+ matchFound := false
+ for _, expected := range expectedFiles {
+ if file.match(expected) {
+ matchFound = true
filesMatched[expected] = true
- mactchFound = true
break
}
}
- if !mactchFound {
- surplus = append(surplus, file.path)
+ if !matchFound {
+ surplus = append(surplus, file.String())
}
}
@@ -3459,9 +3588,9 @@
failed = true
}
- if len(files) > len(filesMatched) {
+ if len(expectedFiles) > len(filesMatched) {
var missing []string
- for _, expected := range files {
+ for _, expected := range expectedFiles {
if !filesMatched[expected] {
missing = append(missing, expected)
}
@@ -3475,6 +3604,32 @@
}
}
+func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) {
+ assertFileListEquals(t, files, getFiles(t, ctx, moduleName, variant))
+}
+
+func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) {
+ deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Rule("deapexer")
+ outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1)
+ if deapexer.Output != nil {
+ outputs = append(outputs, deapexer.Output.String())
+ }
+ for _, output := range deapexer.ImplicitOutputs {
+ outputs = append(outputs, output.String())
+ }
+ actualFiles := make([]fileInApex, 0, len(outputs))
+ for _, output := range outputs {
+ dir := "/deapexer/"
+ pos := strings.LastIndex(output, dir)
+ if pos == -1 {
+ t.Fatal("Unknown deapexer output ", output)
+ }
+ path := output[pos+len(dir):]
+ actualFiles = append(actualFiles, fileInApex{path: path, src: "", isLink: false})
+ }
+ assertFileListEquals(t, files, actualFiles)
+}
+
func TestVndkApexCurrent(t *testing.T) {
commonFiles := []string{
"lib/libc++.so",
@@ -3756,11 +3911,9 @@
}`+vndkLibrariesTxtFiles("28", "current"))
assertApexName := func(expected, moduleName string) {
- bundle := ctx.ModuleForTests(moduleName, "android_common_image").Module().(*apexBundle)
- actual := proptools.String(bundle.properties.Apex_name)
- if !reflect.DeepEqual(actual, expected) {
- t.Errorf("Got '%v', expected '%v'", actual, expected)
- }
+ module := ctx.ModuleForTests(moduleName, "android_common_image")
+ apexManifestRule := module.Rule("apexManifestRule")
+ ensureContains(t, apexManifestRule.Args["opt"], "-v name "+expected)
}
assertApexName("com.android.vndk.v29", "com.android.vndk.current")
@@ -3942,6 +4095,179 @@
apexManifestRule := ctx.ModuleForTests("com.android.vndk.current", "android_common_image").Rule("apexManifestRule")
provideNativeLibs := names(apexManifestRule.Args["provideNativeLibs"])
ensureListEmpty(t, provideNativeLibs)
+ ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
+ "out/soong/.intermediates/libz/android_vendor.29_arm64_armv8-a_shared/libz.so:lib64/libz.so",
+ "out/soong/.intermediates/libz/android_vendor.29_arm_armv7-a-neon_shared/libz.so:lib/libz.so",
+ "*/*",
+ })
+}
+
+func TestVendorApexWithVndkPrebuilts(t *testing.T) {
+ ctx := testApex(t, "",
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = proptools.StringPtr("27")
+ }),
+ android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ cc.RegisterVendorSnapshotModules(ctx)
+ }),
+ withFiles(map[string][]byte{
+ "vendor/foo/Android.bp": []byte(`
+ apex {
+ name: "myapex",
+ binaries: ["foo"],
+ key: "myapex.key",
+ min_sdk_version: "27",
+ vendor: true,
+ }
+
+ cc_binary {
+ name: "foo",
+ vendor: true,
+ srcs: ["abc.cpp"],
+ shared_libs: [
+ "libllndk",
+ "libvndk",
+ ],
+ nocrt: true,
+ system_shared_libs: [],
+ min_sdk_version: "27",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `),
+ // Simulate VNDK prebuilts with vendor_snapshot
+ "prebuilts/vndk/Android.bp": []byte(`
+ vndk_prebuilt_shared {
+ name: "libllndk",
+ version: "27",
+ vendor_available: true,
+ product_available: true,
+ target_arch: "arm64",
+ arch: {
+ arm64: {
+ srcs: ["libllndk.so"],
+ },
+ },
+ }
+
+ vndk_prebuilt_shared {
+ name: "libvndk",
+ version: "27",
+ vendor_available: true,
+ product_available: true,
+ target_arch: "arm64",
+ arch: {
+ arm64: {
+ srcs: ["libvndk.so"],
+ },
+ },
+ vndk: {
+ enabled: true,
+ },
+ min_sdk_version: "27",
+ }
+
+ vndk_prebuilt_shared {
+ name: "libc++",
+ version: "27",
+ target_arch: "arm64",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ arch: {
+ arm64: {
+ srcs: ["libc++.so"],
+ },
+ },
+ min_sdk_version: "apex_inherit",
+ }
+
+ vendor_snapshot {
+ name: "vendor_snapshot",
+ version: "27",
+ arch: {
+ arm64: {
+ vndk_libs: [
+ "libc++",
+ "libllndk",
+ "libvndk",
+ ],
+ static_libs: [
+ "libc++demangle",
+ "libclang_rt.builtins",
+ "libunwind",
+ ],
+ },
+ }
+ }
+
+ vendor_snapshot_static {
+ name: "libclang_rt.builtins",
+ version: "27",
+ target_arch: "arm64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "libclang_rt.builtins-aarch64-android.a",
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "libc++demangle",
+ version: "27",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "libc++demangle.a",
+ },
+ },
+ min_sdk_version: "apex_inherit",
+ }
+
+ vendor_snapshot_static {
+ name: "libunwind",
+ version: "27",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "libunwind.a",
+ },
+ },
+ min_sdk_version: "apex_inherit",
+ }
+ `),
+ }))
+
+ // Should embed the prebuilt VNDK libraries in the apex
+ ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+ "bin/foo",
+ "prebuilts/vndk/libc++.so:lib64/libc++.so",
+ "prebuilts/vndk/libvndk.so:lib64/libvndk.so",
+ })
+
+ // Should link foo with prebuilt libraries (shared/static)
+ ldRule := ctx.ModuleForTests("foo", "android_vendor.27_arm64_armv8-a_myapex").Rule("ld")
+ android.AssertStringDoesContain(t, "should link to prebuilt llndk", ldRule.Args["libFlags"], "prebuilts/vndk/libllndk.so")
+ android.AssertStringDoesContain(t, "should link to prebuilt vndk", ldRule.Args["libFlags"], "prebuilts/vndk/libvndk.so")
+ android.AssertStringDoesContain(t, "should link to prebuilt libc++demangle", ldRule.Args["libFlags"], "prebuilts/vndk/libc++demangle.a")
+ android.AssertStringDoesContain(t, "should link to prebuilt libunwind", ldRule.Args["libFlags"], "prebuilts/vndk/libunwind.a")
+
+ // Should declare the LLNDK library as a "required" external dependency
+ manifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+ requireNativeLibs := names(manifestRule.Args["requireNativeLibs"])
+ ensureListContains(t, requireNativeLibs, "libllndk.so")
}
func TestDependenciesInApexManifest(t *testing.T) {
@@ -4052,57 +4378,11 @@
ensureListEmpty(t, requireNativeLibs)
}
-func TestApexName(t *testing.T) {
- ctx := testApex(t, `
- apex {
- name: "myapex",
- key: "myapex.key",
- apex_name: "com.android.myapex",
- native_shared_libs: ["mylib"],
- updatable: false,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "mylib",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- stl: "none",
- apex_available: [
- "//apex_available:platform",
- "myapex",
- ],
- }
- `)
-
- module := ctx.ModuleForTests("myapex", "android_common_com.android.myapex_image")
- apexManifestRule := module.Rule("apexManifestRule")
- ensureContains(t, apexManifestRule.Args["opt"], "-v name com.android.myapex")
- apexRule := module.Rule("apexRule")
- ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname")
-
- apexBundle := module.Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, ctx, apexBundle)
- name := apexBundle.BaseModuleName()
- prefix := "TARGET_"
- var builder strings.Builder
- data.Custom(&builder, name, prefix, "", data)
- androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
- ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n")
-}
-
func TestOverrideApexManifestDefaultVersion(t *testing.T) {
ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
- apex_name: "com.android.myapex",
native_shared_libs: ["mylib"],
updatable: false,
}
@@ -4127,11 +4407,81 @@
"OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234",
}))
- module := ctx.ModuleForTests("myapex", "android_common_com.android.myapex_image")
+ module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
apexManifestRule := module.Rule("apexManifestRule")
ensureContains(t, apexManifestRule.Args["default_version"], "1234")
}
+func TestCompileMultilibProp(t *testing.T) {
+ testCases := []struct {
+ compileMultiLibProp string
+ containedLibs []string
+ notContainedLibs []string
+ }{
+ {
+ containedLibs: []string{
+ "image.apex/lib64/mylib.so",
+ "image.apex/lib/mylib.so",
+ },
+ compileMultiLibProp: `compile_multilib: "both",`,
+ },
+ {
+ containedLibs: []string{"image.apex/lib64/mylib.so"},
+ notContainedLibs: []string{"image.apex/lib/mylib.so"},
+ compileMultiLibProp: `compile_multilib: "first",`,
+ },
+ {
+ containedLibs: []string{"image.apex/lib64/mylib.so"},
+ notContainedLibs: []string{"image.apex/lib/mylib.so"},
+ // compile_multilib, when unset, should result to the same output as when compile_multilib is "first"
+ },
+ {
+ containedLibs: []string{"image.apex/lib64/mylib.so"},
+ notContainedLibs: []string{"image.apex/lib/mylib.so"},
+ compileMultiLibProp: `compile_multilib: "64",`,
+ },
+ {
+ containedLibs: []string{"image.apex/lib/mylib.so"},
+ notContainedLibs: []string{"image.apex/lib64/mylib.so"},
+ compileMultiLibProp: `compile_multilib: "32",`,
+ },
+ }
+ for _, testCase := range testCases {
+ ctx := testApex(t, fmt.Sprintf(`
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ %s
+ native_shared_libs: ["mylib"],
+ updatable: false,
+ }
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ apex_available: [
+ "//apex_available:platform",
+ "myapex",
+ ],
+ }
+ `, testCase.compileMultiLibProp),
+ )
+ module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ apexRule := module.Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+ for _, containedLib := range testCase.containedLibs {
+ ensureContains(t, copyCmds, containedLib)
+ }
+ for _, notContainedLib := range testCase.notContainedLibs {
+ ensureNotContains(t, copyCmds, notContainedLib)
+ }
+ }
+}
+
func TestNonTestApex(t *testing.T) {
ctx := testApex(t, `
apex {
@@ -4641,6 +4991,9 @@
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"])
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, testingModule.Module())[0]
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "prebuilt_apex", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
}
func TestPrebuiltMissingSrc(t *testing.T) {
@@ -5096,7 +5449,16 @@
apex_set {
name: "myapex",
set: "myapex.apks",
+ exported_java_libs: ["myjavalib"],
exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ exported_systemserverclasspath_fragments: ["my-systemserverclasspath-fragment"],
+ }
+
+ java_import {
+ name: "myjavalib",
+ jars: ["myjavalib.jar"],
+ apex_available: ["myapex"],
+ permitted_packages: ["javalib"],
}
prebuilt_bootclasspath_fragment {
@@ -5113,6 +5475,12 @@
},
}
+ prebuilt_systemserverclasspath_fragment {
+ name: "my-systemserverclasspath-fragment",
+ contents: ["libbaz"],
+ apex_available: ["myapex"],
+ }
+
java_import {
name: "libfoo",
jars: ["libfoo.jar"],
@@ -5129,6 +5497,16 @@
shared_library: false,
permitted_packages: ["bar"],
}
+
+ java_sdk_library_import {
+ name: "libbaz",
+ public: {
+ jars: ["libbaz.jar"],
+ },
+ apex_available: ["myapex"],
+ shared_library: false,
+ permitted_packages: ["baz"],
+ }
`
ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
@@ -5141,6 +5519,24 @@
my-bootclasspath-fragment/index.csv
out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
`)
+
+ myApex := ctx.ModuleForTests("myapex", "android_common_myapex").Module()
+
+ overrideNames := []string{
+ "",
+ "myjavalib.myapex",
+ "libfoo.myapex",
+ "libbar.myapex",
+ "libbaz.myapex",
+ }
+ mkEntries := android.AndroidMkEntriesForTest(t, ctx, myApex)
+ for i, e := range mkEntries {
+ g := e.OverrideName
+ if w := overrideNames[i]; w != g {
+ t.Errorf("Expected override name %q, got %q", w, g)
+ }
+ }
+
})
t.Run("prebuilt with source library preferred", func(t *testing.T) {
@@ -5461,6 +5857,67 @@
})
}
+func TestPrebuiltSkipsSymbols(t *testing.T) {
+ testCases := []struct {
+ name string
+ usePrebuilt bool
+ installSymbolFiles bool
+ }{
+ {
+ name: "Source module build rule doesn't install symbol files",
+ usePrebuilt: true,
+ installSymbolFiles: false,
+ },
+ {
+ name: "Source module is installed with symbols",
+ usePrebuilt: false,
+ installSymbolFiles: true,
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ preferProperty := "prefer: false"
+ if tc.usePrebuilt {
+ preferProperty = "prefer: true"
+ }
+ ctx := testApex(t, `
+ // Source module
+ apex {
+ name: "myapex",
+ binaries: ["foo"],
+ key: "myapex.key",
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ apex_set {
+ name: "myapex",
+ set: "myapex.apks",
+ `+preferProperty+`
+ }
+
+ cc_binary {
+ name: "foo",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [ "myapex" ],
+ }
+ `)
+ // Symbol files are installed by installing entries under ${OUT}/apex/{apex name}
+ android.AssertStringListContainsEquals(t, "Installs",
+ ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().FilesToInstall().Strings(),
+ filepath.Join(ctx.Config().SoongOutDir(), "target/product/test_device/apex/myapex/bin/foo"),
+ tc.installSymbolFiles)
+ })
+ }
+}
+
func TestApexWithTests(t *testing.T) {
ctx := testApex(t, `
apex_test {
@@ -5591,12 +6048,12 @@
}),
)
ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- ensureListContains(t, ab.requiredDeps, "myapex.flattened")
+ ensureListContains(t, ab.makeModulesToInstall, "myapex.flattened")
mk := android.AndroidMkDataForTest(t, ctx, ab)
var builder strings.Builder
mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += myapex.flattened")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex myapex.flattened\n")
}
func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
@@ -6191,9 +6648,6 @@
apps: ["app"],
bpfs: ["bpf"],
prebuilts: ["myetc"],
- bootclasspath_fragments: ["mybootclasspath_fragment"],
- systemserverclasspath_fragments: ["mysystemserverclasspath_fragment"],
- java_libs: ["myjava_library"],
overrides: ["oldapex"],
updatable: false,
}
@@ -6202,11 +6656,8 @@
name: "override_myapex",
base: "myapex",
apps: ["override_app"],
- bpfs: ["override_bpf"],
+ bpfs: ["overrideBpf"],
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",
@@ -6252,8 +6703,8 @@
}
bpf {
- name: "override_bpf",
- srcs: ["override_bpf.c"],
+ name: "overrideBpf",
+ srcs: ["overrideBpf.c"],
}
prebuilt_etc {
@@ -6265,78 +6716,6 @@
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)
@@ -6356,7 +6735,7 @@
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")
+ ensureContains(t, copyCmds, "image.apex/etc/bpf/overrideBpf.o")
ensureNotContains(t, copyCmds, "image.apex/etc/myetc")
ensureContains(t, copyCmds, "image.apex/etc/override_myetc")
@@ -6371,13 +6750,6 @@
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")
@@ -6390,20 +6762,14 @@
data.Custom(&builder, name, "TARGET_", "", data)
androidMk := builder.String()
ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
- ensureContains(t, androidMk, "LOCAL_MODULE := override_bpf.o.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := overrideBpf.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")
}
@@ -6890,6 +7256,42 @@
})
}
+func TestNoDupeApexFiles(t *testing.T) {
+ android.GroupFixturePreparers(
+ android.PrepareForTestWithAndroidBuildComponents,
+ PrepareForTestWithApexBuildComponents,
+ prepareForTestWithMyapex,
+ prebuilt_etc.PrepareForTestWithPrebuiltEtc,
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("is provided by two different files")).
+ RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ prebuilts: ["foo", "bar"],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ prebuilt_etc {
+ name: "foo",
+ src: "myprebuilt",
+ filename_from_src: true,
+ }
+
+ prebuilt_etc {
+ name: "bar",
+ src: "myprebuilt",
+ filename_from_src: true,
+ }
+ `)
+}
+
func TestRejectNonInstallableJavaLibrary(t *testing.T) {
testApexError(t, `"myjar" is not configured to be compiled into dex`, `
apex {
@@ -6950,9 +7352,9 @@
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += a b\n")
- ensureContains(t, androidMk, "LOCAL_HOST_REQUIRED_MODULES += c d\n")
- ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES += e f\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex a b\n")
+ ensureContains(t, androidMk, "LOCAL_HOST_REQUIRED_MODULES := c d\n")
+ ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES := e f\n")
}
func TestSymlinksFromApexToSystem(t *testing.T) {
@@ -6971,7 +7373,7 @@
native_shared_libs: ["mylib"],
java_libs: ["myjar"],
updatable: true,
- min_sdk_version: "current",
+ min_sdk_version: "33",
}
apex_key {
@@ -6983,7 +7385,10 @@
cc_library {
name: "mylib",
srcs: ["mylib.cpp"],
- shared_libs: ["myotherlib"],
+ shared_libs: [
+ "myotherlib",
+ "myotherlib_ext",
+ ],
system_shared_libs: [],
stl: "none",
apex_available: [
@@ -6991,7 +7396,7 @@
"myapex.updatable",
"//apex_available:platform",
],
- min_sdk_version: "current",
+ min_sdk_version: "33",
}
cc_library {
@@ -7004,7 +7409,21 @@
"myapex.updatable",
"//apex_available:platform",
],
- min_sdk_version: "current",
+ min_sdk_version: "33",
+ }
+
+ cc_library {
+ name: "myotherlib_ext",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ system_ext_specific: true,
+ stl: "none",
+ apex_available: [
+ "myapex",
+ "myapex.updatable",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "33",
}
java_library {
@@ -7018,7 +7437,7 @@
"myapex.updatable",
"//apex_available:platform",
],
- min_sdk_version: "current",
+ min_sdk_version: "33",
}
java_library {
@@ -7031,7 +7450,7 @@
"myapex.updatable",
"//apex_available:platform",
],
- min_sdk_version: "current",
+ min_sdk_version: "33",
}
`
@@ -7047,12 +7466,15 @@
t.Errorf("%q is not found", file)
}
- ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string) {
+ ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string, target string) {
for _, f := range files {
if f.path == file {
if !f.isLink {
t.Errorf("%q is not a symlink", file)
}
+ if f.src != target {
+ t.Errorf("expected symlink target to be %q, got %q", target, f.src)
+ }
return
}
}
@@ -7066,23 +7488,27 @@
ensureRealfileExists(t, files, "javalib/myjar.jar")
ensureRealfileExists(t, files, "lib64/mylib.so")
ensureRealfileExists(t, files, "lib64/myotherlib.so")
+ ensureRealfileExists(t, files, "lib64/myotherlib_ext.so")
files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image")
ensureRealfileExists(t, files, "javalib/myjar.jar")
ensureRealfileExists(t, files, "lib64/mylib.so")
ensureRealfileExists(t, files, "lib64/myotherlib.so")
+ ensureRealfileExists(t, files, "lib64/myotherlib_ext.so")
// For bundled build, symlink to the system for the non-updatable APEXes only
ctx = testApex(t, bp)
files = getFiles(t, ctx, "myapex", "android_common_myapex_image")
ensureRealfileExists(t, files, "javalib/myjar.jar")
ensureRealfileExists(t, files, "lib64/mylib.so")
- ensureSymlinkExists(t, files, "lib64/myotherlib.so") // this is symlink
+ ensureSymlinkExists(t, files, "lib64/myotherlib.so", "/system/lib64/myotherlib.so") // this is symlink
+ ensureSymlinkExists(t, files, "lib64/myotherlib_ext.so", "/system_ext/lib64/myotherlib_ext.so") // this is symlink
files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image")
ensureRealfileExists(t, files, "javalib/myjar.jar")
ensureRealfileExists(t, files, "lib64/mylib.so")
- ensureRealfileExists(t, files, "lib64/myotherlib.so") // this is a real file
+ ensureRealfileExists(t, files, "lib64/myotherlib.so") // this is a real file
+ ensureRealfileExists(t, files, "lib64/myotherlib_ext.so") // this is a real file
}
func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) {
@@ -7134,7 +7560,7 @@
ensureNotContains(t, androidMk, "LOCAL_MODULE := prebuilt_myotherlib.myapex\n")
ensureNotContains(t, androidMk, "LOCAL_MODULE := myotherlib.myapex\n")
// `myapex` should have `myotherlib` in its required line, not `prebuilt_myotherlib`
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += mylib.myapex:64 myotherlib:64 apex_manifest.pb.myapex apex_pubkey.myapex\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 myotherlib:64 apex_manifest.pb.myapex apex_pubkey.myapex\n")
}
func TestApexWithJniLibs(t *testing.T) {
@@ -7285,12 +7711,18 @@
ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
s := mod.Rule("apexRule").Args["copy_commands"]
copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
- if len(copyCmds) != 3 {
- t.Fatalf("Expected 3 commands, got %d in:\n%s", len(copyCmds), s)
+ if len(copyCmds) != 4 {
+ t.Fatalf("Expected 4 commands, got %d in:\n%s", len(copyCmds), s)
}
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$")
+ ensureMatches(t, copyCmds[2], "^cp -f .*/app/AppSet@TEST.BUILD_ID/AppSet.apk$")
+ ensureMatches(t, copyCmds[3], "^unzip .*-d .*/app/AppSet@TEST.BUILD_ID .*/AppSet.zip$")
+
+ // Ensure that canned_fs_config has an entry for the app set zip file
+ generateFsRule := mod.Rule("generateFsConfig")
+ cmd := generateFsRule.RuleParams.Command
+ ensureContains(t, cmd, "AppSet.zip")
}
func TestAppSetBundlePrebuilt(t *testing.T) {
@@ -7321,6 +7753,28 @@
android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
}
+func TestApexSetApksModuleAssignment(t *testing.T) {
+ ctx := testApex(t, `
+ apex_set {
+ name: "myapex",
+ set: ":myapex_apks_file",
+ }
+
+ filegroup {
+ name: "myapex_apks_file",
+ srcs: ["myapex.apks"],
+ }
+ `)
+
+ m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+
+ // Check that the extractor produces the correct apks file from the input module
+ extractorOutput := "out/soong/.intermediates/myapex.apex.extractor/android_common/extracted/myapex.apks"
+ extractedApex := m.Output(extractorOutput)
+
+ android.AssertArrayString(t, "extractor input", []string{"myapex.apks"}, extractedApex.Inputs.Strings())
+}
+
func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) {
t.Helper()
@@ -7333,6 +7787,7 @@
"some-updatable-apex",
],
permitted_packages: ["some.updatable.apex.lib"],
+ min_sdk_version: "33",
}
java_library {
@@ -7372,6 +7827,7 @@
],
hostdex: true,
compile_dex: true,
+ min_sdk_version: "33",
}
apex {
@@ -7379,7 +7835,7 @@
key: "some-updatable-apex.key",
java_libs: ["some-updatable-apex-lib"],
updatable: true,
- min_sdk_version: "current",
+ min_sdk_version: "33",
}
apex {
@@ -7402,7 +7858,7 @@
key: "com.android.art.debug.key",
bootclasspath_fragments: ["art-bootclasspath-fragment"],
updatable: true,
- min_sdk_version: "current",
+ min_sdk_version: "33",
}
bootclasspath_fragment {
@@ -7692,12 +8148,13 @@
`)
}
-func TestUpdatable_cannot_be_vendor_apex(t *testing.T) {
- testApexError(t, `"myapex" .*: updatable: vendor APEXes are not updatable`, `
+func Test_use_vndk_as_stable_shouldnt_be_used_for_updatable_vendor_apexes(t *testing.T) {
+ testApexError(t, `"myapex" .*: use_vndk_as_stable: updatable APEXes can't use external VNDK libs`, `
apex {
name: "myapex",
key: "myapex.key",
updatable: true,
+ use_vndk_as_stable: true,
soc_specific: true,
}
@@ -7709,6 +8166,42 @@
`)
}
+func Test_use_vndk_as_stable_shouldnt_be_used_with_min_sdk_version(t *testing.T) {
+ testApexError(t, `"myapex" .*: use_vndk_as_stable: not supported when min_sdk_version is set`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: false,
+ min_sdk_version: "29",
+ use_vndk_as_stable: true,
+ vendor: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+}
+
+func Test_use_vndk_as_stable_shouldnt_be_used_for_non_vendor_apexes(t *testing.T) {
+ testApexError(t, `"myapex" .*: use_vndk_as_stable: not supported for system/system_ext APEXes`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: false,
+ use_vndk_as_stable: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+}
+
func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) {
testApexError(t, `"mysystemserverclasspathfragment" .* it must not set generate_classpaths_proto to false`, `
apex {
@@ -7796,8 +8289,8 @@
testNoUpdatableJarsInBootImage(t, "", preparer, fragments...)
})
- t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
- err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the framework boot image`
+ t.Run("updatable jar from ART apex in the platform bootclasspath => error", func(t *testing.T) {
+ err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the platform bootclasspath`
// Update the dexpreopt BootJars directly.
preparer := android.GroupFixturePreparers(
prepareSetBootJars("com.android.art.debug:some-art-lib"),
@@ -7820,8 +8313,8 @@
testNoUpdatableJarsInBootImage(t, err, preparer)
})
- t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
- err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
+ t.Run("updatable jar from some other apex in the platform bootclasspath => error", func(t *testing.T) {
+ err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the platform bootclasspath`
preparer := android.GroupFixturePreparers(
java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib"),
java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
@@ -7829,7 +8322,7 @@
testNoUpdatableJarsInBootImage(t, err, preparer)
})
- t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
+ t.Run("non-updatable jar from some other apex in the platform bootclasspath => ok", func(t *testing.T) {
preparer := java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
fragment := java.ApexVariantReference{
Apex: proptools.StringPtr("some-non-updatable-apex"),
@@ -7844,7 +8337,7 @@
testNoUpdatableJarsInBootImage(t, err, preparer)
})
- t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
+ t.Run("nonexistent jar in the platform bootclasspath => error", func(t *testing.T) {
err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
preparer := java.FixtureConfigureBootJars("platform:nonexistent")
testNoUpdatableJarsInBootImage(t, err, preparer)
@@ -7857,7 +8350,7 @@
testNoUpdatableJarsInBootImage(t, err, preparer)
})
- t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
+ t.Run("platform jar in the platform bootclasspath => ok", func(t *testing.T) {
preparer := android.GroupFixturePreparers(
java.FixtureConfigureBootJars("platform:some-platform-lib"),
java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
@@ -8317,6 +8810,30 @@
}
}
+func TestApexSet_NativeBridge(t *testing.T) {
+ ctx := testApex(t, `
+ apex_set {
+ name: "myapex",
+ set: "myapex.apks",
+ filename: "foo_v2.apex",
+ overrides: ["foo"],
+ }
+ `,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.Targets[android.Android] = []android.Target{
+ {Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "", Abi: []string{"x86_64"}}},
+ {Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled},
+ }
+ }),
+ )
+
+ m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+
+ // Check extract_apks tool parameters. No native bridge arch expected
+ extractedApex := m.Output("extracted/myapex.apks")
+ android.AssertStringEquals(t, "abis", "X86_64", extractedApex.Args["abis"])
+}
+
func TestNoStaticLinkingToStubsLib(t *testing.T) {
testApexError(t, `.*required by "mylib" is a native library providing stub.*`, `
apex {
@@ -8595,7 +9112,7 @@
// The make level dependency needs to be on otherlib - prebuilt_otherlib isn't
// a thing there.
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherlib\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++:64 mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex otherlib\n")
}
func TestExcludeDependency(t *testing.T) {
@@ -8989,7 +9506,7 @@
var builder strings.Builder
data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n")
}
func TestAndroidMk_DexpreoptBuiltInstalledForApex_Prebuilt(t *testing.T) {
@@ -9065,7 +9582,7 @@
var builder strings.Builder
data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherapex")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex otherapex")
}
func TestAndroidMk_RequiredDeps(t *testing.T) {
@@ -9084,20 +9601,20 @@
`)
bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- bundle.requiredDeps = append(bundle.requiredDeps, "foo")
+ bundle.makeModulesToInstall = append(bundle.makeModulesToInstall, "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")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex foo\n")
flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
- flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
+ flattenedBundle.makeModulesToInstall = append(flattenedBundle.makeModulesToInstall, "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")
+ ensureContains(t, flattenedAndroidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex.flattened apex_pubkey.myapex.flattened foo\n")
}
func TestApexOutputFileProducer(t *testing.T) {
@@ -9336,7 +9853,7 @@
func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
for _, dep := range deps {
- android.AssertStringListContains(t, "", a.requiredDeps, dep)
+ android.AssertStringListContains(t, "", a.makeModulesToInstall, dep)
}
}
@@ -9344,191 +9861,192 @@
func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
for _, dep := range deps {
- android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep)
+ android.AssertStringListDoesNotContain(t, "", a.makeModulesToInstall, dep)
}
}
-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)
- }
-}
+// TODO(b/193460475): Re-enable this test
+//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) {
@@ -9593,6 +10111,313 @@
}
}
-func TestMain(m *testing.M) {
- os.Exit(m.Run())
+func TestApexBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libbaz"],
+ binaries: ["binfoo"],
+ min_sdk_version: "29",
+ }
+ apex_key {
+ name: "myapex.key",
+ }
+ cc_binary {
+ name: "binfoo",
+ shared_libs: ["libbar", "libbaz", "libqux",],
+ apex_available: ["myapex"],
+ min_sdk_version: "29",
+ recovery_available: false,
+ }
+ cc_library {
+ name: "libbar",
+ srcs: ["libbar.cc"],
+ stubs: {
+ symbol_file: "libbar.map.txt",
+ versions: [
+ "29",
+ ],
+ },
+ }
+ cc_library {
+ name: "libbaz",
+ srcs: ["libbaz.cc"],
+ apex_available: ["myapex"],
+ min_sdk_version: "29",
+ stubs: {
+ symbol_file: "libbaz.map.txt",
+ versions: [
+ "29",
+ ],
+ },
+ }
+ cc_api_library {
+ name: "libbar",
+ src: "libbar_stub.so",
+ min_sdk_version: "29",
+ variants: ["apex.29"],
+ }
+ cc_api_variant {
+ name: "libbar",
+ variant: "apex",
+ version: "29",
+ src: "libbar_apex_29.so",
+ }
+ cc_api_library {
+ name: "libbaz",
+ src: "libbaz_stub.so",
+ min_sdk_version: "29",
+ variants: ["apex.29"],
+ }
+ cc_api_variant {
+ name: "libbaz",
+ variant: "apex",
+ version: "29",
+ src: "libbaz_apex_29.so",
+ }
+ cc_api_library {
+ name: "libqux",
+ src: "libqux_stub.so",
+ min_sdk_version: "29",
+ variants: ["apex.29"],
+ }
+ cc_api_variant {
+ name: "libqux",
+ variant: "apex",
+ version: "29",
+ src: "libqux_apex_29.so",
+ }
+ api_imports {
+ name: "api_imports",
+ apex_shared_libs: [
+ "libbar",
+ "libbaz",
+ "libqux",
+ ],
+ }
+ `
+ result := testApex(t, bp)
+
+ hasDep := func(m android.Module, wantDep android.Module) bool {
+ t.Helper()
+ var found bool
+ result.VisitDirectDeps(m, func(dep blueprint.Module) {
+ if dep == wantDep {
+ found = true
+ }
+ })
+ return found
+ }
+
+ // Library defines stubs and cc_api_library should be used with cc_api_library
+ binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Module()
+ libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+ libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+
+ android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
+ android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
+
+ binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Rule("ld").Args["libFlags"]
+ android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
+ android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
+ android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
+
+ // Library defined in the same APEX should be linked with original definition instead of cc_api_library
+ libbazApexVariant := result.ModuleForTests("libbaz", "android_arm64_armv8-a_shared_apex29").Module()
+ libbazApiImportCoreVariant := result.ModuleForTests("libbaz.apiimport", "android_arm64_armv8-a_shared").Module()
+ android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even from same APEX", true, hasDep(binfooApexVariant, libbazApiImportCoreVariant))
+ android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbazApexVariant))
+
+ android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbaz.so")
+ android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbaz.apiimport.so")
+ android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbaz.apex.29.apiimport.so")
+
+ // cc_api_library defined without original library should be linked with cc_api_library
+ libquxApiImportApexVariant := result.ModuleForTests("libqux.apiimport", "android_arm64_armv8-a_shared").Module()
+ android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even original library definition does not exist", true, hasDep(binfooApexVariant, libquxApiImportApexVariant))
+ android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libqux.apex.29.apiimport.so")
+}
+
+func TestPlatformBinaryBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libbar"],
+ min_sdk_version: "29",
+ }
+ apex_key {
+ name: "myapex.key",
+ }
+ cc_binary {
+ name: "binfoo",
+ shared_libs: ["libbar"],
+ recovery_available: false,
+ }
+ cc_library {
+ name: "libbar",
+ srcs: ["libbar.cc"],
+ apex_available: ["myapex"],
+ min_sdk_version: "29",
+ stubs: {
+ symbol_file: "libbar.map.txt",
+ versions: [
+ "29",
+ ],
+ },
+ }
+ cc_api_library {
+ name: "libbar",
+ src: "libbar_stub.so",
+ variants: ["apex.29"],
+ }
+ cc_api_variant {
+ name: "libbar",
+ variant: "apex",
+ version: "29",
+ src: "libbar_apex_29.so",
+ }
+ api_imports {
+ name: "api_imports",
+ apex_shared_libs: [
+ "libbar",
+ ],
+ }
+ `
+
+ result := testApex(t, bp)
+
+ hasDep := func(m android.Module, wantDep android.Module) bool {
+ t.Helper()
+ var found bool
+ result.VisitDirectDeps(m, func(dep blueprint.Module) {
+ if dep == wantDep {
+ found = true
+ }
+ })
+ return found
+ }
+
+ // Library defines stubs and cc_api_library should be used with cc_api_library
+ binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Module()
+ libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+ libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+
+ android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
+ android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
+
+ binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
+ android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
+ android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
+ android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
+}
+
+func TestTrimmedApex(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo","libbaz"],
+ min_sdk_version: "29",
+ trim_against: "mydcla",
+ }
+ apex {
+ name: "mydcla",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo","libbar"],
+ min_sdk_version: "29",
+ file_contexts: ":myapex-file_contexts",
+ dynamic_common_lib_apex: true,
+ }
+ apex_key {
+ name: "myapex.key",
+ }
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libc"],
+ apex_available: ["myapex","mydcla"],
+ min_sdk_version: "29",
+ }
+ cc_library {
+ name: "libbar",
+ shared_libs: ["libc"],
+ apex_available: ["myapex","mydcla"],
+ min_sdk_version: "29",
+ }
+ cc_library {
+ name: "libbaz",
+ shared_libs: ["libc"],
+ apex_available: ["myapex","mydcla"],
+ min_sdk_version: "29",
+ }
+ cc_api_library {
+ name: "libc",
+ src: "libc.so",
+ min_sdk_version: "29",
+ recovery_available: true,
+ }
+ api_imports {
+ name: "api_imports",
+ shared_libs: [
+ "libc",
+ ],
+ header_libs: [],
+ }
+ `
+ ctx := testApex(t, bp)
+ module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ apexRule := module.MaybeRule("apexRule")
+ if apexRule.Rule == nil {
+ t.Errorf("Expecting regular apex rule but a non regular apex rule found")
+ }
+
+ ctx = testApex(t, bp, android.FixtureModifyConfig(android.SetTrimmedApexEnabledForTests))
+ trimmedApexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("TrimmedApexRule")
+ libs_to_trim := trimmedApexRule.Args["libs_to_trim"]
+ android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libfoo")
+ android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libbar")
+ android.AssertStringDoesNotContain(t, "unexpected libs in the libs to trim", libs_to_trim, "libbaz")
+}
+
+func TestCannedFsConfig(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",
+ }`)
+ mod := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ generateFsRule := mod.Rule("generateFsConfig")
+ cmd := generateFsRule.RuleParams.Command
+
+ ensureContains(t, cmd, `( echo '/ 1000 1000 0755'; echo '/apex_manifest.json 1000 1000 0644'; echo '/apex_manifest.pb 1000 1000 0644'; ) >`)
+}
+
+func TestCannedFsConfig_HasCustomConfig(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ canned_fs_config: "my_config",
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }`)
+ mod := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ generateFsRule := mod.Rule("generateFsConfig")
+ cmd := generateFsRule.RuleParams.Command
+
+ // Ensure that canned_fs_config has "cat my_config" at the end
+ ensureContains(t, cmd, `( echo '/ 1000 1000 0755'; echo '/apex_manifest.json 1000 1000 0644'; echo '/apex_manifest.pb 1000 1000 0644'; cat my_config ) >`)
}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index b298dac..1b52886 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -71,10 +71,6 @@
name: "com.android.art",
key: "com.android.art.key",
bootclasspath_fragments: ["art-bootclasspath-fragment"],
- java_libs: [
- "baz",
- "quuz",
- ],
updatable: false,
}
@@ -119,20 +115,7 @@
// Make sure that the art-bootclasspath-fragment is using the correct configuration.
checkBootclasspathFragment(t, result, "art-bootclasspath-fragment", "android_common_apex10000",
- "com.android.art:baz,com.android.art:quuz", `
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.art
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.oat
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.vdex
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.art
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.oat
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.vdex
-`)
+ "com.android.art:baz,com.android.art:quuz")
}
func TestBootclasspathFragments_FragmentDependency(t *testing.T) {
@@ -265,7 +248,7 @@
checkAPIScopeStubs("other", otherInfo, java.CorePlatformHiddenAPIScope)
}
-func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName, variantName string, expectedConfiguredModules string, expectedBootclasspathFragmentFiles string) {
+func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName, variantName string, expectedConfiguredModules string) {
t.Helper()
bootclasspathFragment := result.ModuleForTests(moduleName, variantName).Module().(*java.BootclasspathFragmentModule)
@@ -273,19 +256,6 @@
bootclasspathFragmentInfo := result.ModuleProvider(bootclasspathFragment, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo)
modules := bootclasspathFragmentInfo.Modules()
android.AssertStringEquals(t, "invalid modules for "+moduleName, expectedConfiguredModules, modules.String())
-
- // Get a list of all the paths in the boot image sorted by arch type.
- allPaths := []string{}
- bootImageFilesByArchType := bootclasspathFragmentInfo.AndroidBootImageFilesByArchType()
- for _, archType := range android.ArchTypeList() {
- if paths, ok := bootImageFilesByArchType[archType]; ok {
- for _, path := range paths {
- allPaths = append(allPaths, android.NormalizePathForTesting(path))
- }
- }
- }
-
- android.AssertTrimmedStringEquals(t, "invalid paths for "+moduleName, expectedBootclasspathFragmentFiles, strings.Join(allPaths, "\n"))
}
func TestBootclasspathFragmentInArtApex(t *testing.T) {
@@ -301,11 +271,7 @@
"mybootclasspathfragment",
],
// bar (like foo) should be transitively included in this apex because it is part of the
- // mybootclasspathfragment bootclasspath_fragment. However, it is kept here to ensure that the
- // apex dedups the files correctly.
- java_libs: [
- "bar",
- ],
+ // mybootclasspathfragment bootclasspath_fragment.
updatable: false,
}
@@ -428,51 +394,22 @@
ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
"etc/boot-image.prof",
"etc/classpaths/bootclasspath.pb",
- "javalib/arm/boot.art",
- "javalib/arm/boot.oat",
- "javalib/arm/boot.vdex",
- "javalib/arm/boot-bar.art",
- "javalib/arm/boot-bar.oat",
- "javalib/arm/boot-bar.vdex",
- "javalib/arm64/boot.art",
- "javalib/arm64/boot.oat",
- "javalib/arm64/boot.vdex",
- "javalib/arm64/boot-bar.art",
- "javalib/arm64/boot-bar.oat",
- "javalib/arm64/boot-bar.vdex",
"javalib/bar.jar",
"javalib/foo.jar",
})
java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
- `bar`,
`com.android.art.key`,
`mybootclasspathfragment`,
})
- // 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 source bootclasspath_fragment copies its dex files to the predefined
// locations for the art image.
module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
})
- t.Run("boot image files from source no boot image in apex", func(t *testing.T) {
+ t.Run("generate boot image profile even if dexpreopt is disabled", func(t *testing.T) {
result := android.GroupFixturePreparers(
commonPreparer,
@@ -481,6 +418,7 @@
java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
addSource("foo", "bar"),
java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
+ dexpreopt.FixtureDisableDexpreoptBootImages(true),
).RunTest(t)
ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
@@ -489,21 +427,6 @@
"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("boot image disable generate profile", func(t *testing.T) {
@@ -539,92 +462,24 @@
java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
).RunTest(t)
- ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ ensureExactDeapexedContents(t, result.TestContext, "com.android.art", "android_common", []string{
"etc/boot-image.prof",
- "etc/classpaths/bootclasspath.pb",
- "javalib/arm/boot.art",
- "javalib/arm/boot.oat",
- "javalib/arm/boot.vdex",
- "javalib/arm/boot-bar.art",
- "javalib/arm/boot-bar.oat",
- "javalib/arm/boot-bar.vdex",
- "javalib/arm64/boot.art",
- "javalib/arm64/boot.oat",
- "javalib/arm64/boot.vdex",
- "javalib/arm64/boot-bar.art",
- "javalib/arm64/boot-bar.oat",
- "javalib/arm64/boot-bar.vdex",
"javalib/bar.jar",
"javalib/foo.jar",
})
java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
- `bar`,
`com.android.art.key`,
`mybootclasspathfragment`,
`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,
@@ -774,10 +629,6 @@
module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
-
- // Check that the right deapexer module was chosen for a boot image.
- param := module.Output("out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art")
- android.AssertStringDoesContain(t, "didn't find the expected deapexer in the input path", param.Input.String(), "/com.android.art.deapexer")
})
t.Run("enabled alternative APEX", func(t *testing.T) {
@@ -794,7 +645,7 @@
bootJarLocations := []string{}
for _, output := range module.AllOutputs() {
output = android.StringRelativeToTop(config, output)
- if strings.HasPrefix(output, "out/soong/test_device/dex_artjars_input/") {
+ if strings.HasPrefix(output, "out/soong/dexpreopt_arm64/dex_artjars_input/") {
bootJarLocations = append(bootJarLocations, output)
}
}
@@ -802,7 +653,7 @@
sort.Strings(bootJarLocations)
expected := []string{}
for _, m := range modules {
- expected = append(expected, fmt.Sprintf("out/soong/test_device/dex_artjars_input/%s.jar", m))
+ expected = append(expected, fmt.Sprintf("out/soong/dexpreopt_arm64/dex_artjars_input/%s.jar", m))
}
sort.Strings(expected)
@@ -1105,10 +956,6 @@
name: "com.android.art",
key: "com.android.art.key",
bootclasspath_fragments: ["art-bootclasspath-fragment"],
- java_libs: [
- "baz",
- "quuz",
- ],
updatable: false,
}
@@ -1246,7 +1093,7 @@
func TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForTestWithBootclasspathFragment,
- java.PrepareForTestWithJavaDefaultModules,
+ java.PrepareForTestWithDexpreopt,
prepareForTestWithArtApex,
prepareForTestWithMyapex,
// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
@@ -1270,10 +1117,6 @@
name: "com.android.art",
key: "com.android.art.key",
bootclasspath_fragments: ["art-bootclasspath-fragment"],
- java_libs: [
- "baz",
- "quuz",
- ],
updatable: false,
}
diff --git a/apex/bp2build.go b/apex/bp2build.go
new file mode 100644
index 0000000..a3dda83
--- /dev/null
+++ b/apex/bp2build.go
@@ -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 apex
+
+import (
+ "android/soong/android"
+ "encoding/json"
+ "strings"
+)
+
+// This file contains the bp2build integration for the apex package.
+
+// Export constants as Starlark using bp2build to Bazel.
+func BazelApexToolchainVars() (string, error) {
+ marshalled, err := json.Marshal(apexAvailBaseline)
+ if err != nil {
+ return "", err
+ }
+ content := []string{
+ "# GENERATED BY SOONG. DO NOT EDIT.",
+ "default_manifest_version = " + android.DefaultUpdatableModuleVersion, // constants.go is different in every branch.
+ "apex_available_baseline = json.decode('''" + string(marshalled) + "''')",
+ }
+ return strings.Join(content, "\n"), nil
+}
diff --git a/apex/bp2build_test.go b/apex/bp2build_test.go
new file mode 100644
index 0000000..2a0f6e9
--- /dev/null
+++ b/apex/bp2build_test.go
@@ -0,0 +1,461 @@
+// 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 apex
+
+import (
+ "android/soong/android"
+ "android/soong/bazel/cquery"
+ "strings"
+ "testing"
+)
+
+func TestApexImageInMixedBuilds(t *testing.T) {
+ bp := `
+apex_key{
+ name: "foo_key",
+}
+
+apex {
+ name: "foo",
+ key: "foo_key",
+ updatable: true,
+ min_sdk_version: "31",
+ file_contexts: ":myapex-file_contexts",
+ bazel_module: { label: "//:foo" },
+}`
+
+ outputBaseDir := "out/bazel"
+ result := android.GroupFixturePreparers(
+ prepareForApexTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outputBaseDir,
+ LabelToApexInfo: map[string]cquery.ApexInfo{
+ "//:foo": cquery.ApexInfo{
+ // ApexInfo Starlark provider.
+ SignedOutput: "signed_out.apex",
+ SignedCompressedOutput: "signed_out.capex",
+ UnsignedOutput: "unsigned_out.apex",
+ BundleKeyInfo: []string{"public_key", "private_key"},
+ ContainerKeyInfo: []string{"container_cert", "container_private"},
+ SymbolsUsedByApex: "foo_using.txt",
+ JavaSymbolsUsedByApex: "foo_using.xml",
+ BundleFile: "apex_bundle.zip",
+ InstalledFiles: "installed-files.txt",
+ RequiresLibs: []string{"//path/c:c", "//path/d:d"},
+
+ // unused
+ PackageName: "pkg_name",
+ ProvidesLibs: []string{"a", "b"},
+
+ // ApexMkInfo Starlark provider
+ PayloadFilesInfo: []map[string]string{
+ {
+ "built_file": "bazel-out/adbd",
+ "install_dir": "bin",
+ "class": "nativeExecutable",
+ "make_module_name": "adbd",
+ "basename": "adbd",
+ "package": "foo",
+ },
+ },
+ MakeModulesToInstall: []string{"c"}, // d deliberately omitted
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+
+ m := result.ModuleForTests("foo", "android_common_foo_image").Module()
+ ab, ok := m.(*apexBundle)
+
+ if !ok {
+ t.Fatalf("Expected module to be an apexBundle, was not")
+ }
+
+ // TODO: refactor to android.AssertStringEquals
+ if w, g := "out/bazel/execroot/__main__/public_key", ab.publicKeyFile.String(); w != g {
+ t.Errorf("Expected public key %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/private_key", ab.privateKeyFile.String(); w != g {
+ t.Errorf("Expected private key %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/container_cert", ab.containerCertificateFile.String(); w != g {
+ t.Errorf("Expected public container key %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/container_private", ab.containerPrivateKeyFile.String(); w != g {
+ t.Errorf("Expected private container key %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/signed_out.apex", ab.outputFile.String(); w != g {
+ t.Errorf("Expected output file %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/foo_using.txt", ab.nativeApisUsedByModuleFile.String(); w != g {
+ t.Errorf("Expected output file %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/foo_using.xml", ab.javaApisUsedByModuleFile.String(); w != g {
+ t.Errorf("Expected output file %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/installed-files.txt", ab.installedFilesFile.String(); w != g {
+ t.Errorf("Expected installed-files.txt %q, got %q", w, g)
+ }
+
+ mkData := android.AndroidMkDataForTest(t, result.TestContext, m)
+ var builder strings.Builder
+ mkData.Custom(&builder, "foo", "BAZEL_TARGET_", "", mkData)
+
+ data := builder.String()
+ if w := "ALL_MODULES.$(my_register_name).BUNDLE := out/bazel/execroot/__main__/apex_bundle.zip"; !strings.Contains(data, w) {
+ t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+ }
+ if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/installed-files.txt:foo-installed-files.txt)"; !strings.Contains(data, w) {
+ t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+ }
+
+ // make modules to be installed to system
+ if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+ t.Errorf("Expected makeModulesToInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+ }
+ if w := "LOCAL_REQUIRED_MODULES := adbd.foo c"; !strings.Contains(data, w) {
+ t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
+ }
+}
+
+func TestApexImageCreatesFilesInfoForMake(t *testing.T) {
+ bp := `
+apex_key{
+ name: "foo_key",
+}
+
+apex {
+ name: "foo",
+ key: "foo_key",
+ updatable: true,
+ min_sdk_version: "31",
+ file_contexts: ":myapex-file_contexts",
+ bazel_module: { label: "//:foo" },
+}`
+
+ outputBaseDir := "out/bazel"
+ result := android.GroupFixturePreparers(
+ prepareForApexTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outputBaseDir,
+ LabelToApexInfo: map[string]cquery.ApexInfo{
+ "//:foo": {
+ // ApexInfo Starlark provider. Necessary for the test.
+ SignedOutput: "signed_out.apex",
+ BundleKeyInfo: []string{"public_key", "private_key"},
+ ContainerKeyInfo: []string{"container_cert", "container_private"},
+
+ // ApexMkInfo Starlark provider
+ PayloadFilesInfo: []map[string]string{
+ {
+ "arch": "arm64",
+ "basename": "libcrypto.so",
+ "built_file": "bazel-out/64/libcrypto.so",
+ "class": "nativeSharedLib",
+ "install_dir": "lib64",
+ "make_module_name": "libcrypto",
+ "package": "foo/bar",
+ "unstripped_built_file": "bazel-out/64/unstripped_libcrypto.so",
+ },
+ {
+ "arch": "arm",
+ "basename": "libcrypto.so",
+ "built_file": "bazel-out/32/libcrypto.so",
+ "class": "nativeSharedLib",
+ "install_dir": "lib",
+ "make_module_name": "libcrypto",
+ "package": "foo/bar",
+ },
+ {
+ "arch": "arm64",
+ "basename": "adbd",
+ "built_file": "bazel-out/adbd",
+ "class": "nativeExecutable",
+ "install_dir": "bin",
+ "make_module_name": "adbd",
+ "package": "foo",
+ },
+ },
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+
+ m := result.ModuleForTests("foo", "android_common_foo_image").Module()
+ ab, ok := m.(*apexBundle)
+
+ if !ok {
+ t.Fatalf("Expected module to be an apexBundle, was not")
+ }
+
+ expectedFilesInfo := []apexFile{
+ {
+ androidMkModuleName: "libcrypto",
+ builtFile: android.PathForTesting("out/bazel/execroot/__main__/bazel-out/64/libcrypto.so"),
+ class: nativeSharedLib,
+ customStem: "libcrypto.so",
+ installDir: "lib64",
+ moduleDir: "foo/bar",
+ arch: "arm64",
+ unstrippedBuiltFile: android.PathForTesting("out/bazel/execroot/__main__/bazel-out/64/unstripped_libcrypto.so"),
+ },
+ {
+ androidMkModuleName: "libcrypto",
+ builtFile: android.PathForTesting("out/bazel/execroot/__main__/bazel-out/32/libcrypto.so"),
+ class: nativeSharedLib,
+ customStem: "libcrypto.so",
+ installDir: "lib",
+ moduleDir: "foo/bar",
+ arch: "arm",
+ },
+ {
+ androidMkModuleName: "adbd",
+ builtFile: android.PathForTesting("out/bazel/execroot/__main__/bazel-out/adbd"),
+ class: nativeExecutable,
+ customStem: "adbd",
+ installDir: "bin",
+ moduleDir: "foo",
+ arch: "arm64",
+ },
+ }
+
+ if len(ab.filesInfo) != len(expectedFilesInfo) {
+ t.Errorf("Expected %d entries in ab.filesInfo, but got %d", len(ab.filesInfo), len(expectedFilesInfo))
+ }
+
+ for idx, f := range ab.filesInfo {
+ expected := expectedFilesInfo[idx]
+ android.AssertSame(t, "different class", expected.class, f.class)
+ android.AssertStringEquals(t, "different built file", expected.builtFile.String(), f.builtFile.String())
+ android.AssertStringEquals(t, "different custom stem", expected.customStem, f.customStem)
+ android.AssertStringEquals(t, "different install dir", expected.installDir, f.installDir)
+ android.AssertStringEquals(t, "different make module name", expected.androidMkModuleName, f.androidMkModuleName)
+ android.AssertStringEquals(t, "different moduleDir", expected.moduleDir, f.moduleDir)
+ android.AssertStringEquals(t, "different arch", expected.arch, f.arch)
+ if expected.unstrippedBuiltFile != nil {
+ if f.unstrippedBuiltFile == nil {
+ t.Errorf("expected an unstripped built file path.")
+ }
+ android.AssertStringEquals(t, "different unstripped built file", expected.unstrippedBuiltFile.String(), f.unstrippedBuiltFile.String())
+ }
+ }
+}
+
+func TestCompressedApexImageInMixedBuilds(t *testing.T) {
+ bp := `
+apex_key{
+ name: "foo_key",
+}
+apex {
+ name: "foo",
+ key: "foo_key",
+ updatable: true,
+ min_sdk_version: "31",
+ file_contexts: ":myapex-file_contexts",
+ bazel_module: { label: "//:foo" },
+ test_only_force_compression: true, // force compression
+}`
+
+ outputBaseDir := "out/bazel"
+ result := android.GroupFixturePreparers(
+ prepareForApexTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outputBaseDir,
+ LabelToApexInfo: map[string]cquery.ApexInfo{
+ "//:foo": cquery.ApexInfo{
+ SignedOutput: "signed_out.apex",
+ SignedCompressedOutput: "signed_out.capex",
+ BundleKeyInfo: []string{"public_key", "private_key"},
+ ContainerKeyInfo: []string{"container_cert", "container_private"},
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+
+ m := result.ModuleForTests("foo", "android_common_foo_image").Module()
+ ab, ok := m.(*apexBundle)
+ if !ok {
+ t.Fatalf("Expected module to be an apexBundle, was not")
+ }
+
+ if w, g := "out/bazel/execroot/__main__/signed_out.capex", ab.outputFile.String(); w != g {
+ t.Errorf("Expected output file to be compressed apex %q, got %q", w, g)
+ }
+
+ mkData := android.AndroidMkDataForTest(t, result.TestContext, m)
+ var builder strings.Builder
+ mkData.Custom(&builder, "foo", "BAZEL_TARGET_", "", mkData)
+
+ data := builder.String()
+
+ expectedAndroidMk := []string{
+ "LOCAL_PREBUILT_MODULE_FILE := out/bazel/execroot/__main__/signed_out.capex",
+
+ // Check that the source install file is the capex. The dest is not important.
+ "LOCAL_SOONG_INSTALL_PAIRS := out/bazel/execroot/__main__/signed_out.capex:",
+ }
+ for _, androidMk := range expectedAndroidMk {
+ if !strings.Contains(data, androidMk) {
+ t.Errorf("Expected %q in androidmk data, but did not find %q", androidMk, data)
+ }
+ }
+}
+
+func TestOverrideApexImageInMixedBuilds(t *testing.T) {
+ bp := `
+apex_key{
+ name: "foo_key",
+}
+apex_key{
+ name: "override_foo_key",
+}
+apex {
+ name: "foo",
+ key: "foo_key",
+ updatable: true,
+ min_sdk_version: "31",
+ package_name: "pkg_name",
+ file_contexts: ":myapex-file_contexts",
+ bazel_module: { label: "//:foo" },
+}
+override_apex {
+ name: "override_foo",
+ key: "override_foo_key",
+ package_name: "override_pkg_name",
+ base: "foo",
+ bazel_module: { label: "//:override_foo" },
+}
+`
+
+ outputBaseDir := "out/bazel"
+ result := android.GroupFixturePreparers(
+ prepareForApexTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outputBaseDir,
+ LabelToApexInfo: map[string]cquery.ApexInfo{
+ "//:foo": cquery.ApexInfo{
+ // ApexInfo Starlark provider
+ SignedOutput: "signed_out.apex",
+ UnsignedOutput: "unsigned_out.apex",
+ BundleKeyInfo: []string{"public_key", "private_key"},
+ ContainerKeyInfo: []string{"container_cert", "container_private"},
+ SymbolsUsedByApex: "foo_using.txt",
+ JavaSymbolsUsedByApex: "foo_using.xml",
+ BundleFile: "apex_bundle.zip",
+ InstalledFiles: "installed-files.txt",
+ RequiresLibs: []string{"//path/c:c", "//path/d:d"},
+
+ // unused
+ PackageName: "pkg_name",
+ ProvidesLibs: []string{"a", "b"},
+
+ // ApexMkInfo Starlark provider
+ MakeModulesToInstall: []string{"c"}, // d deliberately omitted
+ },
+ "//:override_foo": cquery.ApexInfo{
+ // ApexInfo Starlark provider
+ SignedOutput: "override_signed_out.apex",
+ UnsignedOutput: "override_unsigned_out.apex",
+ BundleKeyInfo: []string{"override_public_key", "override_private_key"},
+ ContainerKeyInfo: []string{"override_container_cert", "override_container_private"},
+ SymbolsUsedByApex: "override_foo_using.txt",
+ JavaSymbolsUsedByApex: "override_foo_using.xml",
+ BundleFile: "override_apex_bundle.zip",
+ InstalledFiles: "override_installed-files.txt",
+ RequiresLibs: []string{"//path/c:c", "//path/d:d"},
+
+ // unused
+ PackageName: "override_pkg_name",
+ ProvidesLibs: []string{"a", "b"},
+
+ // ApexMkInfo Starlark provider
+ MakeModulesToInstall: []string{"c"}, // d deliberately omitted
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+
+ m := result.ModuleForTests("foo", "android_common_override_foo_foo_image").Module()
+ ab, ok := m.(*apexBundle)
+ if !ok {
+ t.Fatalf("Expected module to be an apexBundle, was not")
+ }
+
+ if w, g := "out/bazel/execroot/__main__/override_public_key", ab.publicKeyFile.String(); w != g {
+ t.Errorf("Expected public key %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/override_private_key", ab.privateKeyFile.String(); w != g {
+ t.Errorf("Expected private key %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/override_container_cert", ab.containerCertificateFile.String(); w != g {
+ t.Errorf("Expected public container key %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/override_container_private", ab.containerPrivateKeyFile.String(); w != g {
+ t.Errorf("Expected private container key %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/override_signed_out.apex", ab.outputFile.String(); w != g {
+ t.Errorf("Expected output file %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/override_foo_using.txt", ab.nativeApisUsedByModuleFile.String(); w != g {
+ t.Errorf("Expected output file %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/override_foo_using.xml", ab.javaApisUsedByModuleFile.String(); w != g {
+ t.Errorf("Expected output file %q, got %q", w, g)
+ }
+
+ if w, g := "out/bazel/execroot/__main__/override_installed-files.txt", ab.installedFilesFile.String(); w != g {
+ t.Errorf("Expected installed-files.txt %q, got %q", w, g)
+ }
+
+ mkData := android.AndroidMkDataForTest(t, result.TestContext, m)
+ var builder strings.Builder
+ mkData.Custom(&builder, "override_foo", "BAZEL_TARGET_", "", mkData)
+
+ data := builder.String()
+ if w := "ALL_MODULES.$(my_register_name).BUNDLE := out/bazel/execroot/__main__/override_apex_bundle.zip"; !strings.Contains(data, w) {
+ t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+ }
+ if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
+ t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+ }
+
+ // make modules to be installed to system
+ if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+ t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+ }
+ if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
+ t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
+ }
+}
diff --git a/apex/builder.go b/apex/builder.go
index 0c1f3ba..3c7671b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -40,6 +40,8 @@
pctx.Import("android/soong/java")
pctx.HostBinToolVariable("apexer", "apexer")
pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")
+ pctx.HostBinToolVariable("apexer_with_trim_preprocessing", "apexer_with_trim_preprocessing")
+
// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
// projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
@@ -69,6 +71,9 @@
pctx.HostBinToolVariable("make_erofs", "make_erofs")
pctx.HostBinToolVariable("apex_compression_tool", "apex_compression_tool")
pctx.HostBinToolVariable("dexdeps", "dexdeps")
+ pctx.HostBinToolVariable("apex_sepolicy_tests", "apex_sepolicy_tests")
+ pctx.HostBinToolVariable("deapexer", "deapexer")
+ pctx.HostBinToolVariable("debugfs_static", "debugfs_static")
pctx.SourcePathVariable("genNdkUsedbyApexPath", "build/soong/scripts/gen_ndk_usedby_apex.sh")
}
@@ -146,6 +151,34 @@
}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
"opt_flags", "manifest", "is_DCLA")
+ TrimmedApexRule = pctx.StaticRule("TrimmedApexRule", blueprint.RuleParams{
+ Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+ `(. ${out}.copy_commands) && ` +
+ `APEXER_TOOL_PATH=${tool_path} ` +
+ `${apexer_with_trim_preprocessing} ` +
+ `--apexer ${apexer} ` +
+ `--canned_fs_config ${canned_fs_config} ` +
+ `--manifest ${manifest} ` +
+ `--libs_to_trim ${libs_to_trim} ` +
+ `${image_dir} ` +
+ `${out} ` +
+ `-- ` +
+ `--include_build_info ` +
+ `--force ` +
+ `--payload_type image ` +
+ `--key ${key} ` +
+ `--file_contexts ${file_contexts} ` +
+ `${opt_flags} `,
+ CommandDeps: []string{"${apexer_with_trim_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}",
+ "${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}",
+ "${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}",
+ "prebuilts/sdk/current/public/android.jar"},
+ Rspfile: "${out}.copy_commands",
+ RspfileContent: "${copy_commands}",
+ Description: "APEX ${image_dir} => ${out}",
+ }, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
+ "opt_flags", "manifest", "libs_to_trim")
+
zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
`(. ${out}.copy_commands) && ` +
@@ -179,13 +212,6 @@
Description: "app bundle",
}, "abi", "config")
- emitApexContentRule = pctx.StaticRule("emitApexContentRule", blueprint.RuleParams{
- Command: `rm -f ${out} && touch ${out} && (. ${out}.emit_commands)`,
- Rspfile: "${out}.emit_commands",
- RspfileContent: "${emit_commands}",
- Description: "Emit APEX image content",
- }, "emit_commands")
-
diffApexContentRule = pctx.StaticRule("diffApexContentRule", blueprint.RuleParams{
Command: `diff --unchanged-group-format='' \` +
`--changed-group-format='%<' \` +
@@ -203,7 +229,12 @@
Description: "Generate symbol list used by Apex",
}, "image_dir", "readelf")
- // Don't add more rules here. Consider using android.NewRuleBuilder instead.
+ apexSepolicyTestsRule = pctx.StaticRule("apexSepolicyTestsRule", blueprint.RuleParams{
+ Command: `${deapexer} --debugfs_path ${debugfs_static} list -Z ${in} > ${out}.fc` +
+ `&& ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`,
+ CommandDeps: []string{"${apex_sepolicy_tests}", "${deapexer}", "${debugfs_static}"},
+ Description: "run apex_sepolicy_tests",
+ })
)
// buildManifest creates buile rules to modify the input apex_manifest.json to add information
@@ -218,10 +249,11 @@
provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
- // APEX name can be overridden
+ // VNDK APEX name is determined at runtime, so update "name" in apex_manifest
optCommands := []string{}
- if a.properties.Apex_name != nil {
- optCommands = append(optCommands, "-v name "+*a.properties.Apex_name)
+ if a.vndkApex {
+ apexName := vndkApexNamePrefix + a.vndkVersion(ctx.DeviceConfig())
+ optCommands = append(optCommands, "-v name "+apexName)
}
// Collect jniLibs. Notice that a.filesInfo is already sorted
@@ -235,8 +267,14 @@
optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " "))
}
+ if android.InList(":vndk", requireNativeLibs) {
+ if _, vndkVersion := a.getImageVariationPair(ctx.DeviceConfig()); vndkVersion != "" {
+ optCommands = append(optCommands, "-v vndkVersion "+vndkVersion)
+ }
+ }
+
manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
- defaultVersion := defaultManifestVersion
+ defaultVersion := android.DefaultUpdatableModuleVersion
if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" {
defaultVersion = override
}
@@ -303,6 +341,8 @@
ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String())
}
+ useFileContextsAsIs := proptools.Bool(a.properties.Use_file_contexts_as_is)
+
output := android.PathForModuleOut(ctx, "file_contexts")
rule := android.NewRuleBuilder(pctx, ctx)
@@ -314,9 +354,11 @@
rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output)
// new line
rule.Command().Text("echo").Text(">>").Output(output)
- // force-label /apex_manifest.pb and / as system_file so that apexd can read them
- rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output)
- rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output)
+ if !useFileContextsAsIs {
+ // force-label /apex_manifest.pb and / as system_file so that apexd can read them
+ rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output)
+ rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output)
+ }
case flattenedApex:
// For flattened apexes, install path should be prepended.
// File_contexts file should be emiited to make via LOCAL_FILE_CONTEXTS
@@ -329,9 +371,11 @@
rule.Command().Text("awk").Text(`'/object_r/{printf("` + apexPath + `%s\n", $0)}'`).Input(fileContexts).Text(">").Output(output)
// new line
rule.Command().Text("echo").Text(">>").Output(output)
- // force-label /apex_manifest.pb and / as system_file so that apexd can read them
- rule.Command().Text("echo").Flag(apexPath + `/apex_manifest\\.pb u:object_r:system_file:s0`).Text(">>").Output(output)
- rule.Command().Text("echo").Flag(apexPath + "/ u:object_r:system_file:s0").Text(">>").Output(output)
+ if !useFileContextsAsIs {
+ // force-label /apex_manifest.pb and / as system_file so that apexd can read them
+ rule.Command().Text("echo").Flag(apexPath + `/apex_manifest\\.pb u:object_r:system_file:s0`).Text(">>").Output(output)
+ rule.Command().Text("echo").Flag(apexPath + "/ u:object_r:system_file:s0").Text(">>").Output(output)
+ }
default:
panic(fmt.Errorf("unsupported type %v", a.properties.ApexType))
}
@@ -416,7 +460,7 @@
func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
apexType := a.properties.ApexType
suffix := apexType.suffix()
- apexName := proptools.StringDefault(a.properties.Apex_name, a.BaseModuleName())
+ apexName := a.BaseModuleName()
////////////////////////////////////////////////////////////////////////////////////////////
// Step 1: copy built files to appropriate directories under the image directory
@@ -424,27 +468,18 @@
imageDir := android.PathForModuleOut(ctx, "image"+suffix)
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>
- // as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files
- // for the overriding VNDK APEXes.
- if a.vndkApex && len(a.overridableProperties.Overrides) > 0 {
+ // We can't install symbol files when prebuilt is used.
+ if a.IsReplacedByPrebuilt() {
installSymbolFiles = false
}
- // Avoid creating duplicate build rules for multi-installed APEXes.
- if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) {
- installSymbolFiles = false
-
- }
// set of dependency module:location mappings
installMapSet := make(map[string]bool)
// TODO(jiyong): use the RuleBuilder
var copyCommands []string
var implicitInputs []android.Path
- pathWhenActivated := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
+ apexDir := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
for _, fi := range a.filesInfo {
destPath := imageDir.Join(ctx, fi.path()).String()
// Prepare the destination path
@@ -459,29 +494,29 @@
// Copy the built file to the directory. But if the symlink optimization is turned
// on, place a symlink to the corresponding file in /system partition instead.
if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
- // TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
- pathOnDevice := filepath.Join("/system", fi.path())
+ pathOnDevice := filepath.Join("/", fi.partition, fi.path())
copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
} else {
+ // Copy the file into APEX
+ copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
+
var installedPath android.InstallPath
if fi.class == appSet {
+ // In case of AppSet, we need to copy additional APKs as well. They
+ // are zipped. So we need to unzip them.
copyCommands = append(copyCommands,
fmt.Sprintf("unzip -qDD -d %s %s", destPathDir,
fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs().String()))
if installSymbolFiles {
- installedPath = ctx.InstallFileWithExtraFilesZip(pathWhenActivated.Join(ctx, fi.installDir),
+ installedPath = ctx.InstallFileWithExtraFilesZip(apexDir.Join(ctx, fi.installDir),
fi.stem(), fi.builtFile, fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs())
}
} else {
- copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
if installSymbolFiles {
- installedPath = ctx.InstallFile(pathWhenActivated.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
+ installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
}
}
implicitInputs = append(implicitInputs, fi.builtFile)
- if installSymbolFiles {
- implicitInputs = append(implicitInputs, installedPath)
- }
// Create additional symlinks pointing the file inside the APEX (if any). Note that
// this is independent from the symlink optimization.
@@ -489,8 +524,7 @@
symlinkDest := imageDir.Join(ctx, symlinkPath).String()
copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
if installSymbolFiles {
- installedSymlink := ctx.InstallSymlink(pathWhenActivated.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
- implicitInputs = append(implicitInputs, installedSymlink)
+ ctx.InstallSymlink(apexDir.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
}
}
@@ -515,15 +549,10 @@
installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
}
implicitInputs = append(implicitInputs, a.manifestPbOut)
- if installSymbolFiles {
- installedManifest := ctx.InstallFile(pathWhenActivated, "apex_manifest.pb", a.manifestPbOut)
- installedKey := ctx.InstallFile(pathWhenActivated, "apex_pubkey", a.publicKeyFile)
- implicitInputs = append(implicitInputs, installedManifest, installedKey)
- }
if len(installMapSet) > 0 {
var installs []string
- installs = append(installs, android.SortedStringKeys(installMapSet)...)
+ installs = append(installs, android.SortedKeys(installMapSet)...)
a.SetLicenseInstallMap(installs)
}
@@ -536,29 +565,20 @@
// to be using this at this moment. Furthermore, this looks very similar to what
// buildInstalledFilesFile does. At least, move this to somewhere else so that this doesn't
// hurt readability.
- // TODO(jiyong): use RuleBuilder
if a.overridableProperties.Allowed_files != nil {
// Build content.txt
- var emitCommands []string
+ var contentLines []string
imageContentFile := android.PathForModuleOut(ctx, "content.txt")
- emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String())
+ contentLines = append(contentLines, "./apex_manifest.pb")
minSdkVersion := a.minSdkVersion(ctx)
if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
- emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
+ contentLines = append(contentLines, "./apex_manifest.json")
}
for _, fi := range a.filesInfo {
- emitCommands = append(emitCommands, "echo './"+fi.path()+"' >> "+imageContentFile.String())
+ contentLines = append(contentLines, "./"+fi.path())
}
- emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
- ctx.Build(pctx, android.BuildParams{
- Rule: emitApexContentRule,
- Implicits: implicitInputs,
- Output: imageContentFile,
- Description: "emit apex image content",
- Args: map[string]string{
- "emit_commands": strings.Join(emitCommands, " && "),
- },
- })
+ sort.Strings(contentLines)
+ android.WriteFileRule(ctx, imageContentFile, strings.Join(contentLines, "\n"))
implicitInputs = append(implicitInputs, imageContentFile)
// Compare content.txt against allowed_files.
@@ -582,8 +602,6 @@
outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
- // Figure out if we need to compress the apex.
- compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.overridableProperties.Compressible, false) && !a.testApex && !ctx.Config().UnbundledBuildApps()
if apexType == imageApex {
////////////////////////////////////////////////////////////////////////////////////
@@ -652,9 +670,9 @@
}
// Create a NOTICE file, and embed it as an asset file in the APEX.
- a.htmlGzNotice = android.PathForModuleOut(ctx, "NOTICE.html.gz")
+ htmlGzNotice := android.PathForModuleOut(ctx, "NOTICE.html.gz")
android.BuildNoticeHtmlOutputFromLicenseMetadata(
- ctx, a.htmlGzNotice, "", "",
+ ctx, htmlGzNotice, "", "",
[]string{
android.PathForModuleInstall(ctx).String() + "/",
android.PathForModuleInPartitionInstall(ctx, "apex").String() + "/",
@@ -662,16 +680,21 @@
noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
builder := android.NewRuleBuilder(pctx, ctx)
builder.Command().Text("cp").
- Input(a.htmlGzNotice).
+ Input(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)
- // don't need hashtree for activation. Therefore, by removing hashtree from
- // apex bundle (filesystem image in it, to be specific), we can save storage.
+ // Apexes which are supposed to be installed in builtin dirs(/system, etc)
+ // don't need hashtree for activation. Therefore, by removing hashtree from
+ // apex bundle (filesystem image in it, to be specific), we can save storage.
+ needHashTree := moduleMinSdkVersion.LessThanOrEqualTo(android.SdkVersion_Android10) ||
+ a.shouldGenerateHashtree()
+ if ctx.Config().ApexCompressionEnabled() && a.isCompressable() {
+ needHashTree = true
+ }
+ if !needHashTree {
optFlags = append(optFlags, "--no_hashtree")
}
@@ -679,12 +702,6 @@
optFlags = append(optFlags, "--unsigned_payload")
}
- if a.properties.Apex_name != nil {
- // If apex_name is set, apexer can skip checking if key name matches with
- // apex name. Note that apex_manifest is also mended.
- optFlags = append(optFlags, "--do_not_check_keyname")
- }
-
if moduleMinSdkVersion == android.SdkVersion_Android10 {
implicitInputs = append(implicitInputs, a.manifestJsonOut)
optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
@@ -709,6 +726,24 @@
"opt_flags": strings.Join(optFlags, " "),
},
})
+ } else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: TrimmedApexRule,
+ Implicits: implicitInputs,
+ Output: unsignedOutputFile,
+ Description: "apex (" + apexType.name() + ")",
+ Args: map[string]string{
+ "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+ "image_dir": imageDir.String(),
+ "copy_commands": strings.Join(copyCommands, " && "),
+ "manifest": a.manifestPbOut.String(),
+ "file_contexts": fileContexts.String(),
+ "canned_fs_config": cannedFsConfig.String(),
+ "key": a.privateKeyFile.String(),
+ "opt_flags": strings.Join(optFlags, " "),
+ "libs_to_trim": strings.Join(a.libs_to_trim(ctx), ","),
+ },
+ })
} else {
ctx.Build(pctx, android.BuildParams{
Rule: apexRule,
@@ -840,6 +875,10 @@
args["implicits"] = strings.Join(implicits.Strings(), ",")
args["outCommaList"] = signedOutputFile.String()
}
+ var validations android.Paths
+ if suffix == imageApexSuffix {
+ validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile.OutputPath))
+ }
ctx.Build(pctx, android.BuildParams{
Rule: rule,
Description: "signapk",
@@ -847,6 +886,7 @@
Input: unsignedOutputFile,
Implicits: implicits,
Args: args,
+ Validations: validations,
})
if suffix == imageApexSuffix {
a.outputApexFile = signedOutputFile
@@ -858,8 +898,9 @@
return
}
- if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
- a.isCompressed = true
+ installSuffix := suffix
+ a.setCompression(ctx)
+ if a.isCompressed {
unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned")
compressRule := android.NewRuleBuilder(pctx, ctx)
@@ -887,10 +928,6 @@
Args: args,
})
a.outputFile = signedCompressedOutputFile
- }
-
- installSuffix := suffix
- if a.isCompressed {
installSuffix = imageCapexSuffix
}
@@ -917,15 +954,20 @@
dir := filepath.Join("apex", bundleName, fi.installDir)
installDir := android.PathForModuleInstall(ctx, dir)
if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
- // TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
- pathOnDevice := filepath.Join("/system", fi.path())
+ pathOnDevice := filepath.Join("/", fi.partition, fi.path())
installedSymlinks = append(installedSymlinks,
ctx.InstallAbsoluteSymlink(installDir, fi.stem(), pathOnDevice))
} else {
- target := ctx.InstallFile(installDir, fi.stem(), fi.builtFile)
- for _, sym := range fi.symlinks {
- installedSymlinks = append(installedSymlinks,
- ctx.InstallSymlink(installDir, sym, target))
+ if fi.class == appSet {
+ as := fi.module.(*java.AndroidAppSet)
+ ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk",
+ as.OutputFile(), as.PackedAdditionalOutputs())
+ } else {
+ target := ctx.InstallFile(installDir, fi.stem(), fi.builtFile)
+ for _, sym := range fi.symlinks {
+ installedSymlinks = append(installedSymlinks,
+ ctx.InstallSymlink(installDir, sym, target))
+ }
}
}
}
@@ -970,7 +1012,7 @@
if a.vndkApex {
overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName)
if overridden {
- return strings.Replace(*a.properties.Apex_name, vndkApexName, overrideName, 1)
+ return overrideName + ".v" + a.vndkVersion(ctx.DeviceConfig())
}
return ""
}
@@ -1029,10 +1071,10 @@
} else {
toMinSdkVersion := "(no version)"
if m, ok := to.(interface {
- MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+ MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
}); ok {
- if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
- toMinSdkVersion = v.ApiLevel.String()
+ if v := m.MinSdkVersion(ctx); !v.IsNone() {
+ toMinSdkVersion = v.String()
}
} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
// TODO(b/175678607) eliminate the use of MinSdkVersion returning
@@ -1053,7 +1095,7 @@
return !externalDep
})
- a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).Raw, depInfos)
+ a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).String(), depInfos)
ctx.Build(pctx, android.BuildParams{
Rule: android.Phony,
@@ -1090,8 +1132,11 @@
executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
}
} else if f.class == appSet {
+ // base APK
+ readOnlyPaths = append(readOnlyPaths, pathInApex)
+ // Additional APKs
appSetDirs = append(appSetDirs, f.installDir)
- appSetFiles[f.installDir] = f.builtFile
+ appSetFiles[f.installDir] = f.module.(*java.AndroidAppSet).PackedAdditionalOutputs()
} else {
readOnlyPaths = append(readOnlyPaths, pathInApex)
}
@@ -1135,3 +1180,17 @@
return cannedFsConfig.OutputPath
}
+
+// Runs apex_sepolicy_tests
+//
+// $ deapexer list -Z {apex_file} > {file_contexts}
+// $ apex_sepolicy_tests -f {file_contexts}
+func runApexSepolicyTests(ctx android.ModuleContext, apexFile android.OutputPath) android.Path {
+ timestamp := android.PathForModuleOut(ctx, "sepolicy_tests.timestamp")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: apexSepolicyTestsRule,
+ Input: apexFile,
+ Output: timestamp,
+ })
+ return timestamp
+}
diff --git a/apex/deapexer.go b/apex/deapexer.go
index 8c9030a..fed9cd1 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -140,6 +140,8 @@
Tool(android.PathForSource(ctx, "build/soong/scripts/unpack-prebuilt-apex.sh")).
BuiltTool("deapexer").
BuiltTool("debugfs").
+ BuiltTool("blkid").
+ BuiltTool("fsck.erofs").
Input(p.inputApex).
Text(deapexerOutput.String())
for _, p := range exportedPaths {
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
new file mode 100644
index 0000000..bba8bb6
--- /dev/null
+++ b/apex/dexpreopt_bootjars_test.go
@@ -0,0 +1,254 @@
+// 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 apex
+
+import (
+ "fmt"
+ "path/filepath"
+ "sort"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOutputs []string, preferPrebuilt bool) {
+ bp := `
+ // Platform.
+
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ api_packages: ["foo"],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ system_ext_specific: true,
+ }
+
+ dex_import {
+ name: "baz",
+ jars: ["a.jar"],
+ }
+
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+ }
+
+ // Source ART APEX.
+
+ java_library {
+ name: "core-oj",
+ srcs: ["core-oj.java"],
+ installable: true,
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ bootclasspath_fragment {
+ name: "art-bootclasspath-fragment",
+ image_name: "art",
+ contents: ["core-oj"],
+ apex_available: [
+ "com.android.art",
+ ],
+ hidden_api: {
+ split_packages: ["*"],
+ },
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "com.android.art.avbpubkey",
+ private_key: "com.android.art.pem",
+ }
+
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ bootclasspath_fragments: ["art-bootclasspath-fragment"],
+ updatable: false,
+ }
+
+ // Prebuilt ART APEX.
+
+ java_import {
+ name: "core-oj",
+ prefer: %[1]t,
+ jars: ["core-oj.jar"],
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "art-bootclasspath-fragment",
+ prefer: %[1]t,
+ image_name: "art",
+ contents: ["core-oj"],
+ hidden_api: {
+ annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+ metadata: "my-bootclasspath-fragment/metadata.csv",
+ index: "my-bootclasspath-fragment/index.csv",
+ stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+ all_flags: "my-bootclasspath-fragment/all-flags.csv",
+ },
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ prebuilt_apex {
+ name: "com.android.art",
+ prefer: %[1]t,
+ apex_name: "com.android.art",
+ src: "com.android.art-arm.apex",
+ exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
+ }
+ `
+
+ result := android.GroupFixturePreparers(
+ java.PrepareForTestWithDexpreopt,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo"),
+ java.FixtureConfigureBootJars("com.android.art:core-oj", "platform:foo", "system_ext:bar", "platform:baz"),
+ PrepareForTestWithApexBuildComponents,
+ prepareForTestWithArtApex,
+ ).RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
+
+ platformBootclasspath := result.ModuleForTests("platform-bootclasspath", "android_common")
+ rule := platformBootclasspath.Output(ruleFile)
+
+ inputs := rule.Implicits.Strings()
+ sort.Strings(inputs)
+ sort.Strings(expectedInputs)
+
+ outputs := append(android.WritablePaths{rule.Output}, rule.ImplicitOutputs...).Strings()
+ sort.Strings(outputs)
+ sort.Strings(expectedOutputs)
+
+ android.AssertStringPathsRelativeToTopEquals(t, "inputs", result.Config, expectedInputs, inputs)
+
+ android.AssertStringPathsRelativeToTopEquals(t, "outputs", result.Config, expectedOutputs, outputs)
+}
+
+func TestDexpreoptBootJarsWithSourceArtApex(t *testing.T) {
+ ruleFile := "boot.art"
+
+ expectedInputs := []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/core-oj.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
+ "out/soong/dexpreopt_arm64/dex_artjars/boot.prof",
+ "out/soong/dexpreopt_arm64/dex_bootjars/boot.prof",
+ }
+
+ expectedOutputs := []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.invocation",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-foo.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-bar.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-baz.oat",
+ }
+
+ testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs, false)
+}
+
+// The only difference is that the ART profile should be deapexed from the prebuilt APEX. Other
+// inputs and outputs should be the same as above.
+func TestDexpreoptBootJarsWithPrebuiltArtApex(t *testing.T) {
+ ruleFile := "boot.art"
+
+ expectedInputs := []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/core-oj.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
+ "out/soong/.intermediates/com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
+ "out/soong/dexpreopt_arm64/dex_bootjars/boot.prof",
+ }
+
+ expectedOutputs := []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.invocation",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-foo.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-bar.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-baz.oat",
+ }
+
+ testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs, true)
+}
+
+// Changes to the boot.zip structure may break the ART APK scanner.
+func TestDexpreoptBootZip(t *testing.T) {
+ ruleFile := "boot.zip"
+
+ ctx := android.PathContextForTesting(android.TestArchConfig("", nil, "", nil))
+ expectedInputs := []string{}
+ for _, target := range ctx.Config().Targets[android.Android] {
+ for _, ext := range []string{".art", ".oat", ".vdex"} {
+ for _, suffix := range []string{"", "-foo", "-bar", "-baz"} {
+ expectedInputs = append(expectedInputs,
+ filepath.Join(
+ "out/soong/dexpreopt_arm64/dex_bootjars",
+ target.Os.String(),
+ "system/framework",
+ target.Arch.ArchType.String(),
+ "boot"+suffix+ext))
+ }
+ }
+ }
+
+ expectedOutputs := []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/boot.zip",
+ }
+
+ testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs, false)
+}
diff --git a/apex/key.go b/apex/key.go
index 6090b65..0a7e80f 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -44,8 +44,6 @@
publicKeyFile android.Path
privateKeyFile android.Path
-
- keyName string
}
type apexKeyProperties struct {
@@ -102,7 +100,6 @@
m.publicKeyFile.String(), pubKeyName, m.privateKeyFile, privKeyName)
return
}
- m.keyName = pubKeyName
}
// //////////////////////////////////////////////////////////////////////
@@ -203,8 +200,11 @@
// For Bazel / bp2build
type bazelApexKeyAttributes struct {
- Public_key bazel.LabelAttribute
- Private_key bazel.LabelAttribute
+ Public_key bazel.LabelAttribute
+ Public_key_name bazel.StringAttribute
+
+ Private_key bazel.LabelAttribute
+ Private_key_name bazel.StringAttribute
}
// ConvertWithBp2build performs conversion apexKey for bp2build
@@ -213,24 +213,23 @@
}
func apexKeyBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexKey) {
- var privateKeyLabelAttribute bazel.LabelAttribute
- if module.properties.Private_key != nil {
- privateKeyLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Private_key))
- }
+ privateKeyLabelAttribute, privateKeyNameAttribute :=
+ android.BazelStringOrLabelFromProp(ctx, module.properties.Private_key)
- var publicKeyLabelAttribute bazel.LabelAttribute
- if module.properties.Public_key != nil {
- publicKeyLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Public_key))
- }
+ publicKeyLabelAttribute, publicKeyNameAttribute :=
+ android.BazelStringOrLabelFromProp(ctx, module.properties.Public_key)
attrs := &bazelApexKeyAttributes{
- Private_key: privateKeyLabelAttribute,
- Public_key: publicKeyLabelAttribute,
+ Private_key: privateKeyLabelAttribute,
+ Private_key_name: privateKeyNameAttribute,
+
+ Public_key: publicKeyLabelAttribute,
+ Public_key_name: publicKeyNameAttribute,
}
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/metadata.go b/apex/metadata.go
new file mode 100644
index 0000000..b1dff3e
--- /dev/null
+++ b/apex/metadata.go
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 apex
+
+import (
+ "encoding/json"
+
+ "github.com/google/blueprint"
+
+ "android/soong/android"
+)
+
+var (
+ mtctx = android.NewPackageContext("android/soong/multitree_apex")
+)
+
+func init() {
+ RegisterModulesSingleton(android.InitRegistrationContext)
+}
+
+func RegisterModulesSingleton(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonType("apex_multitree_singleton", multitreeAnalysisSingletonFactory)
+}
+
+var PrepareForTestWithApexMultitreeSingleton = android.FixtureRegisterWithContext(RegisterModulesSingleton)
+
+func multitreeAnalysisSingletonFactory() android.Singleton {
+ return &multitreeAnalysisSingleton{}
+}
+
+type multitreeAnalysisSingleton struct {
+ multitreeApexMetadataPath android.OutputPath
+}
+
+type ApexMultitreeMetadataEntry struct {
+ // The name of the apex.
+ Name string
+
+ // TODO: Add other properties as needed.
+}
+
+type ApexMultitreeMetadata struct {
+ // Information about the installable apexes.
+ Apexes map[string]ApexMultitreeMetadataEntry
+}
+
+func (p *multitreeAnalysisSingleton) GenerateBuildActions(context android.SingletonContext) {
+ data := ApexMultitreeMetadata{
+ Apexes: make(map[string]ApexMultitreeMetadataEntry, 0),
+ }
+ context.VisitAllModules(func(module android.Module) {
+ // If this module is not being installed, ignore it.
+ if !module.Enabled() || module.IsSkipInstall() {
+ return
+ }
+ // Actual apexes provide ApexBundleInfoProvider.
+ if _, ok := context.ModuleProvider(module, ApexBundleInfoProvider).(ApexBundleInfo); !ok {
+ return
+ }
+ bundle, ok := module.(*apexBundle)
+ if ok && !bundle.testApex && !bundle.vndkApex && bundle.primaryApexType {
+ name := module.Name()
+ entry := ApexMultitreeMetadataEntry{
+ Name: name,
+ }
+ data.Apexes[name] = entry
+ }
+ })
+ p.multitreeApexMetadataPath = android.PathForOutput(context, "multitree_apex_metadata.json")
+
+ jsonStr, err := json.Marshal(data)
+ if err != nil {
+ context.Errorf(err.Error())
+ }
+ android.WriteFileRule(context, p.multitreeApexMetadataPath, string(jsonStr))
+ // This seems cleaner, but doesn't emit the phony rule in testing.
+ // context.Phony("multitree_apex_metadata", p.multitreeApexMetadataPath)
+
+ context.Build(mtctx, android.BuildParams{
+ Rule: blueprint.Phony,
+ Description: "phony rule for multitree_apex_metadata",
+ Inputs: []android.Path{p.multitreeApexMetadataPath},
+ Output: android.PathForPhony(context, "multitree_apex_metadata"),
+ })
+}
diff --git a/apex/metadata_test.go b/apex/metadata_test.go
new file mode 100644
index 0000000..fed5bea
--- /dev/null
+++ b/apex/metadata_test.go
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 apex
+
+import (
+ "strings"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func TestModulesSingleton(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithApexMultitreeSingleton,
+ java.PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithApexBuildComponents,
+ java.FixtureConfigureApexBootJars("myapex:foo"),
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ ).RunTestWithBp(t, `
+ prebuilt_apex {
+ name: "myapex",
+ src: "myapex.apex",
+ exported_bootclasspath_fragments: ["mybootclasspath-fragment"],
+ }
+
+ // A prebuilt java_sdk_library_import that is not preferred by default but will be preferred
+ // because AlwaysUsePrebuiltSdks() is true.
+ java_sdk_library_import {
+ name: "foo",
+ prefer: false,
+ shared_library: false,
+ permitted_packages: ["foo"],
+ public: {
+ jars: ["sdk_library/public/foo-stubs.jar"],
+ stub_srcs: ["sdk_library/public/foo_stub_sources"],
+ current_api: "sdk_library/public/foo.txt",
+ removed_api: "sdk_library/public/foo-removed.txt",
+ sdk_version: "current",
+ },
+ apex_available: ["myapex"],
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "mybootclasspath-fragment",
+ apex_available: [
+ "myapex",
+ ],
+ contents: [
+ "foo",
+ ],
+ hidden_api: {
+ stub_flags: "prebuilt-stub-flags.csv",
+ annotation_flags: "prebuilt-annotation-flags.csv",
+ metadata: "prebuilt-metadata.csv",
+ index: "prebuilt-index.csv",
+ all_flags: "prebuilt-all-flags.csv",
+ },
+ }
+
+ platform_bootclasspath {
+ name: "myplatform-bootclasspath",
+ fragments: [
+ {
+ apex: "myapex",
+ module:"mybootclasspath-fragment",
+ },
+ ],
+ }
+`,
+ )
+
+ outputs := result.SingletonForTests("apex_multitree_singleton").AllOutputs()
+ for _, output := range outputs {
+ testingBuildParam := result.SingletonForTests("apex_multitree_singleton").Output(output)
+ switch {
+ case strings.Contains(output, "soong/multitree_apex_metadata.json"):
+ android.AssertStringEquals(t, "Invalid build rule", "android/soong/android.writeFile", testingBuildParam.Rule.String())
+ android.AssertIntEquals(t, "Invalid input", len(testingBuildParam.Inputs), 0)
+ android.AssertStringDoesContain(t, "Invalid output path", output, "soong/multitree_apex_metadata.json")
+
+ case strings.HasSuffix(output, "multitree_apex_metadata"):
+ android.AssertStringEquals(t, "Invalid build rule", "<builtin>:phony", testingBuildParam.Rule.String())
+ android.AssertStringEquals(t, "Invalid input", testingBuildParam.Inputs[0].String(), "out/soong/multitree_apex_metadata.json")
+ android.AssertStringEquals(t, "Invalid output path", output, "multitree_apex_metadata")
+ android.AssertIntEquals(t, "Invalid args", len(testingBuildParam.Args), 0)
+ }
+ }
+}
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 4b48da8..05bb136 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -20,6 +20,7 @@
"testing"
"android/soong/android"
+ "android/soong/dexpreopt"
"android/soong/java"
"github.com/google/blueprint"
@@ -30,7 +31,7 @@
// apexes.
var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers(
- java.PrepareForTestWithDexpreopt,
+ java.PrepareForTestWithJavaDefaultModules,
PrepareForTestWithApexBuildComponents,
)
@@ -249,6 +250,8 @@
java.FixtureConfigureApexBootJars("myapex:bar"),
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("foo"),
+ java.PrepareForTestWithDexpreopt,
+ dexpreopt.FixtureDisableDexpreoptBootImages(false),
).RunTestWithBp(t, `
apex {
name: "com.android.art",
@@ -539,9 +542,6 @@
// Not a prebuilt as no prebuilt existed when it was added.
"platform:legacy.core.platform.api.stubs",
- // Needed for generating the boot image.
- "platform:dex2oatd",
-
// The platform_bootclasspath intentionally adds dependencies on both source and prebuilt
// modules when available as it does not know which one will be preferred.
"myapex:foo",
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 609a9d2..0d83830 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -24,6 +24,7 @@
"android/soong/android"
"android/soong/java"
"android/soong/provenance"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -34,11 +35,12 @@
blueprint.RuleParams{
Command: `rm -rf "$out" && ` +
`${extract_apks} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
- `-sdk-version=${sdk-version} -abis=${abis} -screen-densities=all -extract-single ` +
+ `-sdk-version=${sdk-version} -skip-sdk-check=${skip-sdk-check} -abis=${abis} ` +
+ `-screen-densities=all -extract-single ` +
`${in}`,
CommandDeps: []string{"${extract_apks}"},
},
- "abis", "allow-prereleased", "sdk-version")
+ "abis", "allow-prereleased", "sdk-version", "skip-sdk-check")
)
type prebuilt interface {
@@ -197,14 +199,11 @@
p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
}
} else if tag == exportedBootclasspathFragmentTag {
- bcpfModule, ok := child.(*java.PrebuiltBootclasspathFragmentModule)
+ _, 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 {
@@ -314,31 +313,29 @@
}
}
-func (p *prebuiltCommon) getExportedDependencies() map[string]exportedDependencyTag {
- dependencies := make(map[string]exportedDependencyTag)
-
- for _, dep := range p.prebuiltCommonProperties.Exported_java_libs {
- dependencies[dep] = exportedJavaLibTag
- }
-
- for _, dep := range p.prebuiltCommonProperties.Exported_bootclasspath_fragments {
- dependencies[dep] = exportedBootclasspathFragmentTag
- }
-
- for _, dep := range p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments {
- dependencies[dep] = exportedSystemserverclasspathFragmentTag
- }
-
- return dependencies
+func (p *prebuiltCommon) hasExportedDeps() bool {
+ return len(p.prebuiltCommonProperties.Exported_java_libs) > 0 ||
+ len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 ||
+ len(p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments) > 0
}
// prebuiltApexContentsDeps adds dependencies onto the prebuilt apex module's contents.
func (p *prebuiltCommon) prebuiltApexContentsDeps(ctx android.BottomUpMutatorContext) {
module := ctx.Module()
- for dep, tag := range p.getExportedDependencies() {
+ for _, dep := range p.prebuiltCommonProperties.Exported_java_libs {
prebuiltDep := android.PrebuiltNameFromSource(dep)
- ctx.AddDependency(module, tag, prebuiltDep)
+ ctx.AddDependency(module, exportedJavaLibTag, prebuiltDep)
+ }
+
+ for _, dep := range p.prebuiltCommonProperties.Exported_bootclasspath_fragments {
+ prebuiltDep := android.PrebuiltNameFromSource(dep)
+ ctx.AddDependency(module, exportedBootclasspathFragmentTag, prebuiltDep)
+ }
+
+ for _, dep := range p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments {
+ prebuiltDep := android.PrebuiltNameFromSource(dep)
+ ctx.AddDependency(module, exportedSystemserverclasspathFragmentTag, prebuiltDep)
}
}
@@ -532,6 +529,10 @@
src = String(p.Arch.Arm64.Src)
case android.Riscv64:
src = String(p.Arch.Riscv64.Src)
+ // HACK: fall back to arm64 prebuilts, the riscv64 ones don't exist yet.
+ if src == "" {
+ src = String(p.Arch.Arm64.Src)
+ }
case android.X86:
src = String(p.Arch.X86.Src)
case android.X86_64:
@@ -542,7 +543,11 @@
}
if src == "" {
- ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
+ if ctx.Config().AllowMissingDependencies() {
+ ctx.AddMissingDependencies([]string{ctx.OtherModuleName(prebuilt)})
+ } else {
+ ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
+ }
// Drop through to return an empty string as the src (instead of nil) to avoid the prebuilt
// logic from reporting a more general, less useful message.
}
@@ -598,7 +603,7 @@
// the listed modules need access to files from within the prebuilt .apex file.
func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string) {
// Only create the deapexer module if it is needed.
- if len(p.getExportedDependencies()) == 0 {
+ if !p.hasExportedDeps() {
return
}
@@ -821,8 +826,11 @@
srcsSupplier := func(ctx android.BaseModuleContext, prebuilt android.Module) []string {
return p.properties.prebuiltSrcs(ctx)
}
+ defaultAllowPrerelease := ctx.Config().IsEnvTrue("SOONG_ALLOW_PRERELEASE_APEXES")
apexSet := android.SingleSourcePathFromSupplier(ctx, srcsSupplier, "set")
p.extractedApex = android.PathForModuleOut(ctx, "extracted", apexSet.Base())
+ // Filter out NativeBridge archs (b/260115309)
+ abis := java.SupportedAbis(ctx, true)
ctx.Build(pctx,
android.BuildParams{
Rule: extractMatchingApex,
@@ -830,9 +838,10 @@
Inputs: android.Paths{apexSet},
Output: p.extractedApex,
Args: map[string]string{
- "abis": strings.Join(java.SupportedAbis(ctx), ","),
- "allow-prereleased": strconv.FormatBool(proptools.Bool(p.properties.Prerelease)),
+ "abis": strings.Join(abis, ","),
+ "allow-prereleased": strconv.FormatBool(proptools.BoolDefault(p.properties.Prerelease, defaultAllowPrerelease)),
"sdk-version": ctx.Config().PlatformSdkVersion().String(),
+ "skip-sdk-check": strconv.FormatBool(ctx.Config().IsEnvTrue("SOONG_SKIP_APPSET_SDK_CHECK")),
},
})
}
@@ -845,17 +854,17 @@
type ApexExtractorProperties struct {
// the .apks file path that contains prebuilt apex files to be extracted.
- Set *string
+ Set *string `android:"path"`
Sanitized struct {
None struct {
- Set *string
+ Set *string `android:"path"`
}
Address struct {
- Set *string
+ Set *string `android:"path"`
}
Hwaddress struct {
- Set *string
+ Set *string `android:"path"`
}
}
@@ -904,6 +913,15 @@
return false
}
+func (a *ApexSet) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{a.outputApex}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
func apexSetFactory() android.Module {
module := &ApexSet{}
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index d037664..f94e50f 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -15,10 +15,11 @@
package apex
import (
- "android/soong/dexpreopt"
+ "strings"
"testing"
"android/soong/android"
+ "android/soong/dexpreopt"
"android/soong/java"
)
@@ -31,7 +32,7 @@
result := android.GroupFixturePreparers(
prepareForTestWithSystemserverclasspathFragment,
prepareForTestWithMyapex,
- dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+ dexpreopt.FixtureSetApexSystemServerJars("myapex:foo", "myapex:bar", "myapex:baz"),
).RunTestWithBp(t, `
apex {
name: "myapex",
@@ -57,10 +58,36 @@
],
}
+ java_library {
+ name: "bar",
+ srcs: ["c.java"],
+ installable: true,
+ dex_preopt: {
+ profile: "bar-art-profile",
+ },
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ java_library {
+ name: "baz",
+ srcs: ["d.java"],
+ installable: true,
+ dex_preopt: {
+ profile_guided: true, // ignored
+ },
+ apex_available: [
+ "myapex",
+ ],
+ }
+
systemserverclasspath_fragment {
name: "mysystemserverclasspathfragment",
contents: [
"foo",
+ "bar",
+ "baz",
],
apex_available: [
"myapex",
@@ -68,15 +95,24 @@
}
`)
- ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ ctx := result.TestContext
+
+ ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"etc/classpaths/systemserverclasspath.pb",
"javalib/foo.jar",
+ "javalib/bar.jar",
+ "javalib/bar.jar.prof",
+ "javalib/baz.jar",
})
- java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex_image", []string{
`myapex.key`,
`mysystemserverclasspathfragment`,
})
+
+ assertProfileGuided(t, ctx, "foo", "android_common_apex10000", false)
+ assertProfileGuided(t, ctx, "bar", "android_common_apex10000", true)
+ assertProfileGuided(t, ctx, "baz", "android_common_apex10000", false)
}
func TestSystemserverclasspathFragmentNoGeneratedProto(t *testing.T) {
@@ -186,7 +222,7 @@
result := android.GroupFixturePreparers(
prepareForTestWithSystemserverclasspathFragment,
prepareForTestWithMyapex,
- dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+ dexpreopt.FixtureSetApexSystemServerJars("myapex:foo", "myapex:bar"),
).RunTestWithBp(t, `
prebuilt_apex {
name: "myapex",
@@ -209,11 +245,23 @@
],
}
+ java_import {
+ name: "bar",
+ jars: ["bar.jar"],
+ dex_preopt: {
+ profile_guided: true,
+ },
+ apex_available: [
+ "myapex",
+ ],
+ }
+
prebuilt_systemserverclasspath_fragment {
name: "mysystemserverclasspathfragment",
prefer: true,
contents: [
"foo",
+ "bar",
],
apex_available: [
"myapex",
@@ -221,22 +269,34 @@
}
`)
- java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{
+ ctx := result.TestContext
+
+ java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
`myapex.apex.selector`,
`prebuilt_mysystemserverclasspathfragment`,
})
- java.CheckModuleDependencies(t, result.TestContext, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+ java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
`myapex.deapexer`,
+ `prebuilt_bar`,
`prebuilt_foo`,
})
+
+ ensureExactDeapexedContents(t, ctx, "myapex", "android_common", []string{
+ "javalib/foo.jar",
+ "javalib/bar.jar",
+ "javalib/bar.jar.prof",
+ })
+
+ assertProfileGuided(t, ctx, "foo", "android_common_myapex", false)
+ assertProfileGuided(t, ctx, "bar", "android_common_myapex", true)
}
func TestSystemserverclasspathFragmentStandaloneContents(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForTestWithSystemserverclasspathFragment,
prepareForTestWithMyapex,
- dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo"),
+ dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo", "myapex:bar", "myapex:baz"),
).RunTestWithBp(t, `
apex {
name: "myapex",
@@ -262,10 +322,36 @@
],
}
+ java_library {
+ name: "bar",
+ srcs: ["c.java"],
+ dex_preopt: {
+ profile: "bar-art-profile",
+ },
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ java_library {
+ name: "baz",
+ srcs: ["d.java"],
+ dex_preopt: {
+ profile_guided: true, // ignored
+ },
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
systemserverclasspath_fragment {
name: "mysystemserverclasspathfragment",
standalone_contents: [
"foo",
+ "bar",
+ "baz",
],
apex_available: [
"myapex",
@@ -273,17 +359,26 @@
}
`)
- ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ ctx := result.TestContext
+
+ ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"etc/classpaths/systemserverclasspath.pb",
"javalib/foo.jar",
+ "javalib/bar.jar",
+ "javalib/bar.jar.prof",
+ "javalib/baz.jar",
})
+
+ assertProfileGuided(t, ctx, "foo", "android_common_apex10000", false)
+ assertProfileGuided(t, ctx, "bar", "android_common_apex10000", true)
+ assertProfileGuided(t, ctx, "baz", "android_common_apex10000", false)
}
func TestPrebuiltStandaloneSystemserverclasspathFragmentContents(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForTestWithSystemserverclasspathFragment,
prepareForTestWithMyapex,
- dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo"),
+ dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo", "myapex:bar"),
).RunTestWithBp(t, `
prebuilt_apex {
name: "myapex",
@@ -306,11 +401,23 @@
],
}
+ java_import {
+ name: "bar",
+ jars: ["bar.jar"],
+ dex_preopt: {
+ profile_guided: true,
+ },
+ apex_available: [
+ "myapex",
+ ],
+ }
+
prebuilt_systemserverclasspath_fragment {
name: "mysystemserverclasspathfragment",
prefer: true,
standalone_contents: [
"foo",
+ "bar",
],
apex_available: [
"myapex",
@@ -318,8 +425,28 @@
}
`)
- java.CheckModuleDependencies(t, result.TestContext, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+ ctx := result.TestContext
+
+ java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
`myapex.deapexer`,
+ `prebuilt_bar`,
`prebuilt_foo`,
})
+
+ ensureExactDeapexedContents(t, ctx, "myapex", "android_common", []string{
+ "javalib/foo.jar",
+ "javalib/bar.jar",
+ "javalib/bar.jar.prof",
+ })
+
+ assertProfileGuided(t, ctx, "foo", "android_common_myapex", false)
+ assertProfileGuided(t, ctx, "bar", "android_common_myapex", true)
+}
+
+func assertProfileGuided(t *testing.T, ctx *android.TestContext, moduleName string, variant string, expected bool) {
+ dexpreopt := ctx.ModuleForTests(moduleName, variant).Rule("dexpreopt")
+ actual := strings.Contains(dexpreopt.RuleParams.Command, "--profile-file=")
+ if expected != actual {
+ t.Fatalf("Expected profile-guided to be %v, got %v", expected, actual)
+ }
}
diff --git a/apex/vndk.go b/apex/vndk.go
index ef3e5e1..095e89d 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -65,8 +65,21 @@
}
vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
- // Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
- ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion)
+ apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
+ if err != nil {
+ mctx.PropertyErrorf("vndk_version", "%s", err.Error())
+ return
+ }
+
+ targets := mctx.MultiTargets()
+ if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) &&
+ vndkVersion != mctx.DeviceConfig().PlatformVndkVersion() {
+ // Disable VNDK APEXes for VNDK versions less than the minimum supported API
+ // level for the primary architecture. This validation is skipped if the VNDK
+ // version matches the platform VNDK version, which can occur when the device
+ // config targets the 'current' VNDK (see `vndkVersion`).
+ ab.Disable()
+ }
}
}
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index d580e5a..21526c3 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -86,7 +86,6 @@
},
system_shared_libs: [],
stl: "none",
- notice: "custom_notice",
}
` + vndkLibrariesTxtFiles("current")
diff --git a/bazel/Android.bp b/bazel/Android.bp
index 9e7edc7..4709f5c 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -7,6 +7,7 @@
pkgPath: "android/soong/bazel",
srcs: [
"aquery.go",
+ "bazel_proxy.go",
"configurability.go",
"constants.go",
"properties.go",
@@ -20,6 +21,7 @@
"soong_build",
],
deps: [
+ "bazel_analysis_v2_proto",
"blueprint",
],
}
diff --git a/bazel/aquery.go b/bazel/aquery.go
index fd8cf67..4d39e8f 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -15,26 +15,37 @@
package bazel
import (
- "encoding/json"
+ "crypto/sha256"
+ "encoding/base64"
"fmt"
"path/filepath"
- "regexp"
+ "reflect"
+ "sort"
"strings"
+ "sync"
+ analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
+
+ "github.com/google/blueprint/metrics"
"github.com/google/blueprint/proptools"
+ "google.golang.org/protobuf/proto"
)
+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 +54,27 @@
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,11 +82,12 @@
type action struct {
Arguments []string
EnvironmentVariables []KeyValuePair
- InputDepSetIds []int
+ InputDepSetIds []depsetId
Mnemonic string
- OutputIds []int
+ OutputIds []artifactId
TemplateContent string
Substitutions []KeyValuePair
+ FileContents string
}
// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
@@ -79,369 +105,569 @@
Command string
Depfile *string
OutputPaths []string
- InputPaths []string
SymlinkPaths []string
- Env []KeyValuePair
+ Env []*analysis_v2_proto.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
+ FileContents 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
+ emptyDepsetIds map[depsetId]struct{}
+ // 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 sync.Map
+ // 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",
}
-// This pattern matches the MANIFEST file created for a py_binary target.
-var manifestFilePattern = regexp.MustCompile(".*/.+\\.runfiles/MANIFEST$")
+const (
+ middlemanMnemonic = "Middleman"
+ // The file name of py3wrapper.sh, which is used by py_binary targets.
+ py3wrapperFileName = "/py3wrapper.sh"
+)
-// The file name of py3wrapper.sh, which is used by py_binary targets.
-var py3wrapperFileName = "/py3wrapper.sh"
-
-func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) {
- pathFragments := map[int]pathFragment{}
- for _, pathFragment := range aqueryResult.PathFragments {
- pathFragments[pathFragment.Id] = pathFragment
+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
+}
- artifactIdToPath := map[int]string{}
+func newAqueryHandler(aqueryResult *analysis_v2_proto.ActionGraphContainer) (*aqueryArtifactHandler, error) {
+ pathFragments := indexBy(aqueryResult.PathFragments, func(pf *analysis_v2_proto.PathFragment) pathFragmentId {
+ return pathFragmentId(pf.Id)
+ })
+
+ artifactIdToPath := make(map[artifactId]string, len(aqueryResult.Artifacts))
for _, artifact := range aqueryResult.Artifacts {
- artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
+ artifactPath, err := expandPathFragment(pathFragmentId(artifact.PathFragmentId), pathFragments)
if err != nil {
return nil, err
}
- artifactIdToPath[artifact.Id] = artifactPath
+ artifactIdToPath[artifactId(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 inputs [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][]uint32{}
for _, actionEntry := range aqueryResult.Actions {
- if actionEntry.Mnemonic == "Middleman" {
+ if actionEntry.Mnemonic == middlemanMnemonic {
for _, outputId := range actionEntry.OutputIds {
- middlemanIdToDepsetIds[outputId] = actionEntry.InputDepSetIds
+ middlemanIdToDepsetIds[artifactId(outputId)] = actionEntry.InputDepSetIds
}
}
}
- 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 *analysis_v2_proto.DepSetOfFiles) depsetId {
+ return depsetId(d.Id)
+ })
- for _, inputDepSetId := range depsetIds {
- inputArtifacts, err := a.artifactIdsFromDepsetId(inputDepSetId)
+ aqueryHandler := aqueryArtifactHandler{
+ depsetIdToAqueryDepset: map[depsetId]AqueryDepset{},
+ depsetHashToAqueryDepset: map[string]AqueryDepset{},
+ depsetHashToArtifactPathsCache: sync.Map{},
+ emptyDepsetIds: make(map[depsetId]struct{}, 0),
+ 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...)
+ }
+
+ return &aqueryHandler, nil
+}
+
+// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
+// depset.
+func (a *aqueryArtifactHandler) populateDepsetMaps(depset *analysis_v2_proto.DepSetOfFiles, middlemanIdToDepsetIds map[artifactId][]uint32, depsetIdToDepset map[depsetId]*analysis_v2_proto.DepSetOfFiles) (*AqueryDepset, error) {
+ if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depsetId(depset.Id)]; containsDepset {
+ return &aqueryDepset, nil
+ }
+ transitiveDepsetIds := depset.TransitiveDepSetIds
+ directArtifactPaths := make([]string, 0, len(depset.DirectArtifactIds))
+ for _, id := range depset.DirectArtifactIds {
+ aId := artifactId(id)
+ path, pathExists := a.artifactIdToPath[aId]
+ if !pathExists {
+ return nil, fmt.Errorf("undefined input artifactId %d", aId)
+ }
+ // Filter out any inputs which are universally dropped, and swap middleman
+ // artifacts with their corresponding depsets.
+ if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[aId]; isMiddleman {
+ // Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
+ transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
+ } else if strings.HasSuffix(path, py3wrapperFileName) ||
+ strings.HasPrefix(path, "../bazel_tools") {
+ continue
+ // Drop these artifacts.
+ // See go/python-binary-host-mixed-build for more details.
+ // 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
+ // TemplateExpandAction handles everything necessary to launch a Pythin application.
+ // 2) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
+ // containing depset to always be considered newer than their outputs.
+ } else {
+ directArtifactPaths = append(directArtifactPaths, path)
+ }
+ }
+
+ childDepsetHashes := make([]string, 0, len(transitiveDepsetIds))
+ for _, id := range transitiveDepsetIds {
+ childDepsetId := depsetId(id)
+ childDepset, exists := depsetIdToDepset[childDepsetId]
+ if !exists {
+ if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
+ continue
} else {
- inputPath, exists := a.artifactIdToPath[inputId]
- if !exists {
- return nil, fmt.Errorf("undefined input artifactId %d", inputId)
- }
- inputPaths = append(inputPaths, inputPath)
+ return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
}
}
- }
-
- // 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) {
+ if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
+ return nil, err
+ } else if childAqueryDepset == nil {
continue
+ } else {
+ childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
}
- filteredInputPaths = append(filteredInputPaths, path)
}
- return filteredInputPaths
+ if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
+ a.emptyDepsetIds[depsetId(depset.Id)] = struct{}{}
+ return nil, nil
+ }
+ aqueryDepset := AqueryDepset{
+ ContentHash: depsetContentHash(directArtifactPaths, childDepsetHashes),
+ DirectArtifacts: directArtifactPaths,
+ TransitiveDepSetHashes: childDepsetHashes,
+ }
+ a.depsetIdToAqueryDepset[depsetId(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 {
- return result, nil
+// 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 []uint32) ([]string, error) {
+ var inputPaths []string
+
+ for _, id := range depsetIds {
+ inputDepSetId := depsetId(id)
+ depset := a.depsetIdToAqueryDepset[inputDepSetId]
+ inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
+ if err != nil {
+ return nil, err
+ }
+ for _, inputPath := range inputArtifacts {
+ inputPaths = append(inputPaths, inputPath)
+ }
}
- if depset, exists := a.depsetIdToDepset[depsetId]; exists {
- result := depset.DirectArtifactIds
- for _, childId := range depset.TransitiveDepSetIds {
- childArtifactIds, err := a.artifactIdsFromDepsetId(childId)
+
+ return inputPaths, nil
+}
+
+func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
+ if result, exists := a.depsetHashToArtifactPathsCache.Load(depsetHash); exists {
+ return result.([]string), nil
+ }
+ 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.Store(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{}
-
- var aqueryResult actionGraphContainer
- err := json.Unmarshal(aqueryJsonProto, &aqueryResult)
+// 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, eventHandler *metrics.EventHandler) ([]*BuildStatement, []AqueryDepset, error) {
+ aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
+ err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
if err != nil {
- return nil, err
- }
- aqueryHandler, err := newAqueryHandler(aqueryResult)
- if err != nil {
- return nil, err
+ return nil, nil, err
}
- for _, actionEntry := range aqueryResult.Actions {
- if shouldSkipAction(actionEntry) {
- continue
+ var aqueryHandler *aqueryArtifactHandler
+ {
+ eventHandler.Begin("init_handler")
+ defer eventHandler.End("init_handler")
+ aqueryHandler, err = newAqueryHandler(aqueryProto)
+ if err != nil {
+ return nil, nil, err
}
- 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)
+ }
+
+ // allocate both length and capacity so each goroutine can write to an index independently without
+ // any need for synchronization for slice access.
+ buildStatements := make([]*BuildStatement, len(aqueryProto.Actions))
+ {
+ eventHandler.Begin("build_statements")
+ defer eventHandler.End("build_statements")
+ wg := sync.WaitGroup{}
+ var errOnce sync.Once
+
+ for i, actionEntry := range aqueryProto.Actions {
+ wg.Add(1)
+ go func(i int, actionEntry *analysis_v2_proto.Action) {
+ buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry)
+ if aErr != nil {
+ errOnce.Do(func() {
+ err = aErr
+ })
} else {
- depfile = &outputPath
+ // set build statement at an index rather than appending such that each goroutine does not
+ // impact other goroutines
+ buildStatements[i] = buildStatement
+ }
+ wg.Done()
+ }(i, actionEntry)
+ }
+ wg.Wait()
+ }
+ if err != nil {
+ return nil, nil, err
+ }
+
+ depsetsByHash := map[string]AqueryDepset{}
+ depsets := make([]AqueryDepset, 0, len(aqueryHandler.depsetIdToAqueryDepset))
+ {
+ eventHandler.Begin("depsets")
+ defer eventHandler.End("depsets")
+ 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 {
- outputPaths = append(outputPaths, outputPath)
+ depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
+ depsets = append(depsets, aqueryDepset)
}
}
- 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,
- }
-
- 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[:]
- } 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
- } 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)
- }
- } else if len(actionEntry.Arguments) < 1 {
- return nil, fmt.Errorf("received action with no command: [%v]", buildStatement)
- }
- buildStatements = append(buildStatements, buildStatement)
}
- return buildStatements, nil
+ eventHandler.Do("build_statement_sort", func() {
+ // 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 {
+ // Sort all nil statements to the end of the slice
+ if buildStatements[i] == nil {
+ return false
+ } else if buildStatements[j] == nil {
+ return true
+ }
+ //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]
+ })
+ })
+ eventHandler.Do("depset_sort", func() {
+ 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 []uint32) ([]string, error) {
+ var hashes []string
+ for _, id := range inputDepsetIds {
+ dId := depsetId(id)
+ if aqueryDepset, exists := a.depsetIdToAqueryDepset[dId]; !exists {
+ if _, empty := a.emptyDepsetIds[dId]; !empty {
+ return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", dId)
+ }
+ } else {
+ hashes = append(hashes, aqueryDepset.ContentHash)
+ }
+ }
+ return hashes, nil
+}
+
+func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+ command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
+ inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
+ if err != nil {
+ return nil, err
+ }
+ outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+ if err != nil {
+ return nil, err
+ }
+
+ buildStatement := &BuildStatement{
+ Command: command,
+ Depfile: depfile,
+ OutputPaths: outputPaths,
+ InputDepsetHashes: inputDepsetHashes,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic,
+ }
+ return buildStatement, nil
+}
+
+func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+ outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+ if err != nil {
+ return nil, err
+ }
+ 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])
+ inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
+ if err != nil {
+ return nil, err
+ }
+
+ buildStatement := &BuildStatement{
+ Command: command,
+ Depfile: depfile,
+ OutputPaths: outputPaths,
+ InputDepsetHashes: inputDepsetHashes,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic,
+ }
+ return buildStatement, nil
+}
+
+func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+ outputPaths, _, err := a.getOutputPaths(actionEntry)
+ var depsetHashes []string
+ if err == nil {
+ depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return &BuildStatement{
+ Depfile: nil,
+ OutputPaths: outputPaths,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic,
+ InputDepsetHashes: depsetHashes,
+ FileContents: actionEntry.FileContents,
+ }, nil
+}
+
+func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+ outputPaths, _, err := a.getOutputPaths(actionEntry)
+ if err != nil {
+ return nil, err
+ }
+ inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
+ if err != nil {
+ return nil, err
+ }
+ 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)
+ }
+ // The actual command is generated in bazelSingleton.GenerateBuildActions
+ return &BuildStatement{
+ Depfile: nil,
+ OutputPaths: outputPaths,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic,
+ InputPaths: inputPaths,
+ }, nil
+}
+
+func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+ outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+ if err != nil {
+ return nil, err
+ }
+
+ inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
+ if err != nil {
+ return nil, err
+ }
+ 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`).
+ 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 *analysis_v2_proto.Action) (outputPaths []string, depfile *string, err error) {
+ for _, outputId := range actionEntry.OutputIds {
+ outputPath, exists := a.artifactIdToPath[artifactId(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.
-func expandTemplateContent(actionEntry action) string {
- replacerString := []string{}
- for _, pair := range actionEntry.Substitutions {
+func expandTemplateContent(actionEntry *analysis_v2_proto.Action) string {
+ replacerString := make([]string, len(actionEntry.Substitutions)*2)
+ for i, 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)
+ replacerString[i*2] = pair.Key
+ replacerString[i*2+1] = value
}
replacer := strings.NewReplacer(replacerString...)
return replacer.Replace(actionEntry.TemplateContent)
}
+// \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
+var commandLineArgumentReplacer = strings.NewReplacer(
+ `\`, `\\`,
+ `$`, `\$`,
+ "`", "\\`",
+ `"`, `\"`,
+ "\n", "\\n",
+ `'`, `'"'"'`,
+)
+
func escapeCommandlineArgument(str string) string {
- // \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
- replacer := strings.NewReplacer(
- `\`, `\\`,
- `$`, `\$`,
- "`", "\\`",
- `"`, `\"`,
- "\n", "\\n",
- `'`, `'"'"'`,
- )
- return replacer.Replace(str)
+ return commandLineArgumentReplacer.Replace(str)
}
-// removePy3wrapperScript removes py3wrapper.sh from the input paths and command of the action of
-// creating python zip file in mixed build mode. py3wrapper.sh is returned as input by aquery but
-// there is no action returned by aquery for creating it. So in mixed build "python3" is used
-// as the PYTHON_BINARY in python binary stub script, and py3wrapper.sh is not needed and should be
-// 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) {
- // Remove from inputs
- filteredInputPaths := []string{}
- for _, path := range bs.InputPaths {
- if !strings.HasSuffix(path, py3wrapperFileName) {
- filteredInputPaths = append(filteredInputPaths, path)
- }
- }
- newInputPaths = filteredInputPaths
-
- // Remove from command line
- var re = regexp.MustCompile(`\S*` + py3wrapperFileName)
- newCommand = re.ReplaceAllString(bs.Command, "")
- return
-}
-
-// addCommandForPyBinaryRunfilesDir adds commands creating python binary runfiles directory.
-// runfiles directory is created by using MANIFEST file and MANIFEST file is the output of
-// SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
-// but since SourceSymlinkManifest doesn't contain sufficient information
-// 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 {
- // 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
-}
-
-func isSymlinkAction(a action) bool {
- return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink"
-}
-
-func isTemplateExpandAction(a action) bool {
- return a.Mnemonic == "TemplateExpand"
-}
-
-func isPythonZipperAction(a action) bool {
- return a.Mnemonic == "PythonZipper"
-}
-
-func shouldSkipAction(a action) bool {
- // TODO(b/180945121): Handle complex symlink actions.
- if a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SourceSymlinkManifest" {
- return true
- }
+func (a *aqueryArtifactHandler) actionToBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+ switch actionEntry.Mnemonic {
// Middleman actions are not handled like other actions; they are handled separately as a
// preparatory step so that their inputs may be relayed to actions depending on middleman
// artifacts.
- if a.Mnemonic == "Middleman" {
- return true
- }
+ case middlemanMnemonic:
+ return nil, nil
+ // PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
+ case "PythonZipper":
+ return nil, nil
// Skip "Fail" actions, which are placeholder actions designed to always fail.
- if a.Mnemonic == "Fail" {
- return true
+ case "Fail":
+ return nil, nil
+ case "BaselineCoverage":
+ return nil, nil
+ case "Symlink", "SolibSymlink", "ExecutableSymlink":
+ return a.symlinkActionBuildStatement(actionEntry)
+ case "TemplateExpand":
+ if len(actionEntry.Arguments) < 1 {
+ return a.templateExpandActionBuildStatement(actionEntry)
+ }
+ case "FileWrite", "SourceSymlinkManifest":
+ return a.fileWriteActionBuildStatement(actionEntry)
+ case "SymlinkTree":
+ return a.symlinkTreeActionBuildStatement(actionEntry)
}
- // TODO(b/180946980): Handle FileWrite. The aquery proto currently contains no information
- // about the contents that are written.
- if a.Mnemonic == "FileWrite" {
- return true
+
+ if len(actionEntry.Arguments) < 1 {
+ return nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
}
- return false
+ return a.normalActionBuildStatement(actionEntry)
+
}
-func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
- labels := []string{}
+func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]*analysis_v2_proto.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 {
@@ -450,10 +676,11 @@
return "", fmt.Errorf("undefined path fragment id %d", currId)
}
labels = append([]string{currFragment.Label}, labels...)
- if currId == currFragment.ParentId {
- return "", fmt.Errorf("Fragment cannot refer to itself as parent %#v", currFragment)
+ parentId := pathFragmentId(currFragment.ParentId)
+ if currId == parentId {
+ return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
}
- currId = currFragment.ParentId
+ currId = parentId
}
return filepath.Join(labels...), nil
}
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 68e50c2..19a584f 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -15,423 +15,346 @@
package bazel
import (
+ "encoding/json"
"fmt"
"reflect"
+ "sort"
"testing"
+
+ analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
+
+ "github.com/google/blueprint/metrics"
+ "google.golang.org/protobuf/proto"
)
func TestAqueryMultiArchGenrule(t *testing.T) {
// This input string is retrieved from a real build of bionic-related genrules.
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }, {
- "id": 2,
- "pathFragmentId": 6
- }, {
- "id": 3,
- "pathFragmentId": 8
- }, {
- "id": 4,
- "pathFragmentId": 12
- }, {
- "id": 5,
- "pathFragmentId": 19
- }, {
- "id": 6,
- "pathFragmentId": 20
- }, {
- "id": 7,
- "pathFragmentId": 21
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
- "mnemonic": "Genrule",
- "configurationId": 1,
- "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
- "environmentVariables": [{
- "key": "PATH",
- "value": "/bin:/usr/bin:/usr/local/bin"
- }],
- "inputDepSetIds": [1],
- "outputIds": [4],
- "primaryOutputId": 4
- }, {
- "targetId": 2,
- "actionKey": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
- "mnemonic": "Genrule",
- "configurationId": 1,
- "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
- "environmentVariables": [{
- "key": "PATH",
- "value": "/bin:/usr/bin:/usr/local/bin"
- }],
- "inputDepSetIds": [2],
- "outputIds": [5],
- "primaryOutputId": 5
- }, {
- "targetId": 3,
- "actionKey": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
- "mnemonic": "Genrule",
- "configurationId": 1,
- "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
- "environmentVariables": [{
- "key": "PATH",
- "value": "/bin:/usr/bin:/usr/local/bin"
- }],
- "inputDepSetIds": [3],
- "outputIds": [6],
- "primaryOutputId": 6
- }, {
- "targetId": 4,
- "actionKey": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
- "mnemonic": "Genrule",
- "configurationId": 1,
- "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
- "environmentVariables": [{
- "key": "PATH",
- "value": "/bin:/usr/bin:/usr/local/bin"
- }],
- "inputDepSetIds": [4],
- "outputIds": [7],
- "primaryOutputId": 7
- }],
- "targets": [{
- "id": 1,
- "label": "@sourceroot//bionic/libc:syscalls-arm",
- "ruleClassId": 1
- }, {
- "id": 2,
- "label": "@sourceroot//bionic/libc:syscalls-x86",
- "ruleClassId": 1
- }, {
- "id": 3,
- "label": "@sourceroot//bionic/libc:syscalls-x86_64",
- "ruleClassId": 1
- }, {
- "id": 4,
- "label": "@sourceroot//bionic/libc:syscalls-arm64",
- "ruleClassId": 1
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1, 2, 3]
- }, {
- "id": 2,
- "directArtifactIds": [1, 2, 3]
- }, {
- "id": 3,
- "directArtifactIds": [1, 2, 3]
- }, {
- "id": 4,
- "directArtifactIds": [1, 2, 3]
- }],
- "configuration": [{
- "id": 1,
- "mnemonic": "k8-fastbuild",
- "platformName": "k8",
- "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
- }],
- "ruleClasses": [{
- "id": 1,
- "name": "genrule"
- }],
- "pathFragments": [{
- "id": 5,
- "label": ".."
- }, {
- "id": 4,
- "label": "sourceroot",
- "parentId": 5
- }, {
- "id": 3,
- "label": "bionic",
- "parentId": 4
- }, {
- "id": 2,
- "label": "libc",
- "parentId": 3
- }, {
- "id": 1,
- "label": "SYSCALLS.TXT",
- "parentId": 2
- }, {
- "id": 7,
- "label": "tools",
- "parentId": 2
- }, {
- "id": 6,
- "label": "gensyscalls.py",
- "parentId": 7
- }, {
- "id": 11,
- "label": "bazel_tools",
- "parentId": 5
- }, {
- "id": 10,
- "label": "tools",
- "parentId": 11
- }, {
- "id": 9,
- "label": "genrule",
- "parentId": 10
- }, {
- "id": 8,
- "label": "genrule-setup.sh",
- "parentId": 9
- }, {
- "id": 18,
- "label": "bazel-out"
- }, {
- "id": 17,
- "label": "sourceroot",
- "parentId": 18
- }, {
- "id": 16,
- "label": "k8-fastbuild",
- "parentId": 17
- }, {
- "id": 15,
- "label": "bin",
- "parentId": 16
- }, {
- "id": 14,
- "label": "bionic",
- "parentId": 15
- }, {
- "id": 13,
- "label": "libc",
- "parentId": 14
- }, {
- "id": 12,
- "label": "syscalls-arm.S",
- "parentId": 13
- }, {
- "id": 19,
- "label": "syscalls-x86.S",
- "parentId": 13
- }, {
- "id": 20,
- "label": "syscalls-x86_64.S",
- "parentId": 13
- }, {
- "id": 21,
- "label": "syscalls-arm64.S",
- "parentId": 13
- }]
-}`
- actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
- expectedBuildStatements := []BuildStatement{}
+ "Artifacts": [
+ { "Id": 1, "path_fragment_id": 1 },
+ { "Id": 2, "path_fragment_id": 6 },
+ { "Id": 3, "path_fragment_id": 8 },
+ { "Id": 4, "path_fragment_id": 12 },
+ { "Id": 5, "path_fragment_id": 19 },
+ { "Id": 6, "path_fragment_id": 20 },
+ { "Id": 7, "path_fragment_id": 21 }],
+ "Actions": [{
+ "target_id": 1,
+ "action_key": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
+ "Mnemonic": "Genrule",
+ "configuration_id": 1,
+ "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
+ "environment_variables": [{
+ "Key": "PATH",
+ "Value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "input_dep_set_ids": [1],
+ "output_ids": [4],
+ "primary_output_id": 4
+ }, {
+ "target_id": 2,
+ "action_key": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
+ "Mnemonic": "Genrule",
+ "configuration_id": 1,
+ "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
+ "environment_variables": [{
+ "Key": "PATH",
+ "Value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "input_dep_set_ids": [2],
+ "output_ids": [5],
+ "primary_output_id": 5
+ }, {
+ "target_id": 3,
+ "action_key": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
+ "Mnemonic": "Genrule",
+ "configuration_id": 1,
+ "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
+ "environment_variables": [{
+ "Key": "PATH",
+ "Value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "input_dep_set_ids": [3],
+ "output_ids": [6],
+ "primary_output_id": 6
+ }, {
+ "target_id": 4,
+ "action_key": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
+ "Mnemonic": "Genrule",
+ "configuration_id": 1,
+ "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
+ "environment_variables": [{
+ "Key": "PATH",
+ "Value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "input_dep_set_ids": [4],
+ "output_ids": [7],
+ "primary_output_id": 7
+ }],
+ "Targets": [
+ { "Id": 1, "Label": "@sourceroot//bionic/libc:syscalls-arm", "rule_class_id": 1 },
+ { "Id": 2, "Label": "@sourceroot//bionic/libc:syscalls-x86", "rule_class_id": 1 },
+ { "Id": 3, "Label": "@sourceroot//bionic/libc:syscalls-x86_64", "rule_class_id": 1 },
+ { "Id": 4, "Label": "@sourceroot//bionic/libc:syscalls-arm64", "rule_class_id": 1 }],
+ "dep_set_of_files": [
+ { "Id": 1, "direct_artifact_ids": [1, 2, 3] },
+ { "Id": 2, "direct_artifact_ids": [1, 2, 3] },
+ { "Id": 3, "direct_artifact_ids": [1, 2, 3] },
+ { "Id": 4, "direct_artifact_ids": [1, 2, 3] }],
+ "Configuration": [{
+ "Id": 1,
+ "Mnemonic": "k8-fastbuild",
+ "platform_name": "k8",
+ "Checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
+ }],
+ "rule_classes": [{ "Id": 1, "Name": "genrule"}],
+ "path_fragments": [
+ { "Id": 5, "Label": ".." },
+ { "Id": 4, "Label": "sourceroot", "parent_id": 5 },
+ { "Id": 3, "Label": "bionic", "parent_id": 4 },
+ { "Id": 2, "Label": "libc", "parent_id": 3 },
+ { "Id": 1, "Label": "SYSCALLS.TXT", "parent_id": 2 },
+ { "Id": 7, "Label": "tools", "parent_id": 2 },
+ { "Id": 6, "Label": "gensyscalls.py", "parent_id": 7 },
+ { "Id": 11, "Label": "bazel_tools", "parent_id": 5 },
+ { "Id": 10, "Label": "tools", "parent_id": 11 },
+ { "Id": 9, "Label": "genrule", "parent_id": 10 },
+ { "Id": 8, "Label": "genrule-setup.sh", "parent_id": 9 },
+ { "Id": 18, "Label": "bazel-out" },
+ { "Id": 17, "Label": "sourceroot", "parent_id": 18 },
+ { "Id": 16, "Label": "k8-fastbuild", "parent_id": 17 },
+ { "Id": 15, "Label": "bin", "parent_id": 16 },
+ { "Id": 14, "Label": "bionic", "parent_id": 15 },
+ { "Id": 13, "Label": "libc", "parent_id": 14 },
+ { "Id": 12, "Label": "syscalls-arm.S", "parent_id": 13 },
+ { "Id": 19, "Label": "syscalls-x86.S", "parent_id": 13 },
+ { "Id": 20, "Label": "syscalls-x86_64.S", "parent_id": 13 },
+ { "Id": 21, "Label": "syscalls-arm64.S", "parent_id": 13 }]
+}
+`
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
+ var expectedBuildStatements []*BuildStatement
for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
expectedBuildStatements = append(expectedBuildStatements,
- BuildStatement{
+ &BuildStatement{
Command: fmt.Sprintf(
"/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
arch, arch),
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"},
+ Env: []*analysis_v2_proto.KeyValuePair{
+ {Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
},
Mnemonic: "Genrule",
})
}
assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
+
+ expectedFlattenedInputs := []string{
+ "../sourceroot/bionic/libc/SYSCALLS.TXT",
+ "../sourceroot/bionic/libc/tools/gensyscalls.py",
+ }
+ // 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) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }, {
- "id": 2,
- "pathFragmentId": 2
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "x",
- "arguments": ["touch", "foo"],
- "inputDepSetIds": [1],
- "outputIds": [3],
- "primaryOutputId": 3
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1, 2]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "one"
- }, {
- "id": 2,
- "label": "two"
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [1],
+ "output_ids": [3],
+ "primary_output_id": 3
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1, 2] }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "two" }]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, "undefined outputId 3")
}
-func TestInvalidInputDepsetId(t *testing.T) {
+func TestInvalidInputDepsetIdFromAction(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": [2],
- "outputIds": [1],
- "primaryOutputId": 1
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1, 2]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "one"
- }, {
- "id": 2,
- "label": "two"
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [2],
+ "output_ids": [1],
+ "primary_output_id": 1
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1, 2] }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "two" }]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
- assertError(t, err, "undefined input depsetId 2")
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
+ assertError(t, err, "undefined (not even empty) input depsetId 2")
+}
+
+func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [1],
+ "output_ids": [1],
+ "primary_output_id": 1
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1, 2], "transitive_dep_set_ids": [42] }],
+ "path_fragments": [
+ { "id": 1, "label": "one"},
+ { "id": 2, "label": "two" }]
+}`
+
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
+ assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
}
func TestInvalidInputArtifactId(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, 3]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "one"
- }, {
- "id": 2,
- "label": "two"
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [1],
+ "output_ids": [1],
+ "primary_output_id": 1
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1, 3] }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "two" }]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, "undefined input artifactId 3")
}
func TestInvalidPathFragmentId(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]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "one"
- }, {
- "id": 2,
- "label": "two",
- "parentId": 3
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [1],
+ "output_ids": [1],
+ "primary_output_id": 1
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1, 2] }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "two", "parent_id": 3 }]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, "undefined path fragment id 3")
}
func TestDepfiles(t *testing.T) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }, {
- "id": 2,
- "pathFragmentId": 2
- }, {
- "id": 3,
- "pathFragmentId": 3
- }],
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 },
+ { "id": 3, "path_fragment_id": 3 }],
"actions": [{
- "targetId": 1,
- "actionKey": "x",
+ "target_Id": 1,
+ "action_Key": "x",
"mnemonic": "x",
"arguments": ["touch", "foo"],
- "inputDepSetIds": [1],
- "outputIds": [2, 3],
- "primaryOutputId": 2
+ "input_dep_set_ids": [1],
+ "output_ids": [2, 3],
+ "primary_output_id": 2
}],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1, 2, 3]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "one"
- }, {
- "id": 2,
- "label": "two"
- }, {
- "id": 3,
- "label": "two.d"
- }]
+ "dep_set_of_files": [
+ { "id": 1, "direct_Artifact_Ids": [1, 2, 3] }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "two" },
+ { "id": 3, "label": "two.d" }]
}`
- actual, err := AqueryBuildStatements([]byte(inputString))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
@@ -451,48 +374,37 @@
func TestMultipleDepfiles(t *testing.T) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }, {
- "id": 2,
- "pathFragmentId": 2
- }, {
- "id": 3,
- "pathFragmentId": 3
- }, {
- "id": 4,
- "pathFragmentId": 4
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "x",
- "arguments": ["touch", "foo"],
- "inputDepSetIds": [1],
- "outputIds": [2,3,4],
- "primaryOutputId": 2
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1, 2, 3, 4]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "one"
- }, {
- "id": 2,
- "label": "two"
- }, {
- "id": 3,
- "label": "two.d"
- }, {
- "id": 4,
- "label": "other.d"
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 },
+ { "id": 3, "path_fragment_id": 3 },
+ { "id": 4, "path_fragment_id": 4 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [1],
+ "output_ids": [2,3,4],
+ "primary_output_id": 2
+ }],
+ "dep_set_of_files": [{
+ "id": 1,
+ "direct_artifact_ids": [1, 2, 3, 4]
+ }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "two" },
+ { "id": 3, "label": "two.d" },
+ { "id": 4, "label": "other.d" }]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
}
@@ -501,362 +413,456 @@
// a single action with many inputs given via a deep depset.
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }, {
- "id": 2,
- "pathFragmentId": 7
- }, {
- "id": 3,
- "pathFragmentId": 8
- }, {
- "id": 4,
- "pathFragmentId": 9
- }, {
- "id": 5,
- "pathFragmentId": 10
- }, {
- "id": 6,
- "pathFragmentId": 11
- }, {
- "id": 7,
- "pathFragmentId": 12
- }, {
- "id": 8,
- "pathFragmentId": 13
- }, {
- "id": 9,
- "pathFragmentId": 14
- }, {
- "id": 10,
- "pathFragmentId": 15
- }, {
- "id": 11,
- "pathFragmentId": 16
- }, {
- "id": 12,
- "pathFragmentId": 17
- }, {
- "id": 13,
- "pathFragmentId": 18
- }, {
- "id": 14,
- "pathFragmentId": 19
- }, {
- "id": 15,
- "pathFragmentId": 20
- }, {
- "id": 16,
- "pathFragmentId": 21
- }, {
- "id": 17,
- "pathFragmentId": 22
- }, {
- "id": 18,
- "pathFragmentId": 23
- }, {
- "id": 19,
- "pathFragmentId": 24
- }, {
- "id": 20,
- "pathFragmentId": 25
- }, {
- "id": 21,
- "pathFragmentId": 26
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
- "mnemonic": "Action",
- "configurationId": 1,
- "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
- "inputDepSetIds": [1],
- "outputIds": [21],
- "primaryOutputId": 21
- }],
- "depSetOfFiles": [{
- "id": 3,
- "directArtifactIds": [1, 2, 3, 4, 5]
- }, {
- "id": 4,
- "directArtifactIds": [6, 7, 8, 9, 10]
- }, {
- "id": 2,
- "transitiveDepSetIds": [3, 4],
- "directArtifactIds": [11, 12, 13, 14, 15]
- }, {
- "id": 5,
- "directArtifactIds": [16, 17, 18, 19]
- }, {
- "id": 1,
- "transitiveDepSetIds": [2, 5],
- "directArtifactIds": [20]
- }],
- "pathFragments": [{
- "id": 6,
- "label": "bazel-out"
- }, {
- "id": 5,
- "label": "sourceroot",
- "parentId": 6
- }, {
- "id": 4,
- "label": "k8-fastbuild",
- "parentId": 5
- }, {
- "id": 3,
- "label": "bin",
- "parentId": 4
- }, {
- "id": 2,
- "label": "testpkg",
- "parentId": 3
- }, {
- "id": 1,
- "label": "test_1",
- "parentId": 2
- }, {
- "id": 7,
- "label": "test_2",
- "parentId": 2
- }, {
- "id": 8,
- "label": "test_3",
- "parentId": 2
- }, {
- "id": 9,
- "label": "test_4",
- "parentId": 2
- }, {
- "id": 10,
- "label": "test_5",
- "parentId": 2
- }, {
- "id": 11,
- "label": "test_6",
- "parentId": 2
- }, {
- "id": 12,
- "label": "test_7",
- "parentId": 2
- }, {
- "id": 13,
- "label": "test_8",
- "parentId": 2
- }, {
- "id": 14,
- "label": "test_9",
- "parentId": 2
- }, {
- "id": 15,
- "label": "test_10",
- "parentId": 2
- }, {
- "id": 16,
- "label": "test_11",
- "parentId": 2
- }, {
- "id": 17,
- "label": "test_12",
- "parentId": 2
- }, {
- "id": 18,
- "label": "test_13",
- "parentId": 2
- }, {
- "id": 19,
- "label": "test_14",
- "parentId": 2
- }, {
- "id": 20,
- "label": "test_15",
- "parentId": 2
- }, {
- "id": 21,
- "label": "test_16",
- "parentId": 2
- }, {
- "id": 22,
- "label": "test_17",
- "parentId": 2
- }, {
- "id": 23,
- "label": "test_18",
- "parentId": 2
- }, {
- "id": 24,
- "label": "test_19",
- "parentId": 2
- }, {
- "id": 25,
- "label": "test_root",
- "parentId": 2
- }, {
- "id": 26,
- "label": "test_out",
- "parentId": 2
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 7 },
+ { "id": 3, "path_fragment_id": 8 },
+ { "id": 4, "path_fragment_id": 9 },
+ { "id": 5, "path_fragment_id": 10 },
+ { "id": 6, "path_fragment_id": 11 },
+ { "id": 7, "path_fragment_id": 12 },
+ { "id": 8, "path_fragment_id": 13 },
+ { "id": 9, "path_fragment_id": 14 },
+ { "id": 10, "path_fragment_id": 15 },
+ { "id": 11, "path_fragment_id": 16 },
+ { "id": 12, "path_fragment_id": 17 },
+ { "id": 13, "path_fragment_id": 18 },
+ { "id": 14, "path_fragment_id": 19 },
+ { "id": 15, "path_fragment_id": 20 },
+ { "id": 16, "path_fragment_id": 21 },
+ { "id": 17, "path_fragment_id": 22 },
+ { "id": 18, "path_fragment_id": 23 },
+ { "id": 19, "path_fragment_id": 24 },
+ { "id": 20, "path_fragment_id": 25 },
+ { "id": 21, "path_fragment_id": 26 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
+ "mnemonic": "Action",
+ "configuration_id": 1,
+ "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
+ "input_dep_set_ids": [1],
+ "output_ids": [21],
+ "primary_output_id": 21
+ }],
+ "dep_set_of_files": [
+ { "id": 3, "direct_artifact_ids": [1, 2, 3, 4, 5] },
+ { "id": 4, "direct_artifact_ids": [6, 7, 8, 9, 10] },
+ { "id": 2, "transitive_dep_set_ids": [3, 4], "direct_artifact_ids": [11, 12, 13, 14, 15] },
+ { "id": 5, "direct_artifact_ids": [16, 17, 18, 19] },
+ { "id": 1, "transitive_dep_set_ids": [2, 5], "direct_artifact_ids": [20] }],
+ "path_fragments": [
+ { "id": 6, "label": "bazel-out" },
+ { "id": 5, "label": "sourceroot", "parent_id": 6 },
+ { "id": 4, "label": "k8-fastbuild", "parent_id": 5 },
+ { "id": 3, "label": "bin", "parent_id": 4 },
+ { "id": 2, "label": "testpkg", "parent_id": 3 },
+ { "id": 1, "label": "test_1", "parent_id": 2 },
+ { "id": 7, "label": "test_2", "parent_id": 2 },
+ { "id": 8, "label": "test_3", "parent_id": 2 },
+ { "id": 9, "label": "test_4", "parent_id": 2 },
+ { "id": 10, "label": "test_5", "parent_id": 2 },
+ { "id": 11, "label": "test_6", "parent_id": 2 },
+ { "id": 12, "label": "test_7", "parent_id": 2 },
+ { "id": 13, "label": "test_8", "parent_id": 2 },
+ { "id": 14, "label": "test_9", "parent_id": 2 },
+ { "id": 15, "label": "test_10", "parent_id": 2 },
+ { "id": 16, "label": "test_11", "parent_id": 2 },
+ { "id": 17, "label": "test_12", "parent_id": 2 },
+ { "id": 18, "label": "test_13", "parent_id": 2 },
+ { "id": 19, "label": "test_14", "parent_id": 2 },
+ { "id": 20, "label": "test_15", "parent_id": 2 },
+ { "id": 21, "label": "test_16", "parent_id": 2 },
+ { "id": 22, "label": "test_17", "parent_id": 2 },
+ { "id": 23, "label": "test_18", "parent_id": 2 },
+ { "id": 24, "label": "test_19", "parent_id": 2 },
+ { "id": 25, "label": "test_root", "parent_id": 2 },
+ { "id": 26,"label": "test_out", "parent_id": 2 }]
}`
- 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))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
}
- 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",
+ actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
+
+ 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"},
+ Mnemonic: "Action",
+ SymlinkPaths: []string{},
},
}
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.
+ var 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 TestSymlinkTree(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "SymlinkTree",
+ "configuration_id": 1,
+ "input_dep_set_ids": [1],
+ "output_ids": [2],
+ "primary_output_id": 2,
+ "execution_platform": "//build/bazel/platforms:linux_x86_64"
+ }],
+ "path_fragments": [
+ { "id": 1, "label": "foo.manifest" },
+ { "id": 2, "label": "foo.runfiles/MANIFEST" }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1] }]
+}
+`
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+ assertBuildStatements(t, []*BuildStatement{
+ &BuildStatement{
+ Command: "",
+ OutputPaths: []string{"foo.runfiles/MANIFEST"},
+ Mnemonic: "SymlinkTree",
+ InputPaths: []string{"foo.manifest"},
+ SymlinkPaths: []string{},
+ },
+ }, actual)
+}
+
+func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
+ const inputString = `{
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 10 },
+ { "id": 2, "path_fragment_id": 20 },
+ { "id": 3, "path_fragment_id": 30 },
+ { "id": 4, "path_fragment_id": 40 }],
+ "dep_set_of_files": [{
+ "id": 1111,
+ "direct_artifact_ids": [3 , 4]
+ }, {
+ "id": 2222,
+ "direct_artifact_ids": [3]
+ }],
+ "actions": [{
+ "target_id": 100,
+ "action_key": "x",
+ "input_dep_set_ids": [1111, 2222],
+ "mnemonic": "x",
+ "arguments": ["bogus", "command"],
+ "output_ids": [2],
+ "primary_output_id": 1
+ }],
+ "path_fragments": [
+ { "id": 10, "label": "input" },
+ { "id": 20, "label": "output" },
+ { "id": 30, "label": "dep1", "parent_id": 50 },
+ { "id": 40, "label": "dep2", "parent_id": 60 },
+ { "id": 50, "label": "bazel_tools", "parent_id": 60 },
+ { "id": 60, "label": ".."}
+ ]
+}`
+ /* depsets
+ 1111 2222
+ / \ |
+ ../dep2 ../bazel_tools/dep1
+ */
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
+ if len(actualDepsets) != 1 {
+ t.Errorf("expected 1 depset but found %#v", actualDepsets)
+ return
+ }
+ dep2Found := false
+ for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
+ if dep == "../bazel_tools/dep1" {
+ t.Errorf("dependency %s expected to be removed but still exists", dep)
+ } else if dep == "../dep2" {
+ dep2Found = true
+ }
+ }
+ if !dep2Found {
+ t.Errorf("dependency ../dep2 expected but not found")
+ }
+
+ expectedBuildStatement := &BuildStatement{
+ Command: "bogus command",
+ OutputPaths: []string{"output"},
+ Mnemonic: "x",
+ SymlinkPaths: []string{},
+ }
+ buildStatementFound := false
+ for _, actualBuildStatement := range actualBuildStatements {
+ if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
+ buildStatementFound = true
+ break
+ }
+ }
+ if !buildStatementFound {
+ t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
+ return
+ }
+}
+
+func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
+ const inputString = `{
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 10 },
+ { "id": 2, "path_fragment_id": 20 },
+ { "id": 3, "path_fragment_id": 30 }],
+ "dep_set_of_files": [{
+ "id": 1111,
+ "transitive_dep_set_ids": [2222]
+ }, {
+ "id": 2222,
+ "direct_artifact_ids": [3]
+ }, {
+ "id": 3333,
+ "direct_artifact_ids": [3]
+ }, {
+ "id": 4444,
+ "transitive_dep_set_ids": [3333]
+ }],
+ "actions": [{
+ "target_id": 100,
+ "action_key": "x",
+ "input_dep_set_ids": [1111, 4444],
+ "mnemonic": "x",
+ "arguments": ["bogus", "command"],
+ "output_ids": [2],
+ "primary_output_id": 1
+ }],
+ "path_fragments": [
+ { "id": 10, "label": "input" },
+ { "id": 20, "label": "output" },
+ { "id": 30, "label": "dep", "parent_id": 50 },
+ { "id": 50, "label": "bazel_tools", "parent_id": 60 },
+ { "id": 60, "label": ".."}
+ ]
+}`
+ /* depsets
+ 1111 4444
+ || ||
+ 2222 3333
+ | |
+ ../bazel_tools/dep
+ Note: in dep_set_of_files:
+ 1111 appears BEFORE its dependency,2222 while
+ 4444 appears AFTER its dependency 3333
+ and this test shows that that order doesn't affect empty depset pruning
+ */
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
+ if len(actualDepsets) != 0 {
+ t.Errorf("expected 0 depsets but found %#v", actualDepsets)
+ return
+ }
+
+ expectedBuildStatement := &BuildStatement{
+ Command: "bogus command",
+ OutputPaths: []string{"output"},
+ Mnemonic: "x",
+ }
+ buildStatementFound := false
+ for _, actualBuildStatement := range actualBuildStatements {
+ if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
+ buildStatementFound = true
+ break
+ }
+ }
+ if !buildStatementFound {
+ t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
+ return
+ }
}
func TestMiddlemenAction(t *testing.T) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }, {
- "id": 2,
- "pathFragmentId": 2
- }, {
- "id": 3,
- "pathFragmentId": 3
- }, {
- "id": 4,
- "pathFragmentId": 4
- }, {
- "id": 5,
- "pathFragmentId": 5
- }, {
- "id": 6,
- "pathFragmentId": 6
- }],
- "pathFragments": [{
- "id": 1,
- "label": "middleinput_one"
- }, {
- "id": 2,
- "label": "middleinput_two"
- }, {
- "id": 3,
- "label": "middleman_artifact"
- }, {
- "id": 4,
- "label": "maininput_one"
- }, {
- "id": 5,
- "label": "maininput_two"
- }, {
- "id": 6,
- "label": "output"
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1, 2]
- }, {
- "id": 2,
- "directArtifactIds": [3, 4, 5]
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "Middleman",
- "arguments": ["touch", "foo"],
- "inputDepSetIds": [1],
- "outputIds": [3],
- "primaryOutputId": 3
- }, {
- "targetId": 2,
- "actionKey": "y",
- "mnemonic": "Main action",
- "arguments": ["touch", "foo"],
- "inputDepSetIds": [2],
- "outputIds": [6],
- "primaryOutputId": 6
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 },
+ { "id": 3, "path_fragment_id": 3 },
+ { "id": 4, "path_fragment_id": 4 },
+ { "id": 5, "path_fragment_id": 5 },
+ { "id": 6, "path_fragment_id": 6 }],
+ "path_fragments": [
+ { "id": 1, "label": "middleinput_one" },
+ { "id": 2, "label": "middleinput_two" },
+ { "id": 3, "label": "middleman_artifact" },
+ { "id": 4, "label": "maininput_one" },
+ { "id": 5, "label": "maininput_two" },
+ { "id": 6, "label": "output" }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1, 2] },
+ { "id": 2, "direct_artifact_ids": [3, 4, 5] }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "Middleman",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [1],
+ "output_ids": [3],
+ "primary_output_id": 3
+ }, {
+ "target_id": 2,
+ "action_key": "y",
+ "mnemonic": "Main action",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [2],
+ "output_ids": [6],
+ "primary_output_id": 6
+ }]
}`
-
- actual, err := AqueryBuildStatements([]byte(inputString))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
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 := 2; len(actualBuildStatements) != expected {
+ t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), 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)
+ }
+
+ bs = actualBuildStatements[1]
+ if bs != nil {
+ t.Errorf("Expected nil action for skipped")
+ }
+}
+
+// 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
+ }
+ var 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]
+ var 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 %d depsets, but got %d depsets", len(expectedDepsetFiles), len(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) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 3
- }, {
- "id": 2,
- "pathFragmentId": 5
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "Symlink",
- "inputDepSetIds": [1],
- "outputIds": [2],
- "primaryOutputId": 2
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "one"
- }, {
- "id": 2,
- "label": "file_subdir",
- "parentId": 1
- }, {
- "id": 3,
- "label": "file",
- "parentId": 2
- }, {
- "id": 4,
- "label": "symlink_subdir",
- "parentId": 1
- }, {
- "id": 5,
- "label": "symlink",
- "parentId": 4
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 3 },
+ { "id": 2, "path_fragment_id": 5 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "Symlink",
+ "input_dep_set_ids": [1],
+ "output_ids": [2],
+ "primary_output_id": 2
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1] }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "file_subdir", "parent_id": 1 },
+ { "id": 3, "label": "file", "parent_id": 2 },
+ { "id": 4, "label": "symlink_subdir", "parent_id": 1 },
+ { "id": 5, "label": "symlink", "parent_id": 4 }]
}`
-
- actual, err := AqueryBuildStatements([]byte(inputString))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
- expectedBuildStatements := []BuildStatement{
- BuildStatement{
+ expectedBuildStatements := []*BuildStatement{
+ &BuildStatement{
Command: "mkdir -p one/symlink_subdir && " +
"rm -f one/symlink_subdir/symlink && " +
"ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
@@ -872,55 +878,39 @@
func TestSymlinkQuotesPaths(t *testing.T) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 3
- }, {
- "id": 2,
- "pathFragmentId": 5
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "SolibSymlink",
- "inputDepSetIds": [1],
- "outputIds": [2],
- "primaryOutputId": 2
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "one"
- }, {
- "id": 2,
- "label": "file subdir",
- "parentId": 1
- }, {
- "id": 3,
- "label": "file",
- "parentId": 2
- }, {
- "id": 4,
- "label": "symlink subdir",
- "parentId": 1
- }, {
- "id": 5,
- "label": "symlink",
- "parentId": 4
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 3 },
+ { "id": 2, "path_fragment_id": 5 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "SolibSymlink",
+ "input_dep_set_ids": [1],
+ "output_ids": [2],
+ "primary_output_id": 2
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1] }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "file subdir", "parent_id": 1 },
+ { "id": 3, "label": "file", "parent_id": 2 },
+ { "id": 4, "label": "symlink subdir", "parent_id": 1 },
+ { "id": 5, "label": "symlink", "parent_id": 4 }]
}`
- actual, err := AqueryBuildStatements([]byte(inputString))
-
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
- expectedBuildStatements := []BuildStatement{
- BuildStatement{
+ expectedBuildStatements := []*BuildStatement{
+ &BuildStatement{
Command: "mkdir -p 'one/symlink subdir' && " +
"rm -f 'one/symlink subdir/symlink' && " +
"ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
@@ -936,127 +926,106 @@
func TestSymlinkMultipleInputs(t *testing.T) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }, {
- "id": 2,
- "pathFragmentId": 2
- }, {
- "id": 3,
- "pathFragmentId": 3
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "Symlink",
- "inputDepSetIds": [1],
- "outputIds": [3],
- "primaryOutputId": 3
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1,2]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "file"
- }, {
- "id": 2,
- "label": "other_file"
- }, {
- "id": 3,
- "label": "symlink"
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 },
+ { "id": 3, "path_fragment_id": 3 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "Symlink",
+ "input_dep_set_ids": [1],
+ "output_ids": [3],
+ "primary_output_id": 3
+ }],
+ "dep_set_of_files": [{ "id": 1, "direct_artifact_ids": [1,2] }],
+ "path_fragments": [
+ { "id": 1, "label": "file" },
+ { "id": 2, "label": "other_file" },
+ { "id": 3, "label": "symlink" }]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
}
func TestSymlinkMultipleOutputs(t *testing.T) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }, {
- "id": 2,
- "pathFragmentId": 2
- }, {
- "id": 3,
- "pathFragmentId": 3
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "Symlink",
- "inputDepSetIds": [1],
- "outputIds": [2,3],
- "primaryOutputId": 2
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [1]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "file"
- }, {
- "id": 2,
- "label": "symlink"
- }, {
- "id": 3,
- "label": "other_symlink"
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 3, "path_fragment_id": 3 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "Symlink",
+ "input_dep_set_ids": [1],
+ "output_ids": [2,3],
+ "primary_output_id": 2
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1] }],
+ "path_fragments": [
+ { "id": 1, "label": "file" },
+ { "id": 2, "label": "symlink" },
+ { "id": 3, "label": "other_symlink" }]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
- assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
+ assertError(t, err, "undefined outputId 2")
}
func TestTemplateExpandActionSubstitutions(t *testing.T) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "TemplateExpand",
- "configurationId": 1,
- "outputIds": [1],
- "primaryOutputId": 1,
- "executionPlatform": "//build/bazel/platforms:linux_x86_64",
- "templateContent": "Test template substitutions: %token1%, %python_binary%",
- "substitutions": [{
- "key": "%token1%",
- "value": "abcd"
- },{
- "key": "%python_binary%",
- "value": "python3"
- }]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "template_file"
- }]
+ "artifacts": [{
+ "id": 1,
+ "path_fragment_id": 1
+ }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "TemplateExpand",
+ "configuration_id": 1,
+ "output_ids": [1],
+ "primary_output_id": 1,
+ "execution_platform": "//build/bazel/platforms:linux_x86_64",
+ "template_content": "Test template substitutions: %token1%, %python_binary%",
+ "substitutions": [
+ { "key": "%token1%", "value": "abcd" },
+ { "key": "%python_binary%", "value": "python3" }]
+ }],
+ "path_fragments": [
+ { "id": 1, "label": "template_file" }]
}`
- actual, err := AqueryBuildStatements([]byte(inputString))
-
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
- expectedBuildStatements := []BuildStatement{
- BuildStatement{
+ expectedBuildStatements := []*BuildStatement{
+ &BuildStatement{
Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
"chmod a+x template_file'",
- OutputPaths: []string{"template_file"},
- Mnemonic: "TemplateExpand",
+ OutputPaths: []string{"template_file"},
+ Mnemonic: "TemplateExpand",
+ SymlinkPaths: []string{},
},
}
assertBuildStatements(t, expectedBuildStatements, actual)
@@ -1065,303 +1034,106 @@
func TestTemplateExpandActionNoOutput(t *testing.T) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "TemplateExpand",
- "configurationId": 1,
- "primaryOutputId": 1,
- "executionPlatform": "//build/bazel/platforms:linux_x86_64",
- "templateContent": "Test template substitutions: %token1%, %python_binary%",
- "substitutions": [{
- "key": "%token1%",
- "value": "abcd"
- },{
- "key": "%python_binary%",
- "value": "python3"
- }]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "template_file"
- }]
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "TemplateExpand",
+ "configuration_id": 1,
+ "primary_output_id": 1,
+ "execution_platform": "//build/bazel/platforms:linux_x86_64",
+ "templateContent": "Test template substitutions: %token1%, %python_binary%",
+ "substitutions": [
+ { "key": "%token1%", "value": "abcd" },
+ { "key": "%python_binary%", "value": "python3" }]
+ }],
+ "path_fragments": [
+ { "id": 1, "label": "template_file" }]
}`
- _, err := AqueryBuildStatements([]byte(inputString))
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, `Expect 1 output to template expand action, got: output []`)
}
-func TestPythonZipperActionSuccess(t *testing.T) {
+func TestFileWrite(t *testing.T) {
const inputString = `
{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- },{
- "id": 2,
- "pathFragmentId": 2
- },{
- "id": 3,
- "pathFragmentId": 3
- },{
- "id": 4,
- "pathFragmentId": 4
- },{
- "id": 5,
- "pathFragmentId": 10
- },{
- "id": 10,
- "pathFragmentId": 20
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "TemplateExpand",
- "configurationId": 1,
- "outputIds": [1],
- "primaryOutputId": 1,
- "executionPlatform": "//build/bazel/platforms:linux_x86_64",
- "templateContent": "Test template substitutions: %token1%, %python_binary%",
- "substitutions": [{
- "key": "%token1%",
- "value": "abcd"
- },{
- "key": "%python_binary%",
- "value": "python3"
- }]
- },{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "PythonZipper",
- "configurationId": 1,
- "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
- "outputIds": [2],
- "inputDepSetIds": [1],
- "primaryOutputId": 2
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [4, 3, 5]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "python_binary"
- },{
- "id": 2,
- "label": "python_binary.zip"
- },{
- "id": 3,
- "label": "python_binary.py"
- },{
- "id": 9,
- "label": ".."
- }, {
- "id": 8,
- "label": "bazel_tools",
- "parentId": 9
- }, {
- "id": 7,
- "label": "tools",
- "parentId": 8
- }, {
- "id": 6,
- "label": "zip",
- "parentId": 7
- }, {
- "id": 5,
- "label": "zipper",
- "parentId": 6
- }, {
- "id": 4,
- "label": "zipper",
- "parentId": 5
- },{
- "id": 16,
- "label": "bazel-out"
- },{
- "id": 15,
- "label": "bazel_tools",
- "parentId": 16
- }, {
- "id": 14,
- "label": "k8-fastbuild",
- "parentId": 15
- }, {
- "id": 13,
- "label": "bin",
- "parentId": 14
- }, {
- "id": 12,
- "label": "tools",
- "parentId": 13
- }, {
- "id": 11,
- "label": "python",
- "parentId": 12
- }, {
- "id": 10,
- "label": "py3wrapper.sh",
- "parentId": 11
- },{
- "id": 20,
- "label": "python_binary"
- }]
-}`
- actual, err := AqueryBuildStatements([]byte(inputString))
-
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "FileWrite",
+ "configuration_id": 1,
+ "output_ids": [1],
+ "primary_output_id": 1,
+ "execution_platform": "//build/bazel/platforms:linux_x86_64",
+ "file_contents": "file data\n"
+ }],
+ "path_fragments": [
+ { "id": 1, "label": "foo.manifest" }]
+}
+`
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
+ assertBuildStatements(t, []*BuildStatement{
+ &BuildStatement{
+ OutputPaths: []string{"foo.manifest"},
+ Mnemonic: "FileWrite",
+ FileContents: "file data\n",
+ SymlinkPaths: []string{},
+ },
+ }, actual)
+}
- expectedBuildStatements := []BuildStatement{
- BuildStatement{
- Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > python_binary && " +
- "chmod a+x python_binary'",
- InputPaths: []string{"python_binary.zip"},
- OutputPaths: []string{"python_binary"},
- Mnemonic: "TemplateExpand",
- },
- BuildStatement{
- Command: "../bazel_tools/tools/zip/zipper/zipper cC python_binary.zip __main__.py=bazel-out/k8-fastbuild/bin/python_binary.temp " +
- "__init__.py= runfiles/__main__/__init__.py= runfiles/__main__/python_binary.py=python_binary.py && " +
- "../bazel_tools/tools/zip/zipper/zipper x python_binary.zip -d python_binary.runfiles && ln -sf runfiles/__main__ python_binary.runfiles",
- InputPaths: []string{"../bazel_tools/tools/zip/zipper/zipper", "python_binary.py"},
- OutputPaths: []string{"python_binary.zip"},
- Mnemonic: "PythonZipper",
- },
+func TestSourceSymlinkManifest(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 }],
+ "actions": [{
+ "target_id": 1,
+ "action_key": "x",
+ "mnemonic": "SourceSymlinkManifest",
+ "configuration_id": 1,
+ "output_ids": [1],
+ "primary_output_id": 1,
+ "execution_platform": "//build/bazel/platforms:linux_x86_64",
+ "file_contents": "symlink target\n"
+ }],
+ "path_fragments": [
+ { "id": 1, "label": "foo.manifest" }]
+}
+`
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
}
- assertBuildStatements(t, expectedBuildStatements, actual)
-}
-
-func TestPythonZipperActionNoInput(t *testing.T) {
- const inputString = `
-{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- },{
- "id": 2,
- "pathFragmentId": 2
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "PythonZipper",
- "configurationId": 1,
- "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
- "outputIds": [2],
- "primaryOutputId": 2
- }],
- "pathFragments": [{
- "id": 1,
- "label": "python_binary"
- },{
- "id": 2,
- "label": "python_binary.zip"
- }]
-}`
- _, err := AqueryBuildStatements([]byte(inputString))
- assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input [], output ["python_binary.zip"]`)
-}
-
-func TestPythonZipperActionNoOutput(t *testing.T) {
- const inputString = `
-{
- "artifacts": [{
- "id": 1,
- "pathFragmentId": 1
- },{
- "id": 2,
- "pathFragmentId": 2
- },{
- "id": 3,
- "pathFragmentId": 3
- },{
- "id": 4,
- "pathFragmentId": 4
- },{
- "id": 5,
- "pathFragmentId": 10
- }],
- "actions": [{
- "targetId": 1,
- "actionKey": "x",
- "mnemonic": "PythonZipper",
- "configurationId": 1,
- "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
- "inputDepSetIds": [1]
- }],
- "depSetOfFiles": [{
- "id": 1,
- "directArtifactIds": [4, 3, 5]
- }],
- "pathFragments": [{
- "id": 1,
- "label": "python_binary"
- },{
- "id": 2,
- "label": "python_binary.zip"
- },{
- "id": 3,
- "label": "python_binary.py"
- },{
- "id": 9,
- "label": ".."
- }, {
- "id": 8,
- "label": "bazel_tools",
- "parentId": 9
- }, {
- "id": 7,
- "label": "tools",
- "parentId": 8
- }, {
- "id": 6,
- "label": "zip",
- "parentId": 7
- }, {
- "id": 5,
- "label": "zipper",
- "parentId": 6
- }, {
- "id": 4,
- "label": "zipper",
- "parentId": 5
- },{
- "id": 16,
- "label": "bazel-out"
- },{
- "id": 15,
- "label": "bazel_tools",
- "parentId": 16
- }, {
- "id": 14,
- "label": "k8-fastbuild",
- "parentId": 15
- }, {
- "id": 13,
- "label": "bin",
- "parentId": 14
- }, {
- "id": 12,
- "label": "tools",
- "parentId": 13
- }, {
- "id": 11,
- "label": "python",
- "parentId": 12
- }, {
- "id": 10,
- "label": "py3wrapper.sh",
- "parentId": 11
- }]
-}`
- _, 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 []`)
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+ assertBuildStatements(t, []*BuildStatement{
+ &BuildStatement{
+ OutputPaths: []string{"foo.manifest"},
+ Mnemonic: "SourceSymlinkManifest",
+ SymlinkPaths: []string{},
+ },
+ }, actual)
}
func assertError(t *testing.T, err error, expected string) {
@@ -1375,57 +1147,80 @@
// Asserts that the given actual build statements match the given expected build statements.
// Build statement equivalence is determined using buildStatementEquals.
-func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+func assertBuildStatements(t *testing.T, expected []*BuildStatement, actual []*BuildStatement) {
t.Helper()
if len(expected) != len(actual) {
t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
len(expected), len(actual), expected, actual)
return
}
-ACTUAL_LOOP:
- for _, actualStatement := range actual {
- for _, expectedStatement := range expected {
- if buildStatementEquals(actualStatement, expectedStatement) {
- continue ACTUAL_LOOP
+ type compareFn = func(i int, j int) bool
+ byCommand := func(slice []*BuildStatement) compareFn {
+ return func(i int, j int) bool {
+ if slice[i] == nil {
+ return false
+ } else if slice[j] == nil {
+ return false
}
+ return slice[i].Command < slice[j].Command
}
- t.Errorf("unexpected build statement %#v.\n expected: %#v",
- actualStatement, expected)
- return
+ }
+ sort.SliceStable(expected, byCommand(expected))
+ sort.SliceStable(actual, byCommand(actual))
+ for i, actualStatement := range actual {
+ expectedStatement := expected[i]
+ if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
+ t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
+ differingField, actualStatement, expectedStatement)
+ return
+ }
}
}
-func buildStatementEquals(first BuildStatement, second BuildStatement) bool {
+func buildStatementEquals(first *BuildStatement, second *BuildStatement) string {
+ if (first == nil) != (second == nil) {
+ return "Nil"
+ }
if first.Mnemonic != second.Mnemonic {
- return false
+ return "Mnemonic"
}
if first.Command != second.Command {
- return false
+ return "Command"
}
// Ordering is significant for environment variables.
if !reflect.DeepEqual(first.Env, second.Env) {
- return false
+ return "Env"
}
// Ordering is irrelevant for input and output paths, so compare sets.
- if !reflect.DeepEqual(stringSet(first.InputPaths), stringSet(second.InputPaths)) {
- return false
+ if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
+ return "InputPaths"
}
- if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
- return false
+ if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
+ return "OutputPaths"
}
- if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) {
- return false
+ if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
+ return "SymlinkPaths"
}
if first.Depfile != second.Depfile {
- return false
+ return "Depfile"
}
- return true
+ return ""
}
-func stringSet(stringSlice []string) map[string]struct{} {
- stringMap := make(map[string]struct{})
- for _, s := range stringSlice {
- stringMap[s] = struct{}{}
+func sortedStrings(stringSlice []string) []string {
+ sorted := make([]string, len(stringSlice))
+ copy(sorted, stringSlice)
+ sort.Strings(sorted)
+ return sorted
+}
+
+// Transform the json format to ActionGraphContainer
+func JsonToActionGraphContainer(inputString string) ([]byte, error) {
+ var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
+ err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
+ if err != nil {
+ return []byte(""), err
}
- return stringMap
+ data, _ := proto.Marshal(&aqueryProtoResult)
+ return data, err
}
diff --git a/bazel/bazel_proxy.go b/bazel/bazel_proxy.go
new file mode 100644
index 0000000..d7f5e64
--- /dev/null
+++ b/bazel/bazel_proxy.go
@@ -0,0 +1,219 @@
+// Copyright 2023 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 bazel
+
+import (
+ "bytes"
+ "encoding/gob"
+ "fmt"
+ "net"
+ os_lib "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+// Logs fatal events of ProxyServer.
+type ServerLogger interface {
+ Fatal(v ...interface{})
+ Fatalf(format string, v ...interface{})
+}
+
+// CmdRequest is a request to the Bazel Proxy server.
+type CmdRequest struct {
+ // Args to the Bazel command.
+ Argv []string
+ // Environment variables to pass to the Bazel invocation. Strings should be of
+ // the form "KEY=VALUE".
+ Env []string
+}
+
+// CmdResponse is a response from the Bazel Proxy server.
+type CmdResponse struct {
+ Stdout string
+ Stderr string
+ ErrorString string
+}
+
+// ProxyClient is a client which can issue Bazel commands to the Bazel
+// proxy server. Requests are issued (and responses received) via a unix socket.
+// See ProxyServer for more details.
+type ProxyClient struct {
+ outDir string
+}
+
+// ProxyServer is a server which runs as a background goroutine. Each
+// request to the server describes a Bazel command which the server should run.
+// The server then issues the Bazel command, and returns a response describing
+// the stdout/stderr of the command.
+// Client-server communication is done via a unix socket under the output
+// directory.
+// The server is intended to circumvent sandboxing for subprocesses of the
+// build. The build orchestrator (soong_ui) can launch a server to exist outside
+// of sandboxing, and sandboxed processes (such as soong_build) can issue
+// bazel commands through this socket tunnel. This allows a sandboxed process
+// to issue bazel requests to a bazel that resides outside of sandbox. This
+// is particularly useful to maintain a persistent Bazel server which lives
+// past the duration of a single build.
+// The ProxyServer will only live as long as soong_ui does; the
+// underlying Bazel server will live past the duration of the build.
+type ProxyServer struct {
+ logger ServerLogger
+ outDir string
+ workspaceDir string
+ // The server goroutine will listen on this channel and stop handling requests
+ // once it is written to.
+ done chan struct{}
+}
+
+// NewProxyClient is a constructor for a ProxyClient.
+func NewProxyClient(outDir string) *ProxyClient {
+ return &ProxyClient{
+ outDir: outDir,
+ }
+}
+
+func unixSocketPath(outDir string) string {
+ return filepath.Join(outDir, "bazelsocket.sock")
+}
+
+// IssueCommand issues a request to the Bazel Proxy Server to issue a Bazel
+// request. Returns a response describing the output from the Bazel process
+// (if the Bazel process had an error, then the response will include an error).
+// Returns an error if there was an issue with the connection to the Bazel Proxy
+// server.
+func (b *ProxyClient) IssueCommand(req CmdRequest) (CmdResponse, error) {
+ var resp CmdResponse
+ var err error
+ // Check for connections every 1 second. This is chosen to be a relatively
+ // short timeout, because the proxy server should accept requests quite
+ // quickly.
+ d := net.Dialer{Timeout: 1 * time.Second}
+ var conn net.Conn
+ conn, err = d.Dial("unix", unixSocketPath(b.outDir))
+ if err != nil {
+ return resp, err
+ }
+ defer conn.Close()
+
+ enc := gob.NewEncoder(conn)
+ if err = enc.Encode(req); err != nil {
+ return resp, err
+ }
+ dec := gob.NewDecoder(conn)
+ err = dec.Decode(&resp)
+ return resp, err
+}
+
+// NewProxyServer is a constructor for a ProxyServer.
+func NewProxyServer(logger ServerLogger, outDir string, workspaceDir string) *ProxyServer {
+ return &ProxyServer{
+ logger: logger,
+ outDir: outDir,
+ workspaceDir: workspaceDir,
+ done: make(chan struct{}),
+ }
+}
+
+func (b *ProxyServer) handleRequest(conn net.Conn) error {
+ defer conn.Close()
+
+ dec := gob.NewDecoder(conn)
+ var req CmdRequest
+ if err := dec.Decode(&req); err != nil {
+ return fmt.Errorf("Error decoding request: %s", err)
+ }
+
+ bazelCmd := exec.Command("./build/bazel/bin/bazel", req.Argv...)
+ bazelCmd.Dir = b.workspaceDir
+ bazelCmd.Env = req.Env
+
+ stderr := &bytes.Buffer{}
+ bazelCmd.Stderr = stderr
+ var stdout string
+ var bazelErrString string
+
+ if output, err := bazelCmd.Output(); err != nil {
+ bazelErrString = fmt.Sprintf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
+ err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
+ } else {
+ stdout = string(output)
+ }
+
+ resp := CmdResponse{stdout, string(stderr.Bytes()), bazelErrString}
+ enc := gob.NewEncoder(conn)
+ if err := enc.Encode(&resp); err != nil {
+ return fmt.Errorf("Error encoding response: %s", err)
+ }
+ return nil
+}
+
+func (b *ProxyServer) listenUntilClosed(listener net.Listener) error {
+ for {
+ // Check for connections every 1 second. This is a blocking operation, so
+ // if the server is closed, the goroutine will not fully close until this
+ // deadline is reached. Thus, this deadline is short (but not too short
+ // so that the routine churns).
+ listener.(*net.UnixListener).SetDeadline(time.Now().Add(time.Second))
+ conn, err := listener.Accept()
+
+ select {
+ case <-b.done:
+ return nil
+ default:
+ }
+
+ if err != nil {
+ if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
+ // Timeout is normal and expected while waiting for client to establish
+ // a connection.
+ continue
+ } else {
+ b.logger.Fatalf("Listener error: %s", err)
+ }
+ }
+
+ err = b.handleRequest(conn)
+ if err != nil {
+ b.logger.Fatal(err)
+ }
+ }
+}
+
+// Start initializes the server unix socket and (in a separate goroutine)
+// handles requests on the socket until the server is closed. Returns an error
+// if a failure occurs during initialization. Will log any post-initialization
+// errors to the server's logger.
+func (b *ProxyServer) Start() error {
+ unixSocketAddr := unixSocketPath(b.outDir)
+ if err := os_lib.RemoveAll(unixSocketAddr); err != nil {
+ return fmt.Errorf("couldn't remove socket '%s': %s", unixSocketAddr, err)
+ }
+ listener, err := net.Listen("unix", unixSocketAddr)
+
+ if err != nil {
+ return fmt.Errorf("error listening on socket '%s': %s", unixSocketAddr, err)
+ }
+
+ go b.listenUntilClosed(listener)
+ return nil
+}
+
+// Close shuts down the server. This will stop the server from listening for
+// additional requests.
+func (b *ProxyServer) Close() {
+ b.done <- struct{}{}
+}
diff --git a/bazel/configurability.go b/bazel/configurability.go
index e5c1429..4680256 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -16,6 +16,8 @@
import (
"fmt"
+ "math"
+ "sort"
"strings"
)
@@ -28,7 +30,7 @@
archX86_64 = "x86_64"
// OsType names in arch.go
- osAndroid = "android"
+ OsAndroid = "android"
osDarwin = "darwin"
osLinux = "linux_glibc"
osLinuxMusl = "linux_musl"
@@ -45,6 +47,8 @@
osArchDarwinX86_64 = "darwin_x86_64"
osArchLinuxX86 = "linux_glibc_x86"
osArchLinuxX86_64 = "linux_glibc_x86_64"
+ osArchLinuxMuslArm = "linux_musl_arm"
+ osArchLinuxMuslArm64 = "linux_musl_arm64"
osArchLinuxMuslX86 = "linux_musl_x86"
osArchLinuxMuslX86_64 = "linux_musl_x86_64"
osArchLinuxBionicArm64 = "linux_bionic_arm64"
@@ -64,8 +68,80 @@
ConditionsDefaultSelectKey = "//conditions:default"
productVariableBazelPackage = "//build/bazel/product_variables"
+
+ AndroidAndInApex = "android-in_apex"
+ AndroidAndNonApex = "android-non_apex"
+
+ InApex = "in_apex"
+ NonApex = "non_apex"
)
+func PowerSetWithoutEmptySet[T any](items []T) [][]T {
+ resultSize := int(math.Pow(2, float64(len(items))))
+ powerSet := make([][]T, 0, resultSize-1)
+ for i := 1; i < resultSize; i++ {
+ combination := make([]T, 0)
+ for j := 0; j < len(items); j++ {
+ if (i>>j)%2 == 1 {
+ combination = append(combination, items[j])
+ }
+ }
+ powerSet = append(powerSet, combination)
+ }
+ return powerSet
+}
+
+func createPlatformArchMap() map[string]string {
+ // Copy of archFeatures from android/arch_list.go because the bazel
+ // package can't access the android package
+ archFeatures := map[string][]string{
+ "arm": {
+ "neon",
+ },
+ "arm64": {
+ "dotprod",
+ },
+ "riscv64": {},
+ "x86": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "avx512",
+ "popcnt",
+ "movbe",
+ },
+ "x86_64": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "avx512",
+ "popcnt",
+ },
+ }
+ result := make(map[string]string)
+ for arch, allFeatures := range archFeatures {
+ result[arch] = "//build/bazel/platforms/arch:" + arch
+ // Sometimes we want to select on multiple features being active, so
+ // add the power set of all possible features to the map. More details
+ // in android.ModuleBase.GetArchVariantProperties
+ for _, features := range PowerSetWithoutEmptySet(allFeatures) {
+ sort.Strings(features)
+ archFeaturesName := arch + "-" + strings.Join(features, "-")
+ result[archFeaturesName] = "//build/bazel/platforms/arch/variants:" + archFeaturesName
+ }
+ }
+ result[ConditionsDefaultConfigKey] = ConditionsDefaultSelectKey
+ return result
+}
+
var (
// These are the list of OSes and architectures with a Bazel config_setting
// and constraint value equivalent. These exist in arch.go, but the android
@@ -74,21 +150,14 @@
// A map of architectures to the Bazel label of the constraint_value
// for the @platforms//cpu:cpu constraint_setting
- platformArchMap = map[string]string{
- archArm: "//build/bazel/platforms/arch:arm",
- archArm64: "//build/bazel/platforms/arch:arm64",
- archRiscv64: "//build/bazel/platforms/arch:riscv64",
- archX86: "//build/bazel/platforms/arch:x86",
- archX86_64: "//build/bazel/platforms/arch:x86_64",
- ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of as arch select map.
- }
+ platformArchMap = createPlatformArchMap()
// A map of target operating systems to the Bazel label of the
// constraint_value for the @platforms//os:os constraint_setting
platformOsMap = map[string]string{
- osAndroid: "//build/bazel/platforms/os:android",
+ OsAndroid: "//build/bazel/platforms/os:android",
osDarwin: "//build/bazel/platforms/os:darwin",
- osLinux: "//build/bazel/platforms/os:linux",
+ osLinux: "//build/bazel/platforms/os:linux_glibc",
osLinuxMusl: "//build/bazel/platforms/os:linux_musl",
osLinuxBionic: "//build/bazel/platforms/os:linux_bionic",
osWindows: "//build/bazel/platforms/os:windows",
@@ -105,6 +174,8 @@
osArchDarwinX86_64: "//build/bazel/platforms/os_arch:darwin_x86_64",
osArchLinuxX86: "//build/bazel/platforms/os_arch:linux_glibc_x86",
osArchLinuxX86_64: "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
+ osArchLinuxMuslArm: "//build/bazel/platforms/os_arch:linux_musl_arm",
+ osArchLinuxMuslArm64: "//build/bazel/platforms/os_arch:linux_musl_arm64",
osArchLinuxMuslX86: "//build/bazel/platforms/os_arch:linux_musl_x86",
osArchLinuxMuslX86_64: "//build/bazel/platforms/os_arch:linux_musl_x86_64",
osArchLinuxBionicArm64: "//build/bazel/platforms/os_arch:linux_bionic_arm64",
@@ -120,7 +191,7 @@
// TODO(cparsons): Source from arch.go; this task is nontrivial, as it currently results
// in a cyclic dependency.
osToArchMap = map[string][]string{
- osAndroid: {archArm, archArm64, archRiscv64, archX86, archX86_64},
+ OsAndroid: {archArm, archArm64, archRiscv64, archX86, archX86_64},
osLinux: {archX86, archX86_64},
osLinuxMusl: {archX86, archX86_64},
osDarwin: {archArm64, archX86_64},
@@ -128,6 +199,23 @@
// TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
osWindows: {archX86, archX86_64},
}
+
+ osAndInApexMap = map[string]string{
+ AndroidAndInApex: "//build/bazel/rules/apex:android-in_apex",
+ AndroidAndNonApex: "//build/bazel/rules/apex:android-non_apex",
+ osDarwin: "//build/bazel/platforms/os:darwin",
+ osLinux: "//build/bazel/platforms/os:linux_glibc",
+ osLinuxMusl: "//build/bazel/platforms/os:linux_musl",
+ osLinuxBionic: "//build/bazel/platforms/os:linux_bionic",
+ osWindows: "//build/bazel/platforms/os:windows",
+ ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
+ }
+
+ inApexMap = map[string]string{
+ InApex: "//build/bazel/rules/apex:in_apex",
+ NonApex: "//build/bazel/rules/apex:non_apex",
+ ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
+ }
)
// basic configuration types
@@ -139,6 +227,8 @@
os
osArch
productVariables
+ osAndInApex
+ inApex
)
func osArchString(os string, arch string) string {
@@ -152,6 +242,8 @@
os: "os",
osArch: "arch_os",
productVariables: "product_variables",
+ osAndInApex: "os_in_apex",
+ inApex: "in_apex",
}[ct]
}
@@ -175,6 +267,14 @@
}
case productVariables:
// do nothing
+ case osAndInApex:
+ if _, ok := osAndInApexMap[config]; !ok {
+ panic(fmt.Errorf("Unknown os+in_apex config: %s", config))
+ }
+ case inApex:
+ if _, ok := inApexMap[config]; !ok {
+ panic(fmt.Errorf("Unknown in_apex config: %s", config))
+ }
default:
panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
}
@@ -198,6 +298,10 @@
return ConditionsDefaultSelectKey
}
return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
+ case osAndInApex:
+ return osAndInApexMap[config]
+ case inApex:
+ return inApexMap[config]
default:
panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType))
}
@@ -212,13 +316,18 @@
OsConfigurationAxis = ConfigurationAxis{configurationType: os}
// An axis for arch+os-specific configurations
OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
+ // An axis for os+in_apex-specific configurations
+ OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex}
+ // An axis for in_apex-specific configurations
+ InApexAxis = ConfigurationAxis{configurationType: inApex}
)
// ProductVariableConfigurationAxis returns an axis for the given product variable
-func ProductVariableConfigurationAxis(variable string) ConfigurationAxis {
+func ProductVariableConfigurationAxis(variable string, outerAxis ConfigurationAxis) ConfigurationAxis {
return ConfigurationAxis{
configurationType: productVariables,
subType: variable,
+ outerAxisType: outerAxis.configurationType,
}
}
@@ -229,11 +338,13 @@
// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
// distinguish between them without needing to list all 17 product variables.
subType string
+ // used to keep track of which product variables are arch variant
+ outerAxisType configurationType
}
func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
- if ca.configurationType < other.configurationType {
- return true
+ if ca.configurationType == other.configurationType {
+ return ca.subType < other.subType
}
- return ca.subType < other.subType
+ return ca.configurationType < other.configurationType
}
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 5d00b0b..6a3b3c8 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -1,17 +1,27 @@
package cquery
import (
+ "encoding/json"
"fmt"
"strings"
)
var (
- GetOutputFiles = &getOutputFilesRequestType{}
- GetPythonBinary = &getPythonBinaryRequestType{}
- GetCcInfo = &getCcInfoType{}
+ GetOutputFiles = &getOutputFilesRequestType{}
+ GetPythonBinary = &getPythonBinaryRequestType{}
+ GetCcInfo = &getCcInfoType{}
+ GetApexInfo = &getApexInfoType{}
+ GetCcUnstrippedInfo = &getCcUnstrippedInfoType{}
)
+type CcAndroidMkInfo struct {
+ LocalStaticLibs []string
+ LocalWholeStaticLibs []string
+ LocalSharedLibs []string
+}
+
type CcInfo struct {
+ CcAndroidMkInfo
OutputFiles []string
CcObjectFiles []string
CcSharedLibraryFiles []string
@@ -27,7 +37,10 @@
// be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles,
// but general cc_library will also have dynamic libraries in output files).
RootDynamicLibraries []string
+ TidyFiles []string
TocFile string
+ UnstrippedOutput string
+ AbiDiffFiles []string
}
type getOutputFilesRequestType struct{}
@@ -45,7 +58,7 @@
// all request-relevant information about a target and returns a string containing
// this information.
// The function should have the following properties:
-// - `target` is the only parameter to this function (a configured target).
+// - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
// - The return value must be a string.
// - The function body should not be indented outside of its own scope.
func (g getOutputFilesRequestType) StarlarkFunctionBody() string {
@@ -70,7 +83,7 @@
// all request-relevant information about a target and returns a string containing
// this information.
// The function should have the following properties:
-// - `target` is the only parameter to this function (a configured target).
+// - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
// - The return value must be a string.
// - The function body should not be indented outside of its own scope.
func (g getPythonBinaryRequestType) StarlarkFunctionBody() string {
@@ -97,13 +110,16 @@
// all request-relevant information about a target and returns a string containing
// this information.
// The function should have the following properties:
-// - `target` is the only parameter to this function (a configured target).
+// - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
// - The return value must be a string.
// - The function body should not be indented outside of its own scope.
func (g getCcInfoType) StarlarkFunctionBody() string {
return `
outputFiles = [f.path for f in target.files.to_list()]
-cc_info = providers(target)["CcInfo"]
+p = providers(target)
+cc_info = p.get("CcInfo")
+if not cc_info:
+ fail("%s did not provide CcInfo" % id_string)
includes = cc_info.compilation_context.includes.to_list()
system_includes = cc_info.compilation_context.system_includes.to_list()
@@ -115,8 +131,8 @@
linker_inputs = cc_info.linking_context.linker_inputs.to_list()
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]
+if static_info_tag in p:
+ static_info = p[static_info_tag]
ccObjectFiles = [f.path for f in static_info.objects]
rootStaticArchives = [static_info.root_static_archive.path]
else:
@@ -132,13 +148,21 @@
sharedLibraries = []
rootSharedLibraries = []
-shared_info_tag = "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"
-if shared_info_tag in providers(target):
- shared_info = providers(target)[shared_info_tag]
- for lib in shared_info.linker_input.libraries:
- path = lib.dynamic_library.path
- rootSharedLibraries += [path]
- sharedLibraries.append(path)
+shared_info_tag = "//build/bazel/rules/cc:cc_library_shared.bzl%CcSharedLibraryOutputInfo"
+stubs_tag = "//build/bazel/rules/cc:cc_stub_library.bzl%CcStubInfo"
+unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
+unstripped = ""
+
+if shared_info_tag in p:
+ shared_info = p[shared_info_tag]
+ path = shared_info.output_file.path
+ sharedLibraries.append(path)
+ rootSharedLibraries += [path]
+ unstripped = path
+ if unstripped_tag in p:
+ unstripped = p[unstripped_tag].unstripped.path
+elif stubs_tag in p:
+ rootSharedLibraries.extend([f.path for f in target.files.to_list()])
else:
for linker_input in linker_inputs:
for library in linker_input.libraries:
@@ -150,69 +174,212 @@
toc_file = ""
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
+if toc_file_tag in p:
+ toc_file = p[toc_file_tag].toc.path
else:
# NOTE: It's OK if there's no ToC, as Soong just uses it for optimization
pass
-returns = [
- outputFiles,
- ccObjectFiles,
- sharedLibraries,
- staticLibraries,
- includes,
- system_includes,
- headers,
- rootStaticArchives,
- rootSharedLibraries,
- [toc_file]
-]
+tidy_files = []
+clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
+if clang_tidy_info:
+ tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
-return "|".join([", ".join(r) for r in returns])`
+abi_diff_files = []
+abi_diff_info = p.get("//build/bazel/rules/abi:abi_dump.bzl%AbiDiffInfo")
+if abi_diff_info:
+ abi_diff_files = [f.path for f in abi_diff_info.diff_files.to_list()]
+
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+ androidmk_info = p[androidmk_tag]
+ local_static_libs = androidmk_info.local_static_libs
+ local_whole_static_libs = androidmk_info.local_whole_static_libs
+ local_shared_libs = androidmk_info.local_shared_libs
+
+return json.encode({
+ "OutputFiles": outputFiles,
+ "CcObjectFiles": ccObjectFiles,
+ "CcSharedLibraryFiles": sharedLibraries,
+ "CcStaticLibraryFiles": staticLibraries,
+ "Includes": includes,
+ "SystemIncludes": system_includes,
+ "Headers": headers,
+ "RootStaticArchives": rootStaticArchives,
+ "RootDynamicLibraries": rootSharedLibraries,
+ "TidyFiles": [t for t in tidy_files],
+ "TocFile": toc_file,
+ "UnstrippedOutput": unstripped,
+ "AbiDiffFiles": abi_diff_files,
+ "LocalStaticLibs": [l for l in local_static_libs],
+ "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+ "LocalSharedLibs": [l for l in local_shared_libs],
+})`
+
}
// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
// The given rawString must correspond to the string output which was created by evaluating the
// Starlark given in StarlarkFunctionBody.
func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
- const expectedLen = 10
- splitString := strings.Split(rawString, "|")
- if len(splitString) != expectedLen {
- return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
+ var ccInfo CcInfo
+ if err := parseJson(rawString, &ccInfo); err != nil {
+ return ccInfo, err
}
- outputFilesString := splitString[0]
- ccObjectsString := splitString[1]
- ccSharedLibrariesString := splitString[2]
- ccStaticLibrariesString := splitString[3]
- includesString := splitString[4]
- systemIncludesString := splitString[5]
- headersString := splitString[6]
- rootStaticArchivesString := splitString[7]
- rootDynamicLibrariesString := splitString[8]
- tocFile := splitString[9] // NOTE: Will be the empty string if there wasn't
+ return ccInfo, nil
+}
- outputFiles := splitOrEmpty(outputFilesString, ", ")
- ccObjects := splitOrEmpty(ccObjectsString, ", ")
- ccSharedLibraries := splitOrEmpty(ccSharedLibrariesString, ", ")
- ccStaticLibraries := splitOrEmpty(ccStaticLibrariesString, ", ")
- includes := splitOrEmpty(includesString, ", ")
- systemIncludes := splitOrEmpty(systemIncludesString, ", ")
- headers := splitOrEmpty(headersString, ", ")
- rootStaticArchives := splitOrEmpty(rootStaticArchivesString, ", ")
- rootDynamicLibraries := splitOrEmpty(rootDynamicLibrariesString, ", ")
- return CcInfo{
- OutputFiles: outputFiles,
- CcObjectFiles: ccObjects,
- CcSharedLibraryFiles: ccSharedLibraries,
- CcStaticLibraryFiles: ccStaticLibraries,
- Includes: includes,
- SystemIncludes: systemIncludes,
- Headers: headers,
- RootStaticArchives: rootStaticArchives,
- RootDynamicLibraries: rootDynamicLibraries,
- TocFile: tocFile,
- }, nil
+// Query Bazel for the artifacts generated by the apex modules.
+type getApexInfoType struct{}
+
+// Name returns a string name for this request type. Such request type names must be unique,
+// and must only consist of alphanumeric characters.
+func (g getApexInfoType) Name() string {
+ return "getApexInfo"
+}
+
+// StarlarkFunctionBody returns a starlark function body to process this request type.
+// The returned string is the body of a Starlark function which obtains
+// all request-relevant information about a target and returns a string containing
+// this information. The function should have the following properties:
+// - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
+// - The return value must be a string.
+// - The function body should not be indented outside of its own scope.
+func (g getApexInfoType) StarlarkFunctionBody() string {
+ return `
+info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexInfo")
+if not info:
+ fail("%s did not provide ApexInfo" % id_string)
+bundle_key_info = info.bundle_key_info
+container_key_info = info.container_key_info
+
+signed_compressed_output = "" # no .capex if the apex is not compressible, cannot be None as it needs to be json encoded.
+if info.signed_compressed_output:
+ signed_compressed_output = info.signed_compressed_output.path
+
+mk_info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexMkInfo")
+if not mk_info:
+ fail("%s did not provide ApexMkInfo" % id_string)
+
+tidy_files = []
+clang_tidy_info = providers(target).get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
+if clang_tidy_info:
+ tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
+
+return json.encode({
+ "signed_output": info.signed_output.path,
+ "signed_compressed_output": signed_compressed_output,
+ "unsigned_output": info.unsigned_output.path,
+ "provides_native_libs": [str(lib) for lib in info.provides_native_libs],
+ "requires_native_libs": [str(lib) for lib in info.requires_native_libs],
+ "bundle_key_info": [bundle_key_info.public_key.path, bundle_key_info.private_key.path],
+ "container_key_info": [container_key_info.pem.path, container_key_info.pk8.path, container_key_info.key_name],
+ "package_name": info.package_name,
+ "symbols_used_by_apex": info.symbols_used_by_apex.path,
+ "java_symbols_used_by_apex": info.java_symbols_used_by_apex.path,
+ "backing_libs": info.backing_libs.path,
+ "bundle_file": info.base_with_config_zip.path,
+ "installed_files": info.installed_files.path,
+ "make_modules_to_install": mk_info.make_modules_to_install,
+ "files_info": mk_info.files_info,
+ "tidy_files": [t for t in tidy_files],
+})`
+}
+
+type ApexInfo struct {
+ // From the ApexInfo provider
+ SignedOutput string `json:"signed_output"`
+ SignedCompressedOutput string `json:"signed_compressed_output"`
+ UnsignedOutput string `json:"unsigned_output"`
+ ProvidesLibs []string `json:"provides_native_libs"`
+ RequiresLibs []string `json:"requires_native_libs"`
+ BundleKeyInfo []string `json:"bundle_key_info"`
+ ContainerKeyInfo []string `json:"container_key_info"`
+ PackageName string `json:"package_name"`
+ SymbolsUsedByApex string `json:"symbols_used_by_apex"`
+ JavaSymbolsUsedByApex string `json:"java_symbols_used_by_apex"`
+ BackingLibs string `json:"backing_libs"`
+ BundleFile string `json:"bundle_file"`
+ InstalledFiles string `json:"installed_files"`
+ TidyFiles []string `json:"tidy_files"`
+
+ // From the ApexMkInfo provider
+ MakeModulesToInstall []string `json:"make_modules_to_install"`
+ PayloadFilesInfo []map[string]string `json:"files_info"`
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getApexInfoType) ParseResult(rawString string) (ApexInfo, error) {
+ var info ApexInfo
+ err := parseJson(rawString, &info)
+ return info, err
+}
+
+// getCcUnstrippedInfoType implements cqueryRequest interface. It handles the
+// interaction with `bazel cquery` to retrieve CcUnstrippedInfo provided
+// by the` cc_binary` and `cc_shared_library` rules.
+type getCcUnstrippedInfoType struct{}
+
+func (g getCcUnstrippedInfoType) Name() string {
+ return "getCcUnstrippedInfo"
+}
+
+func (g getCcUnstrippedInfoType) StarlarkFunctionBody() string {
+ return `
+p = providers(target)
+output_path = target.files.to_list()[0].path
+
+unstripped = output_path
+unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
+if unstripped_tag in p:
+ unstripped_info = p[unstripped_tag]
+ unstripped = unstripped_info.unstripped[0].files.to_list()[0].path
+
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+ androidmk_info = p[androidmk_tag]
+ local_static_libs = androidmk_info.local_static_libs
+ local_whole_static_libs = androidmk_info.local_whole_static_libs
+ local_shared_libs = androidmk_info.local_shared_libs
+
+tidy_files = []
+clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
+if clang_tidy_info:
+ tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
+
+return json.encode({
+ "OutputFile": output_path,
+ "UnstrippedOutput": unstripped,
+ "LocalStaticLibs": [l for l in local_static_libs],
+ "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+ "LocalSharedLibs": [l for l in local_shared_libs],
+ "TidyFiles": [t for t in tidy_files],
+})
+`
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getCcUnstrippedInfoType) ParseResult(rawString string) (CcUnstrippedInfo, error) {
+ var info CcUnstrippedInfo
+ err := parseJson(rawString, &info)
+ return info, err
+}
+
+type CcUnstrippedInfo struct {
+ CcAndroidMkInfo
+ OutputFile string
+ UnstrippedOutput string
+ TidyFiles []string
}
// splitOrEmpty is a modification of strings.Split() that returns an empty list
@@ -224,3 +391,15 @@
return strings.Split(s, sep)
}
}
+
+// parseJson decodes json string into the fields of the receiver.
+// Unknown attribute name causes panic.
+func parseJson(jsonString string, info interface{}) error {
+ decoder := json.NewDecoder(strings.NewReader(jsonString))
+ decoder.DisallowUnknownFields() //useful to detect typos, e.g. in unit tests
+ err := decoder.Decode(info)
+ if err != nil {
+ return fmt.Errorf("cannot parse cquery result '%s': %s", jsonString, err)
+ }
+ return nil
+}
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 606e285..7003ce1 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -1,13 +1,14 @@
package cquery
import (
- "fmt"
+ "encoding/json"
"reflect"
"strings"
"testing"
)
func TestGetOutputFilesParseResults(t *testing.T) {
+ t.Parallel()
testCases := []struct {
description string
input string
@@ -30,14 +31,17 @@
},
}
for _, tc := range testCases {
- actualOutput := GetOutputFiles.ParseResult(tc.input)
- if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
- t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
- }
+ t.Run(tc.description, func(t *testing.T) {
+ actualOutput := GetOutputFiles.ParseResult(tc.input)
+ if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+ t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
+ }
+ })
}
}
func TestGetPythonBinaryParseResults(t *testing.T) {
+ t.Parallel()
testCases := []struct {
description string
input string
@@ -55,82 +59,41 @@
},
}
for _, tc := range testCases {
- actualOutput := GetPythonBinary.ParseResult(tc.input)
- if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
- t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
- }
+ t.Run(tc.description, func(t *testing.T) {
+ actualOutput := GetPythonBinary.ParseResult(tc.input)
+ if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+ t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
+ }
+ })
}
}
func TestGetCcInfoParseResults(t *testing.T) {
- const expectedSplits = 10
- noResult := strings.Repeat("|", expectedSplits-1)
+ t.Parallel()
testCases := []struct {
- description string
- input string
- expectedOutput CcInfo
- expectedErrorMessage string
+ description string
+ inputCcInfo CcInfo
+ expectedOutput CcInfo
}{
{
- description: "no result",
- input: noResult,
- expectedOutput: CcInfo{
- OutputFiles: []string{},
- CcObjectFiles: []string{},
- CcSharedLibraryFiles: []string{},
- CcStaticLibraryFiles: []string{},
- Includes: []string{},
- SystemIncludes: []string{},
- Headers: []string{},
- RootStaticArchives: []string{},
- RootDynamicLibraries: []string{},
- TocFile: "",
- },
- },
- {
- description: "only output",
- input: "test" + noResult,
- expectedOutput: CcInfo{
- OutputFiles: []string{"test"},
- CcObjectFiles: []string{},
- CcSharedLibraryFiles: []string{},
- CcStaticLibraryFiles: []string{},
- Includes: []string{},
- SystemIncludes: []string{},
- Headers: []string{},
- RootStaticArchives: []string{},
- RootDynamicLibraries: []string{},
- TocFile: "",
- },
- },
- {
- description: "only ToC",
- input: noResult + "test",
- expectedOutput: CcInfo{
- OutputFiles: []string{},
- CcObjectFiles: []string{},
- CcSharedLibraryFiles: []string{},
- CcStaticLibraryFiles: []string{},
- Includes: []string{},
- SystemIncludes: []string{},
- Headers: []string{},
- RootStaticArchives: []string{},
- RootDynamicLibraries: []string{},
- TocFile: "test",
- },
+ description: "no result",
+ inputCcInfo: CcInfo{},
+ expectedOutput: CcInfo{},
},
{
description: "all items set",
- input: "out1, out2" +
- "|object1, object2" +
- "|shared_lib1, shared_lib2" +
- "|static_lib1, static_lib2" +
- "|., dir/subdir" +
- "|system/dir, system/other/dir" +
- "|dir/subdir/hdr.h" +
- "|rootstaticarchive1" +
- "|rootdynamiclibrary1" +
- "|lib.so.toc",
+ inputCcInfo: CcInfo{
+ OutputFiles: []string{"out1", "out2"},
+ CcObjectFiles: []string{"object1", "object2"},
+ CcSharedLibraryFiles: []string{"shared_lib1", "shared_lib2"},
+ CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"},
+ Includes: []string{".", "dir/subdir"},
+ SystemIncludes: []string{"system/dir", "system/other/dir"},
+ Headers: []string{"dir/subdir/hdr.h"},
+ RootStaticArchives: []string{"rootstaticarchive1"},
+ RootDynamicLibraries: []string{"rootdynamiclibrary1"},
+ TocFile: "lib.so.toc",
+ },
expectedOutput: CcInfo{
OutputFiles: []string{"out1", "out2"},
CcObjectFiles: []string{"object1", "object2"},
@@ -144,26 +107,203 @@
TocFile: "lib.so.toc",
},
},
+ }
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ jsonInput, _ := json.Marshal(tc.inputCcInfo)
+ actualOutput, err := GetCcInfo.ParseResult(string(jsonInput))
+ if err != nil {
+ t.Errorf("error parsing result: %q", err)
+ } else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+ t.Errorf("expected %#v\n!= actual %#v", tc.expectedOutput, actualOutput)
+ }
+ })
+ }
+}
+
+func TestGetCcInfoParseResultsError(t *testing.T) {
+ t.Parallel()
+ testCases := []struct {
+ description string
+ input string
+ expectedError string
+ }{
{
- description: "too few result splits",
- input: "|",
- expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, []string{"", ""}),
+ description: "not json",
+ input: ``,
+ expectedError: `cannot parse cquery result '': EOF`,
},
{
- description: "too many result splits",
- input: strings.Repeat("|", expectedSplits+1), // 2 too many
- expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)),
+ description: "invalid field",
+ input: `{
+ "toc_file": "dir/file.so.toc"
+}`,
+ expectedError: `json: unknown field "toc_file"`,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ _, err := GetCcInfo.ParseResult(tc.input)
+ if !strings.Contains(err.Error(), tc.expectedError) {
+ t.Errorf("expected string %q in error message, got %q", tc.expectedError, err)
+ }
+ })
+ }
+}
+
+func TestGetApexInfoParseResults(t *testing.T) {
+ t.Parallel()
+ testCases := []struct {
+ description string
+ input string
+ expectedOutput ApexInfo
+ }{
+ {
+ description: "no result",
+ input: "{}",
+ expectedOutput: ApexInfo{},
+ },
+ {
+ description: "one result",
+ input: `{
+ "signed_output":"my.apex",
+ "unsigned_output":"my.apex.unsigned",
+ "requires_native_libs":["//bionic/libc:libc","//bionic/libdl:libdl"],
+ "bundle_key_info":["foo.pem", "foo.privkey"],
+ "container_key_info":["foo.x509.pem", "foo.pk8", "foo"],
+ "package_name":"package.name",
+ "symbols_used_by_apex": "path/to/my.apex_using.txt",
+ "backing_libs":"path/to/backing.txt",
+ "bundle_file": "dir/bundlefile.zip",
+ "installed_files":"path/to/installed-files.txt",
+ "provides_native_libs":[],
+ "make_modules_to_install": ["foo","bar"]
+}`,
+ expectedOutput: ApexInfo{
+ // ApexInfo
+ SignedOutput: "my.apex",
+ UnsignedOutput: "my.apex.unsigned",
+ RequiresLibs: []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
+ ProvidesLibs: []string{},
+ BundleKeyInfo: []string{"foo.pem", "foo.privkey"},
+ ContainerKeyInfo: []string{"foo.x509.pem", "foo.pk8", "foo"},
+ PackageName: "package.name",
+ SymbolsUsedByApex: "path/to/my.apex_using.txt",
+ BackingLibs: "path/to/backing.txt",
+ BundleFile: "dir/bundlefile.zip",
+ InstalledFiles: "path/to/installed-files.txt",
+
+ // ApexMkInfo
+ MakeModulesToInstall: []string{"foo", "bar"},
+ },
},
}
for _, tc := range testCases {
- actualOutput, err := GetCcInfo.ParseResult(tc.input)
- if (err == nil && tc.expectedErrorMessage != "") ||
- (err != nil && err.Error() != tc.expectedErrorMessage) {
- t.Errorf("%q:\nexpected Error %s\n, got %s", tc.description, tc.expectedErrorMessage, err)
- } else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
- t.Errorf("%q:\n expected %#v\n!= actual %#v", tc.description, tc.expectedOutput, actualOutput)
- }
+ t.Run(tc.description, func(t *testing.T) {
+ actualOutput, err := GetApexInfo.ParseResult(tc.input)
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+ if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+ t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
+ }
+ })
+ }
+}
+
+func TestGetApexInfoParseResultsError(t *testing.T) {
+ t.Parallel()
+ testCases := []struct {
+ description string
+ input string
+ expectedError string
+ }{
+ {
+ description: "not json",
+ input: ``,
+ expectedError: `cannot parse cquery result '': EOF`,
+ },
+ {
+ description: "invalid field",
+ input: `{
+ "fake_field": "path/to/file"
+}`,
+ expectedError: `json: unknown field "fake_field"`,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ _, err := GetApexInfo.ParseResult(tc.input)
+ if !strings.Contains(err.Error(), tc.expectedError) {
+ t.Errorf("expected string %q in error message, got %q", tc.expectedError, err)
+ }
+ })
+ }
+}
+
+func TestGetCcUnstrippedParseResults(t *testing.T) {
+ t.Parallel()
+ testCases := []struct {
+ description string
+ input string
+ expectedOutput CcUnstrippedInfo
+ }{
+ {
+ description: "no result",
+ input: "{}",
+ expectedOutput: CcUnstrippedInfo{},
+ },
+ {
+ description: "one result",
+ input: `{"OutputFile":"myapp", "UnstrippedOutput":"myapp_unstripped"}`,
+ expectedOutput: CcUnstrippedInfo{
+ OutputFile: "myapp",
+ UnstrippedOutput: "myapp_unstripped",
+ },
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ actualOutput, err := GetCcUnstrippedInfo.ParseResult(tc.input)
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+ if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+ t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
+ }
+ })
+ }
+}
+
+func TestGetCcUnstrippedParseResultsErrors(t *testing.T) {
+ t.Parallel()
+ testCases := []struct {
+ description string
+ input string
+ expectedError string
+ }{
+ {
+ description: "not json",
+ input: ``,
+ expectedError: `cannot parse cquery result '': EOF`,
+ },
+ {
+ description: "invalid field",
+ input: `{
+ "fake_field": "path/to/file"
+}`,
+ expectedError: `json: unknown field "fake_field"`,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ _, err := GetCcUnstrippedInfo.ParseResult(tc.input)
+ if !strings.Contains(err.Error(), tc.expectedError) {
+ t.Errorf("expected string %q in error message, got %q", tc.expectedError, err)
+ }
+ })
}
}
diff --git a/bazel/properties.go b/bazel/properties.go
index a3555f8..40d0ba3 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -17,6 +17,7 @@
import (
"fmt"
"path/filepath"
+ "reflect"
"regexp"
"sort"
"strings"
@@ -73,6 +74,27 @@
}
}
+func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis {
+ keys := make([]ConfigurationAxis, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+
+ sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+ return keys
+}
+
+// MakeLabelListFromTargetNames creates a LabelList from unqualified target names
+// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
+func MakeLabelListFromTargetNames(targetNames []string) LabelList {
+ labels := []Label{}
+ for _, name := range targetNames {
+ label := Label{Label: ":" + name}
+ labels = append(labels, label)
+ }
+ return MakeLabelList(labels)
+}
+
func (ll *LabelList) Equals(other LabelList) bool {
if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
return false
@@ -94,6 +116,10 @@
return ll.Includes == nil && ll.Excludes == nil
}
+func (ll *LabelList) IsEmpty() bool {
+ return len(ll.Includes) == 0 && len(ll.Excludes) == 0
+}
+
func (ll *LabelList) deepCopy() LabelList {
return LabelList{
Includes: ll.Includes[:],
@@ -115,7 +141,7 @@
return dirs
}
-// Add inserts the label Label at the end of the LabelList.
+// Add inserts the label Label at the end of the LabelList.Includes.
func (ll *LabelList) Add(label *Label) {
if label == nil {
return
@@ -123,16 +149,48 @@
ll.Includes = append(ll.Includes, *label)
}
+// AddExclude inserts the label Label at the end of the LabelList.Excludes.
+func (ll *LabelList) AddExclude(label *Label) {
+ if label == nil {
+ return
+ }
+ ll.Excludes = append(ll.Excludes, *label)
+}
+
// Append appends the fields of other labelList to the corresponding fields of ll.
func (ll *LabelList) Append(other LabelList) {
if len(ll.Includes) > 0 || len(other.Includes) > 0 {
ll.Includes = append(ll.Includes, other.Includes...)
}
if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
- ll.Excludes = append(other.Excludes, other.Excludes...)
+ ll.Excludes = append(ll.Excludes, other.Excludes...)
}
}
+// Partition splits a LabelList into two LabelLists depending on the return value
+// of the predicate.
+// This function preserves the Includes and Excludes, but it does not provide
+// that information to the partition function.
+func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) {
+ predicated := LabelList{}
+ unpredicated := LabelList{}
+ for _, include := range ll.Includes {
+ if predicate(include) {
+ predicated.Add(&include)
+ } else {
+ unpredicated.Add(&include)
+ }
+ }
+ for _, exclude := range ll.Excludes {
+ if predicate(exclude) {
+ predicated.AddExclude(&exclude)
+ } else {
+ unpredicated.AddExclude(&exclude)
+ }
+ }
+ return predicated, unpredicated
+}
+
// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
// the slice in a sorted order.
func UniqueSortedBazelLabels(originalLabels []Label) []Label {
@@ -273,7 +331,19 @@
_, containsProductVariables := axisTypes[productVariables]
if containsProductVariables {
if containsOs || containsArch || containsOsArch {
- return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+ if containsArch {
+ allProductVariablesAreArchVariant := true
+ for k := range la.ConfigurableValues {
+ if k.configurationType == productVariables && k.outerAxisType != arch {
+ allProductVariablesAreArchVariant = false
+ }
+ }
+ if !allProductVariablesAreArchVariant {
+ return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+ }
+ } else {
+ return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+ }
}
}
if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
@@ -328,7 +398,7 @@
switch axis.configurationType {
case noConfig:
la.Value = &value
- case arch, os, osArch, productVariables:
+ case arch, os, osArch, productVariables, osAndInApex:
if la.ConfigurableValues == nil {
la.ConfigurableValues = make(configurableLabels)
}
@@ -344,7 +414,7 @@
switch axis.configurationType {
case noConfig:
return la.Value
- case arch, os, osArch, productVariables:
+ case arch, os, osArch, productVariables, osAndInApex:
return la.ConfigurableValues[axis][config]
default:
panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
@@ -353,13 +423,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
- for k := range la.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(la.ConfigurableValues)
}
// MakeLabelAttribute turns a string into a LabelAttribute
@@ -409,13 +473,18 @@
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)
switch axis.configurationType {
case noConfig:
ba.Value = value
- case arch, os, osArch, productVariables:
+ case arch, os, osArch, productVariables, osAndInApex:
if ba.ConfigurableValues == nil {
ba.ConfigurableValues = make(configurableBools)
}
@@ -465,6 +534,37 @@
return result, nil
}
+// ToStringListAttribute creates a StringListAttribute from this BoolAttribute,
+// where each bool corresponds to a string list value generated by the provided
+// function.
+// TODO(b/271425661): Generalize this
+func (ba *BoolAttribute) ToStringListAttribute(valueFunc func(boolPtr *bool, axis ConfigurationAxis, config string) []string) (StringListAttribute, error) {
+ mainVal := valueFunc(ba.Value, NoConfigAxis, "")
+ if !ba.HasConfigurableValues() {
+ return MakeStringListAttribute(mainVal), nil
+ }
+
+ result := StringListAttribute{}
+ if err := ba.Collapse(); err != nil {
+ return result, err
+ }
+
+ for axis, configToBools := range ba.ConfigurableValues {
+ if len(configToBools) < 1 {
+ continue
+ }
+ for config, boolPtr := range configToBools {
+ val := valueFunc(&boolPtr, axis, config)
+ if !reflect.DeepEqual(val, mainVal) {
+ result.SetSelectValue(axis, config, val)
+ }
+ }
+ result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
+ }
+
+ return result, nil
+}
+
// Collapse reduces the configurable axes of the boolean attribute to a single axis.
// This is necessary for final writing to bp2build, as a configurable boolean
// attribute can only be comprised by a single select.
@@ -531,7 +631,7 @@
switch axis.configurationType {
case noConfig:
return ba.Value
- case arch, os, osArch, productVariables:
+ case arch, os, osArch, productVariables, osAndInApex:
if v, ok := ba.ConfigurableValues[axis][config]; ok {
return &v
} else {
@@ -544,13 +644,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
- for k := range ba.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(ba.ConfigurableValues)
}
// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
@@ -610,6 +704,11 @@
// specific select statements where an empty list for a non-default select
// key has a meaning.
EmitEmptyList bool
+
+ // If a property has struct tag "variant_prepend", this value should
+ // be set to True, so that when bp2build generates BUILD.bazel, variant
+ // properties(select ...) come before general properties.
+ Prepend bool
}
type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
@@ -652,6 +751,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)
}
@@ -662,7 +766,7 @@
switch axis.configurationType {
case noConfig:
lla.Value = list
- case arch, os, osArch, productVariables:
+ case arch, os, osArch, productVariables, osAndInApex, inApex:
if lla.ConfigurableValues == nil {
lla.ConfigurableValues = make(configurableLabelLists)
}
@@ -678,7 +782,7 @@
switch axis.configurationType {
case noConfig:
return lla.Value
- case arch, os, osArch, productVariables:
+ case arch, os, osArch, productVariables, osAndInApex, inApex:
return lla.ConfigurableValues[axis][config]
default:
panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
@@ -687,13 +791,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
- for k := range lla.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(lla.ConfigurableValues)
}
// Append all values, including os and arch specific ones, from another
@@ -740,6 +838,16 @@
return false
}
+// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
+func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
+ for _, values := range lla.ConfigurableValues[axis] {
+ if !values.IsNil() {
+ return true
+ }
+ }
+ return false
+}
+
// IsEmpty returns true if the attribute has no values under any configuration.
func (lla LabelListAttribute) IsEmpty() bool {
if len(lla.Value.Includes) > 0 {
@@ -774,6 +882,26 @@
// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
// the base value and included in default values as appropriate.
func (lla *LabelListAttribute) ResolveExcludes() {
+ // If there are OsAndInApexAxis, we need to use
+ // * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be
+ // included in non-Android OSes
+ // * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_
+ // be included in the non-Android OSes
+ if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok {
+ inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey]
+ for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] {
+ // OsAndroid has already handled its excludes.
+ // We only need to copy the excludes from other arches, so if there are none, skip it.
+ if config == OsAndroid || len(labels.Excludes) == 0 {
+ continue
+ }
+ lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{
+ Includes: inApexLabels.Includes,
+ Excludes: labels.Excludes,
+ }
+ }
+ }
+
for axis, configToLabels := range lla.ConfigurableValues {
baseLabels := lla.Value.deepCopy()
for config, val := range configToLabels {
@@ -784,7 +912,7 @@
// then remove all config-specific excludes
allLabels := baseLabels.deepCopy()
allLabels.Append(val)
- lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
+ lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
}
// After going through all configs, delete the duplicates in the config
@@ -808,6 +936,29 @@
}
}
+// Partition splits a LabelListAttribute into two LabelListAttributes depending
+// on the return value of the predicate.
+// This function preserves the Includes and Excludes, but it does not provide
+// that information to the partition function.
+func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
+ predicated := LabelListAttribute{}
+ unpredicated := LabelListAttribute{}
+
+ valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
+ predicated.SetValue(valuePartitionTrue)
+ unpredicated.SetValue(valuePartitionFalse)
+
+ for axis, selectValueLabelLists := range lla.ConfigurableValues {
+ for config, labelList := range selectValueLabelLists {
+ configPredicated, configUnpredicated := labelList.Partition(predicate)
+ predicated.SetSelectValue(axis, config, configPredicated)
+ unpredicated.SetSelectValue(axis, config, configUnpredicated)
+ }
+ }
+
+ return predicated, unpredicated
+}
+
// OtherModuleContext is a limited context that has methods with information about other modules.
type OtherModuleContext interface {
ModuleFromName(name string) (blueprint.Module, bool)
@@ -946,6 +1097,156 @@
return ret
}
+// StringAttribute corresponds to the string Bazel attribute type with
+// support for additional metadata, like configurations.
+type StringAttribute struct {
+ // The base value of the string attribute.
+ Value *string
+
+ // The configured attribute label list Values. Optional
+ // a map of independent configurability axes
+ ConfigurableValues configurableStrings
+}
+
+type configurableStrings map[ConfigurationAxis]stringSelectValues
+
+func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
+ if cs[axis] == nil {
+ cs[axis] = make(stringSelectValues)
+ }
+ cs[axis][config] = str
+}
+
+type stringSelectValues map[string]*string
+
+// HasConfigurableValues returns true if the attribute contains axis-specific string values.
+func (sa StringAttribute) HasConfigurableValues() bool {
+ for _, selectValues := range sa.ConfigurableValues {
+ if len(selectValues) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// SetValue sets the base, non-configured value for the Label
+func (sa *StringAttribute) SetValue(value string) {
+ sa.SetSelectValue(NoConfigAxis, "", &value)
+}
+
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
+ axis.validateConfig(config)
+ switch axis.configurationType {
+ case noConfig:
+ sa.Value = str
+ case arch, os, osArch, productVariables:
+ if sa.ConfigurableValues == nil {
+ sa.ConfigurableValues = make(configurableStrings)
+ }
+ sa.ConfigurableValues.setValueForAxis(axis, config, str)
+ default:
+ panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+ }
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
+ axis.validateConfig(config)
+ switch axis.configurationType {
+ case noConfig:
+ return sa.Value
+ case arch, os, osArch, productVariables:
+ if v, ok := sa.ConfigurableValues[axis][config]; ok {
+ return v
+ } else {
+ return nil
+ }
+ default:
+ panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+ }
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+ return SortedConfigurationAxes(sa.ConfigurableValues)
+}
+
+// Collapse reduces the configurable axes of the string attribute to a single axis.
+// This is necessary for final writing to bp2build, as a configurable string
+// attribute can only be comprised by a single select.
+func (sa *StringAttribute) Collapse() error {
+ axisTypes := sa.axisTypes()
+ _, containsOs := axisTypes[os]
+ _, containsArch := axisTypes[arch]
+ _, containsOsArch := axisTypes[osArch]
+ _, containsProductVariables := axisTypes[productVariables]
+ if containsProductVariables {
+ if containsOs || containsArch || containsOsArch {
+ return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes")
+ }
+ }
+ if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
+ // If a bool attribute has both os and arch configuration axes, the only
+ // way to successfully union their values is to increase the granularity
+ // of the configuration criteria to os_arch.
+ for osType, supportedArchs := range osToArchMap {
+ for _, supportedArch := range supportedArchs {
+ osArch := osArchString(osType, supportedArch)
+ if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
+ // Do nothing, as the arch_os is explicitly defined already.
+ } else {
+ archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
+ osVal := sa.SelectValue(OsConfigurationAxis, osType)
+ if osVal != nil && archVal != nil {
+ // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
+ // runs after os mutator.
+ sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
+ } else if osVal != nil && archVal == nil {
+ sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
+ } else if osVal == nil && archVal != nil {
+ sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
+ }
+ }
+ }
+ }
+ /// All os_arch values are now set. Clear os and arch axes.
+ delete(sa.ConfigurableValues, ArchConfigurationAxis)
+ delete(sa.ConfigurableValues, OsConfigurationAxis)
+ // Verify post-condition; this should never fail, provided no additional
+ // axes are introduced.
+ if len(sa.ConfigurableValues) > 1 {
+ panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
+ }
+ } else if containsProductVariables {
+ usedBaseValue := false
+ for a, configToProp := range sa.ConfigurableValues {
+ if a.configurationType == productVariables {
+ for c, p := range configToProp {
+ if p == nil {
+ sa.SetSelectValue(a, c, sa.Value)
+ usedBaseValue = true
+ }
+ }
+ }
+ }
+ if usedBaseValue {
+ sa.Value = nil
+ }
+ }
+ return nil
+}
+
+func (sa *StringAttribute) axisTypes() map[configurationType]bool {
+ types := map[configurationType]bool{}
+ for k := range sa.ConfigurableValues {
+ if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
+ types[k.configurationType] = true
+ }
+ }
+ return types
+}
+
// StringListAttribute corresponds to the string_list Bazel attribute type with
// support for additional metadata, like configurations.
type StringListAttribute struct {
@@ -955,6 +1256,16 @@
// The configured attribute label list Values. Optional
// a map of independent configurability axes
ConfigurableValues configurableStringLists
+
+ // If a property has struct tag "variant_prepend", this value should
+ // be set to True, so that when bp2build generates BUILD.bazel, variant
+ // properties(select ...) come before general properties.
+ Prepend bool
+}
+
+// IsEmpty returns true if the attribute has no values under any configuration.
+func (sla StringListAttribute) IsEmpty() bool {
+ return len(sla.Value) == 0 && !sla.HasConfigurableValues()
}
type configurableStringLists map[ConfigurationAxis]stringListSelectValues
@@ -1035,7 +1346,7 @@
switch axis.configurationType {
case noConfig:
sla.Value = list
- case arch, os, osArch, productVariables:
+ case arch, os, osArch, productVariables, osAndInApex:
if sla.ConfigurableValues == nil {
sla.ConfigurableValues = make(configurableStringLists)
}
@@ -1051,7 +1362,7 @@
switch axis.configurationType {
case noConfig:
return sla.Value
- case arch, os, osArch, productVariables:
+ case arch, os, osArch, productVariables, osAndInApex:
return sla.ConfigurableValues[axis][config]
default:
panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
@@ -1060,13 +1371,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
- for k := range sla.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(sla.ConfigurableValues)
}
// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
@@ -1100,6 +1405,9 @@
// TryVariableSubstitution, replace string substitution formatting within each string in slice with
// Starlark string.format compatible tag for productVariable.
func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
+ if len(slice) == 0 {
+ return slice, false
+ }
ret := make([]string, 0, len(slice))
changesMade := false
for _, s := range slice {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 7b76b74..cf03eb5 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -231,6 +231,7 @@
"all_include",
"arm_exclude",
"android_exclude",
+ "product_config_exclude",
},
[]string{"all_exclude"},
),
@@ -247,14 +248,14 @@
OsArchConfigurationAxis: labelListSelectValues{
"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
},
- ProductVariableConfigurationAxis("product_with_defaults"): labelListSelectValues{
+ ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): labelListSelectValues{
"a": makeLabelList([]string{}, []string{"not_in_value"}),
"b": makeLabelList([]string{"b_val"}, []string{}),
"c": makeLabelList([]string{"c_val"}, []string{}),
- ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}),
+ ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}),
},
- ProductVariableConfigurationAxis("product_only_with_excludes"): labelListSelectValues{
- "a": makeLabelList([]string{}, []string{"not_in_value"}),
+ ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{
+ "a": makeLabelList([]string{}, []string{"product_config_exclude"}),
},
},
}
@@ -281,12 +282,16 @@
"linux_x86": makeLabels("linux_x86_include"),
ConditionsDefaultConfigKey: nilLabels,
},
- ProductVariableConfigurationAxis("product_with_defaults"): {
+ ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): {
"a": nilLabels,
"b": makeLabels("b_val"),
"c": makeLabels("c_val"),
ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
},
+ ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): {
+ "a": nilLabels,
+ ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
+ },
}
for _, axis := range attr.SortedConfigurationAxes() {
if _, ok := expectedConfiguredIncludes[axis]; !ok {
@@ -310,6 +315,134 @@
}
}
+func TestLabelListAttributePartition(t *testing.T) {
+ testCases := []struct {
+ name string
+ input LabelListAttribute
+ predicated LabelListAttribute
+ unpredicated LabelListAttribute
+ predicate func(label Label) bool
+ }{
+ {
+ name: "move all to predicated partition",
+ input: MakeLabelListAttribute(makeLabelList(
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ )),
+ predicated: MakeLabelListAttribute(makeLabelList(
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ )),
+ unpredicated: LabelListAttribute{},
+ predicate: func(label Label) bool {
+ return true
+ },
+ },
+ {
+ name: "move all to unpredicated partition",
+ input: MakeLabelListAttribute(makeLabelList(
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ )),
+ predicated: LabelListAttribute{},
+ unpredicated: MakeLabelListAttribute(makeLabelList(
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ )),
+ predicate: func(label Label) bool {
+ return false
+ },
+ },
+ {
+ name: "partition includes and excludes",
+ input: MakeLabelListAttribute(makeLabelList(
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ )),
+ predicated: MakeLabelListAttribute(makeLabelList(
+ []string{"keep1", "keep2"},
+ []string{"keep1", "keep2"},
+ )),
+ unpredicated: MakeLabelListAttribute(makeLabelList(
+ []string{"throw1", "throw2"},
+ []string{"throw1", "throw2"},
+ )),
+ predicate: func(label Label) bool {
+ return strings.HasPrefix(label.Label, "keep")
+ },
+ },
+ {
+ name: "partition excludes only",
+ input: MakeLabelListAttribute(makeLabelList(
+ []string{},
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ )),
+ predicated: MakeLabelListAttribute(makeLabelList(
+ []string{},
+ []string{"keep1", "keep2"},
+ )),
+ unpredicated: MakeLabelListAttribute(makeLabelList(
+ []string{},
+ []string{"throw1", "throw2"},
+ )),
+ predicate: func(label Label) bool {
+ return strings.HasPrefix(label.Label, "keep")
+ },
+ },
+ {
+ name: "partition includes only",
+ input: MakeLabelListAttribute(makeLabelList(
+ []string{"keep1", "throw1", "keep2", "throw2"},
+ []string{},
+ )),
+ predicated: MakeLabelListAttribute(makeLabelList(
+ []string{"keep1", "keep2"},
+ []string{},
+ )),
+ unpredicated: MakeLabelListAttribute(makeLabelList(
+ []string{"throw1", "throw2"},
+ []string{},
+ )),
+ predicate: func(label Label) bool {
+ return strings.HasPrefix(label.Label, "keep")
+ },
+ },
+ {
+ name: "empty partition",
+ input: MakeLabelListAttribute(makeLabelList([]string{}, []string{})),
+ predicated: LabelListAttribute{},
+ unpredicated: LabelListAttribute{},
+ predicate: func(label Label) bool {
+ return true
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ predicated, unpredicated := tc.input.Partition(tc.predicate)
+ if !predicated.Value.Equals(tc.predicated.Value) {
+ t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated)
+ }
+ for axis, configs := range predicated.ConfigurableValues {
+ tcConfigs, ok := tc.predicated.ConfigurableValues[axis]
+ if !ok || !reflect.DeepEqual(configs, tcConfigs) {
+ t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated)
+ }
+ }
+ if !unpredicated.Value.Equals(tc.unpredicated.Value) {
+ t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated)
+ }
+ for axis, configs := range unpredicated.ConfigurableValues {
+ tcConfigs, ok := tc.unpredicated.ConfigurableValues[axis]
+ if !ok || !reflect.DeepEqual(configs, tcConfigs) {
+ t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated)
+ }
+ }
+ })
+ }
+}
+
// labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of
// typ
func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {
@@ -546,7 +679,7 @@
OsArchConfigurationAxis: stringListSelectValues{
"linux_x86": {"linux_x86_include"},
},
- ProductVariableConfigurationAxis("a"): stringListSelectValues{
+ ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
"a": []string{"not_in_value"},
},
},
@@ -571,7 +704,7 @@
"linux": []string{"linux_include"},
},
OsArchConfigurationAxis: stringListSelectValues{},
- ProductVariableConfigurationAxis("a"): stringListSelectValues{
+ ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
"a": []string{"not_in_value"},
},
}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index e0ce194..b6635c4 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -8,6 +8,7 @@
srcs: [
"androidbp_to_build_templates.go",
"bp2build.go",
+ "bp2build_product_config.go",
"build_conversion.go",
"bzl_conversion.go",
"configurability.go",
@@ -15,24 +16,27 @@
"conversion.go",
"metrics.go",
"symlink_forest.go",
+ "testing.go",
],
deps: [
"soong-android",
"soong-android-allowlists",
"soong-android-soongconfig",
- "soong-shared",
"soong-apex",
"soong-bazel",
"soong-cc",
"soong-cc-config",
"soong-etc",
"soong-genrule",
+ "soong-linkerconfig",
"soong-python",
"soong-sh",
+ "soong-shared",
"soong-starlark-format",
"soong-ui-metrics",
],
testSrcs: [
+ "aar_conversion_test.go",
"android_app_certificate_conversion_test.go",
"android_app_conversion_test.go",
"apex_conversion_test.go",
@@ -48,22 +52,33 @@
"cc_prebuilt_library_conversion_test.go",
"cc_prebuilt_library_shared_test.go",
"cc_prebuilt_library_static_test.go",
+ "cc_prebuilt_object_conversion_test.go",
+ "cc_test_conversion_test.go",
+ "cc_yasm_conversion_test.go",
"conversion_test.go",
+ "droidstubs_conversion_test.go",
"filegroup_conversion_test.go",
"genrule_conversion_test.go",
+ "gensrcs_conversion_test.go",
"java_binary_host_conversion_test.go",
+ "java_host_for_device_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",
+ "license_conversion_test.go",
+ "license_kind_conversion_test.go",
+ "linker_config_conversion_test.go",
+ "ndk_headers_conversion_test.go",
+ "package_conversion_test.go",
"performance_test.go",
"prebuilt_etc_conversion_test.go",
"python_binary_conversion_test.go",
"python_library_conversion_test.go",
+ "python_test_conversion_test.go",
"sh_conversion_test.go",
"soong_config_module_type_conversion_test.go",
- "testing.go",
],
pluginFor: [
"soong_build",
diff --git a/bp2build/aar_conversion_test.go b/bp2build/aar_conversion_test.go
new file mode 100644
index 0000000..09d9dc1
--- /dev/null
+++ b/bp2build/aar_conversion_test.go
@@ -0,0 +1,212 @@
+// 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 (
+ "android/soong/android"
+ "android/soong/java"
+ "fmt"
+
+ "testing"
+)
+
+func TestConvertAndroidLibrary(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+ Description: "Android Library - simple example",
+ ModuleTypeUnderTest: "android_library",
+ ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+ Filesystem: map[string]string{
+ "lib.java": "",
+ "arm.java": "",
+ "x86.java": "",
+ "res/res.png": "",
+ "manifest/AndroidManifest.xml": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+ name: "TestLib",
+ srcs: ["lib.java"],
+ arch: {
+ arm: {
+ srcs: ["arm.java"],
+ },
+ x86: {
+ srcs: ["x86.java"],
+ }
+ },
+ manifest: "manifest/AndroidManifest.xml",
+ static_libs: ["static_lib_dep"],
+ java_version: "7",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget(
+ "android_library",
+ "TestLib",
+ AttrNameToString{
+ "srcs": `["lib.java"] + select({
+ "//build/bazel/platforms/arch:arm": ["arm.java"],
+ "//build/bazel/platforms/arch:x86": ["x86.java"],
+ "//conditions:default": [],
+ })`,
+ "manifest": `"manifest/AndroidManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ "deps": `[":static_lib_dep"]`,
+ "exports": `[":static_lib_dep"]`,
+ "java_version": `"7"`,
+ }),
+ MakeNeverlinkDuplicateTargetWithAttrs(
+ "android_library",
+ "TestLib",
+ AttrNameToString{"java_version": `"7"`}),
+ }})
+}
+
+func TestConvertAndroidLibraryWithNoSources(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+ Description: "Android Library - modules with deps must have sources",
+ ModuleTypeUnderTest: "android_library",
+ ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+ Filesystem: map[string]string{
+ "res/res.png": "",
+ "AndroidManifest.xml": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("android_library", "lib_dep") + `
+android_library {
+ name: "TestLib",
+ srcs: [],
+ manifest: "AndroidManifest.xml",
+ libs: ["lib_dep"],
+}
+`,
+ ExpectedErr: fmt.Errorf("Module has direct dependencies but no sources. Bazel will not allow this."),
+ ExpectedBazelTargets: []string{},
+ })
+}
+
+func TestConvertAndroidLibraryImport(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(
+ t,
+ func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_library", java.AndroidLibraryFactory)
+ },
+ Bp2buildTestCase{
+ Description: "Android Library Import",
+ ModuleTypeUnderTest: "android_library_import",
+ ModuleTypeUnderTestFactory: java.AARImportFactory,
+ Filesystem: map[string]string{
+ "import.aar": "",
+ },
+ // Bazel's aar_import can only export *_import targets, so we expect
+ // only "static_import_dep" in exports, but both "static_lib_dep" and
+ // "static_import_dep" in deps
+ Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") +
+ simpleModuleDoNotConvertBp2build("android_library_import", "static_import_dep") + `
+android_library_import {
+ name: "TestImport",
+ aars: ["import.aar"],
+ static_libs: ["static_lib_dep", "static_import_dep"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget(
+ "aar_import",
+ "TestImport",
+ AttrNameToString{
+ "aar": `"import.aar"`,
+ "deps": `[
+ ":static_lib_dep",
+ ":static_import_dep",
+ ]`,
+ "exports": `[":static_import_dep"]`,
+ },
+ ),
+ MakeNeverlinkDuplicateTarget("android_library", "TestImport"),
+ },
+ },
+ )
+}
+
+func TestConvertAndroidLibraryKotlin(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+ Description: "Android Library with .kt srcs and common_srcs attribute",
+ ModuleTypeUnderTest: "android_library",
+ ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+ Filesystem: map[string]string{
+ "AndroidManifest.xml": "",
+ },
+ Blueprint: `
+android_library {
+ name: "TestLib",
+ srcs: ["a.java", "b.kt"],
+ common_srcs: ["c.kt"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget(
+ "android_library",
+ "TestLib",
+ AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "common_srcs": `["c.kt"]`,
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ }),
+ MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+ }})
+}
+
+func TestConvertAndroidLibraryKotlinCflags(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+ Description: "Android Library with .kt srcs and kotlincflags ",
+ ModuleTypeUnderTest: "android_library",
+ ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+ Filesystem: map[string]string{
+ "AndroidManifest.xml": "",
+ },
+ Blueprint: `
+android_library {
+ name: "TestLib",
+ srcs: ["a.java", "b.kt"],
+ kotlincflags: ["-flag1", "-flag2"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget(
+ "android_library",
+ "TestLib",
+ AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "kotlincflags": `[
+ "-flag1",
+ "-flag2",
+ ]`,
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ }),
+ MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+ }})
+}
diff --git a/bp2build/android_app_certificate_conversion_test.go b/bp2build/android_app_certificate_conversion_test.go
index 035a352..0104513 100644
--- a/bp2build/android_app_certificate_conversion_test.go
+++ b/bp2build/android_app_certificate_conversion_test.go
@@ -21,28 +21,28 @@
"testing"
)
-func runAndroidAppCertificateTestCase(t *testing.T, tc bp2buildTestCase) {
+func runAndroidAppCertificateTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, registerAndroidAppCertificateModuleTypes, tc)
+ RunBp2BuildTestCase(t, registerAndroidAppCertificateModuleTypes, tc)
}
func registerAndroidAppCertificateModuleTypes(ctx android.RegistrationContext) {
}
func TestAndroidAppCertificateSimple(t *testing.T) {
- runAndroidAppCertificateTestCase(t, bp2buildTestCase{
- description: "Android app certificate - simple example",
- moduleTypeUnderTest: "android_app_certificate",
- moduleTypeUnderTestFactory: java.AndroidAppCertificateFactory,
- filesystem: map[string]string{},
- blueprint: `
+ runAndroidAppCertificateTestCase(t, Bp2buildTestCase{
+ Description: "Android app certificate - simple example",
+ ModuleTypeUnderTest: "android_app_certificate",
+ ModuleTypeUnderTestFactory: java.AndroidAppCertificateFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
android_app_certificate {
name: "com.android.apogee.cert",
certificate: "chamber_of_secrets_dir",
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("android_app_certificate", "com.android.apogee.cert", attrNameToString{
+ ExpectedBazelTargets: []string{
+ 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 3824586..928a1f2 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -21,52 +21,55 @@
"testing"
)
-func runAndroidAppTestCase(t *testing.T, tc bp2buildTestCase) {
+func runAndroidAppTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, registerAndroidAppModuleTypes, tc)
+ RunBp2BuildTestCase(t, registerAndroidAppModuleTypes, tc)
}
func registerAndroidAppModuleTypes(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ ctx.RegisterModuleType("java_library", java.LibraryFactory)
}
func TestMinimalAndroidApp(t *testing.T) {
- runAndroidAppTestCase(t, bp2buildTestCase{
- description: "Android app - simple example",
- moduleTypeUnderTest: "android_app",
- moduleTypeUnderTestFactory: java.AndroidAppFactory,
- filesystem: map[string]string{
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app - simple example",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
"app.java": "",
"res/res.png": "",
"AndroidManifest.xml": "",
},
- blueprint: `
+ Blueprint: `
android_app {
name: "TestApp",
srcs: ["app.java"],
sdk_version: "current",
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("android_binary", "TestApp", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
"srcs": `["app.java"]`,
"manifest": `"AndroidManifest.xml"`,
"resource_files": `["res/res.png"]`,
+ "sdk_version": `"current"`,
}),
}})
}
func TestAndroidAppAllSupportedFields(t *testing.T) {
- runAndroidAppTestCase(t, bp2buildTestCase{
- description: "Android app - all supported fields",
- moduleTypeUnderTest: "android_app",
- moduleTypeUnderTestFactory: java.AndroidAppFactory,
- filesystem: map[string]string{
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app - all supported fields",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
"app.java": "",
"resa/res.png": "",
"resb/res.png": "",
"manifest/AndroidManifest.xml": "",
},
- blueprint: simpleModuleDoNotConvertBp2build("android_app", "static_lib_dep") + `
+ Blueprint: simpleModuleDoNotConvertBp2build("android_app", "static_lib_dep") + `
android_app {
name: "TestApp",
srcs: ["app.java"],
@@ -74,35 +77,40 @@
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",
+ certificate: "foocert",
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("android_binary", "TestApp", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
"srcs": `["app.java"]`,
"manifest": `"manifest/AndroidManifest.xml"`,
"resource_files": `[
"resa/res.png",
"resb/res.png",
]`,
- "custom_package": `"com.google"`,
- "deps": `[":static_lib_dep"]`,
+ "custom_package": `"com.google"`,
+ "deps": `[":static_lib_dep"]`,
+ "java_version": `"7"`,
+ "sdk_version": `"current"`,
+ "certificate_name": `"foocert"`,
}),
}})
}
func TestAndroidAppArchVariantSrcs(t *testing.T) {
- runAndroidAppTestCase(t, bp2buildTestCase{
- description: "Android app - arch variant srcs",
- moduleTypeUnderTest: "android_app",
- moduleTypeUnderTestFactory: java.AndroidAppFactory,
- filesystem: map[string]string{
+ 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: `
+ Blueprint: `
android_app {
name: "TestApp",
sdk_version: "current",
@@ -116,8 +124,8 @@
}
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("android_binary", "TestApp", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
"srcs": `select({
"//build/bazel/platforms/arch:arm": ["arm.java"],
"//build/bazel/platforms/arch:x86": ["x86.java"],
@@ -125,6 +133,266 @@
})`,
"manifest": `"AndroidManifest.xml"`,
"resource_files": `["res/res.png"]`,
+ "sdk_version": `"current"`,
+ }),
+ }})
+}
+
+func TestAndroidAppCertIsModule(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app - cert is module",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{},
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "TestApp",
+ certificate: ":foocert",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
+ "certificate": `":foocert"`,
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ }),
+ }})
+}
+
+func TestAndroidAppCertIsSrcFile(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app - cert is src file",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
+ "foocert": "",
+ },
+ Blueprint: `
+android_app {
+ name: "TestApp",
+ certificate: "foocert",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
+ "certificate": `"foocert"`,
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ }),
+ }})
+}
+
+func TestAndroidAppCertIsNotSrcOrModule(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app - cert is not src or module",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
+ // deliberate empty
+ },
+ Blueprint: `
+android_app {
+ name: "TestApp",
+ certificate: "foocert",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
+ "certificate_name": `"foocert"`,
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ }),
+ }})
+}
+
+func TestAndroidAppLibs(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with libs",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{},
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ libs: ["barLib"]
+}
+java_library{
+ name: "barLib",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ "deps": `[":barLib-neverlink"]`,
+ }),
+ }})
+}
+
+func TestAndroidAppKotlinSrcs(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with kotlin sources and common_srcs",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
+ "res/res.png": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ srcs: ["a.java", "b.kt"],
+ certificate: ":foocert",
+ manifest: "fooManifest.xml",
+ libs: ["barLib"]
+}
+java_library{
+ name: "barLib",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+ MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "manifest": `"fooManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ "deps": `[":barLib-neverlink"]`,
+ }),
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "deps": `[":foo_kt"]`,
+ "certificate": `":foocert"`,
+ "manifest": `"fooManifest.xml"`,
+ }),
+ }})
+}
+
+func TestAndroidAppCommonSrcs(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with common_srcs",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
+ "res/res.png": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ common_srcs: ["b.kt"],
+ certificate: "foocert",
+ manifest: "fooManifest.xml",
+ libs: ["barLib"],
+}
+java_library{
+ name: "barLib",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+ MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "common_srcs": `["b.kt"]`,
+ "manifest": `"fooManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ "deps": `[":barLib-neverlink"]`,
+ }),
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "deps": `[":foo_kt"]`,
+ "certificate_name": `"foocert"`,
+ "manifest": `"fooManifest.xml"`,
+ }),
+ }})
+}
+
+func TestAndroidAppKotlinCflags(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with kotlincflags",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
+ "res/res.png": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ srcs: ["a.java", "b.kt"],
+ certificate: ":foocert",
+ manifest: "fooManifest.xml",
+ kotlincflags: ["-flag1", "-flag2"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "manifest": `"fooManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ "kotlincflags": `[
+ "-flag1",
+ "-flag2",
+ ]`,
+ }),
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "deps": `[":foo_kt"]`,
+ "certificate": `":foocert"`,
+ "manifest": `"fooManifest.xml"`,
+ }),
+ }})
+}
+
+func TestAndroidAppMinSdkProvided(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with value for min_sdk_version",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{},
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ sdk_version: "current",
+ min_sdk_version: "24",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ "manifest_values": `{
+ "minSdkVersion": "24",
+ }`,
+ "sdk_version": `"current"`,
+ }),
+ }})
+}
+
+func TestAndroidAppMinSdkDefaultToSdkVersion(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with value for sdk_version",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{},
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ sdk_version: "30",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ "manifest_values": `{
+ "minSdkVersion": "30",
+ }`,
+ "sdk_version": `"30"`,
}),
}})
}
diff --git a/bp2build/androidbp_to_build_templates.go b/bp2build/androidbp_to_build_templates.go
index 5fed4fa..9b21c32 100644
--- a/bp2build/androidbp_to_build_templates.go
+++ b/bp2build/androidbp_to_build_templates.go
@@ -23,7 +23,7 @@
// A macro call in the BUILD file representing a Soong module, with space
// for expanding more attributes.
- soongModuleTarget = `soong_module(
+ soongModuleTargetTemplate = `soong_module(
name = "%s",
soong_module_name = "%s",
soong_module_type = "%s",
@@ -31,10 +31,13 @@
soong_module_deps = %s,
%s)`
- bazelTarget = `%s(
+ ruleTargetTemplate = `%s(
name = "%s",
%s)`
+ unnamedRuleTargetTemplate = `%s(
+%s)`
+
// A simple provider to mark and differentiate Soong module rule shims from
// regular Bazel rules. Every Soong module rule shim returns a
// SoongModuleInfo provider, and can only depend on rules returning
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index 9057189..1cc3f22 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -18,15 +18,17 @@
"android/soong/android"
"android/soong/apex"
"android/soong/cc"
+ "android/soong/etc"
"android/soong/java"
"android/soong/sh"
+ "fmt"
"testing"
)
-func runApexTestCase(t *testing.T, tc bp2buildTestCase) {
+func runApexTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, registerApexModuleTypes, tc)
+ RunBp2BuildTestCase(t, registerApexModuleTypes, tc)
}
func registerApexModuleTypes(ctx android.RegistrationContext) {
@@ -39,15 +41,40 @@
ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory)
+ ctx.RegisterModuleType("cc_test", cc.TestFactory)
+}
+
+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("apex_test", apex.TestApexBundleFactory)
+ ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
+ ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ ctx.RegisterModuleType("apex", apex.BundleFactory)
+ ctx.RegisterModuleType("apex_defaults", apex.DefaultsFactory)
+ ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory)
+ ctx.RegisterModuleType("soong_config_module_type", android.SoongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", android.SoongConfigStringVariableDummyFactory)
}
func TestApexBundleSimple(t *testing.T) {
- runApexTestCase(t, bp2buildTestCase{
- description: "apex - example with all props",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: `
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - example with all props, file_context is a module in same Android.bp",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
apex_key {
name: "com.android.apogee.key",
public_key: "com.android.apogee.avbpubkey",
@@ -71,22 +98,20 @@
bazel_module: { bp2build_available: false },
}
-// TODO(b/194878861): Add bp2build support for prebuilt_etc
-cc_library {
- name: "pretend_prebuilt_1",
+prebuilt_etc {
+ name: "prebuilt_1",
bazel_module: { bp2build_available: false },
}
-// TODO(b/194878861): Add bp2build support for prebuilt_etc
-cc_library {
- name: "pretend_prebuilt_2",
+prebuilt_etc {
+ name: "prebuilt_2",
bazel_module: { bp2build_available: false },
}
filegroup {
name: "com.android.apogee-file_contexts",
srcs: [
- "com.android.apogee-file_contexts",
+ "com.android.apogee-file_contexts",
],
bazel_module: { bp2build_available: false },
}
@@ -98,10 +123,10 @@
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",
+ certificate: ":com.android.apogee.certificate",
updatable: false,
installable: false,
compressible: false,
@@ -114,13 +139,15 @@
"sh_binary_2",
],
prebuilts: [
- "pretend_prebuilt_1",
- "pretend_prebuilt_2",
+ "prebuilt_1",
+ "prebuilt_2",
],
+ package_name: "com.android.apogee.test.package",
+ logging_parent: "logging.parent",
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
"android_manifest": `"ApogeeAndroidManifest.xml"`,
"binaries": `[
":cc_binary_1",
@@ -132,10 +159,17 @@
"key": `":com.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_32": `select({
+ "//build/bazel/platforms/arch:arm": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_2",
+ ],
+ "//build/bazel/platforms/arch:x86": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_2",
+ ],
+ "//conditions:default": [],
+ })`,
"native_shared_libs_64": `select({
"//build/bazel/platforms/arch:arm64": [
":native_shared_lib_1",
@@ -148,174 +182,409 @@
"//conditions:default": [],
})`,
"prebuilts": `[
- ":pretend_prebuilt_1",
- ":pretend_prebuilt_2",
+ ":prebuilt_1",
+ ":prebuilt_2",
]`,
- "updatable": "False",
- "compressible": "False",
+ "updatable": "False",
+ "compressible": "False",
+ "package_name": `"com.android.apogee.test.package"`,
+ "logging_parent": `"logging.parent"`,
+ }),
+ }})
+}
+
+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"`,
}),
}})
}
func TestApexBundleCompileMultilibBoth(t *testing.T) {
- runApexTestCase(t, bp2buildTestCase{
- description: "apex - example with compile_multilib=both",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: createMultilibBlueprint("both"),
- expectedBazelTargets: []string{
- makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - example with compile_multilib=both",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ 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(`compile_multilib: "both",`),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
"native_shared_libs_32": `[
- ":native_shared_lib_1",
- ":native_shared_lib_3",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib32",
] + select({
- "//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
- "//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
+ "//build/bazel/platforms/arch:arm": [":native_shared_lib_for_first"],
+ "//build/bazel/platforms/arch:x86": [":native_shared_lib_for_first"],
"//conditions:default": [],
})`,
"native_shared_libs_64": `select({
"//build/bazel/platforms/arch:arm64": [
- ":native_shared_lib_1",
- ":native_shared_lib_4",
- ":native_shared_lib_2",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib64",
+ ":native_shared_lib_for_first",
],
"//build/bazel/platforms/arch:x86_64": [
- ":native_shared_lib_1",
- ":native_shared_lib_4",
- ":native_shared_lib_2",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib64",
+ ":native_shared_lib_for_first",
],
"//conditions:default": [],
})`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
}),
}})
}
-func TestApexBundleCompileMultilibFirst(t *testing.T) {
- runApexTestCase(t, bp2buildTestCase{
- description: "apex - example with compile_multilib=first",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: createMultilibBlueprint("first"),
- expectedBazelTargets: []string{
- makeBazelTarget("apex", "com.android.apogee", attrNameToString{
- "native_shared_libs_32": `select({
+func TestApexBundleCompileMultilibFirstAndDefaultValue(t *testing.T) {
+ expectedBazelTargets := []string{
+ MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+ "native_shared_libs_32": `select({
"//build/bazel/platforms/arch:arm": [
- ":native_shared_lib_1",
- ":native_shared_lib_3",
- ":native_shared_lib_2",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib32",
+ ":native_shared_lib_for_first",
],
"//build/bazel/platforms/arch:x86": [
- ":native_shared_lib_1",
- ":native_shared_lib_3",
- ":native_shared_lib_2",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib32",
+ ":native_shared_lib_for_first",
],
"//conditions:default": [],
})`,
- "native_shared_libs_64": `select({
+ "native_shared_libs_64": `select({
"//build/bazel/platforms/arch:arm64": [
- ":native_shared_lib_1",
- ":native_shared_lib_4",
- ":native_shared_lib_2",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib64",
+ ":native_shared_lib_for_first",
],
"//build/bazel/platforms/arch:x86_64": [
- ":native_shared_lib_1",
- ":native_shared_lib_4",
- ":native_shared_lib_2",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib64",
+ ":native_shared_lib_for_first",
],
"//conditions:default": [],
})`,
- }),
- }})
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ }),
+ }
+
+ // "first" is the default value of compile_multilib prop so `compile_multilib_: "first"` and unset compile_multilib
+ // should result to the same bp2build output
+ compileMultiLibPropValues := []string{`compile_multilib: "first",`, ""}
+ for _, compileMultiLibProp := range compileMultiLibPropValues {
+ descriptionSuffix := compileMultiLibProp
+ if descriptionSuffix == "" {
+ descriptionSuffix = "compile_multilib unset"
+ }
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - example with " + compileMultiLibProp,
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ 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(compileMultiLibProp),
+ ExpectedBazelTargets: expectedBazelTargets,
+ })
+ }
}
func TestApexBundleCompileMultilib32(t *testing.T) {
- runApexTestCase(t, bp2buildTestCase{
- description: "apex - example with compile_multilib=32",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: createMultilibBlueprint("32"),
- expectedBazelTargets: []string{
- makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - example with compile_multilib=32",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ 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(`compile_multilib: "32",`),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
"native_shared_libs_32": `[
- ":native_shared_lib_1",
- ":native_shared_lib_3",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib32",
] + select({
- "//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
- "//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
+ "//build/bazel/platforms/arch:arm": [":native_shared_lib_for_first"],
+ "//build/bazel/platforms/arch:x86": [":native_shared_lib_for_first"],
"//conditions:default": [],
})`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
}),
}})
}
func TestApexBundleCompileMultilib64(t *testing.T) {
- runApexTestCase(t, bp2buildTestCase{
- description: "apex - example with compile_multilib=64",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: createMultilibBlueprint("64"),
- expectedBazelTargets: []string{
- makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - example with compile_multilib=64",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ 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(`compile_multilib: "64",`),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
"native_shared_libs_64": `select({
"//build/bazel/platforms/arch:arm64": [
- ":native_shared_lib_1",
- ":native_shared_lib_4",
- ":native_shared_lib_2",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib64",
+ ":native_shared_lib_for_first",
],
"//build/bazel/platforms/arch:x86_64": [
- ":native_shared_lib_1",
- ":native_shared_lib_4",
- ":native_shared_lib_2",
+ ":unnested_native_shared_lib",
+ ":native_shared_lib_for_both",
+ ":native_shared_lib_for_lib64",
+ ":native_shared_lib_for_first",
],
"//conditions:default": [],
})`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
}),
}})
}
+func createMultilibBlueprint(compile_multilib string) string {
+ return fmt.Sprintf(`
+cc_library {
+ name: "native_shared_lib_for_both",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "native_shared_lib_for_first",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "native_shared_lib_for_lib32",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "native_shared_lib_for_lib64",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "unnested_native_shared_lib",
+ bazel_module: { bp2build_available: false },
+}
+
+apex {
+ name: "com.android.apogee",
+ %s
+ native_shared_libs: ["unnested_native_shared_lib"],
+ multilib: {
+ both: {
+ native_shared_libs: [
+ "native_shared_lib_for_both",
+ ],
+ },
+ first: {
+ native_shared_libs: [
+ "native_shared_lib_for_first",
+ ],
+ },
+ lib32: {
+ native_shared_libs: [
+ "native_shared_lib_for_lib32",
+ ],
+ },
+ lib64: {
+ native_shared_libs: [
+ "native_shared_lib_for_lib64",
+ ],
+ },
+ },
+}`, compile_multilib)
+}
+
func TestApexBundleDefaultPropertyValues(t *testing.T) {
- runApexTestCase(t, bp2buildTestCase{
- description: "apex - default property values",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: `
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - default property values",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ 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",
}
`,
- expectedBazelTargets: []string{makeBazelTarget("apex", "com.android.apogee", attrNameToString{
- "manifest": `"apogee_manifest.json"`,
+ ExpectedBazelTargets: []string{MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+ "manifest": `"apogee_manifest.json"`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
}),
}})
}
func TestApexBundleHasBazelModuleProps(t *testing.T) {
- runApexTestCase(t, bp2buildTestCase{
- description: "apex - has bazel module props",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- filesystem: map[string]string{},
- blueprint: `
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - has bazel module props",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ 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",
manifest: "manifest.json",
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{makeBazelTarget("apex", "apogee", attrNameToString{
- "manifest": `"manifest.json"`,
+ ExpectedBazelTargets: []string{MakeBazelTarget("apex", "apogee", AttrNameToString{
+ "manifest": `"manifest.json"`,
+ "file_contexts": `"//system/sepolicy/apex:apogee-file_contexts"`,
}),
}})
}
-func createMultilibBlueprint(compile_multilib string) string {
- return `
+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 },
@@ -326,40 +595,996 @@
bazel_module: { bp2build_available: false },
}
-cc_library {
- name: "native_shared_lib_3",
+prebuilt_etc {
+ name: "prebuilt_1",
+ bazel_module: { bp2build_available: false },
+}
+
+prebuilt_etc {
+ name: "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: [
+ "prebuilt_1",
+ "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"`,
+ "base_apex_name": `"com.android.apogee"`,
+ "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": `select({
+ "//build/bazel/platforms/arch:arm": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_2",
+ ],
+ "//build/bazel/platforms/arch:x86": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_2",
+ ],
+ "//conditions:default": [],
+ })`,
+ "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 TestOverrideApexTest(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_4",
+ name: "native_shared_lib_1",
+ bazel_module: { bp2build_available: false },
+}
+
+prebuilt_etc {
+ name: "prebuilt_1",
+ 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_test {
+ 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",
+ ],
+ binaries: [
+ "cc_binary_1",
+ "sh_binary_2",
+ ],
+ prebuilts: [
+ "prebuilt_1",
+ ],
+ 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"`,
+ "base_apex_name": `"com.android.apogee"`,
+ "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": `select({
+ "//build/bazel/platforms/arch:arm": [":native_shared_lib_1"],
+ "//build/bazel/platforms/arch:x86": [":native_shared_lib_1"],
+ "//conditions:default": [],
+ })`,
+ "native_shared_libs_64": `select({
+ "//build/bazel/platforms/arch:arm64": [":native_shared_lib_1"],
+ "//build/bazel/platforms/arch:x86_64": [":native_shared_lib_1"],
+ "//conditions:default": [],
+ })`,
+ "testonly": "True",
+ "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{
+ "base_apex_name": `"com.android.apogee"`,
+ "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{
+ "base_apex_name": `"com.android.apogee"`,
+ "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{
+ "base_apex_name": `"com.android.apogee"`,
+ "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{
+ "base_apex_name": `"com.android.apogee"`,
+ "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{
+ "base_apex_name": `"com.android.apogee"`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ "package_name": `"com.google.android.apogee"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_NoPrebuiltsOverride(t *testing.T) {
+ runOverrideApexTestCase(t, Bp2buildTestCase{
+ Description: "override_apex - no override",
+ 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: `
+prebuilt_etc {
+ name: "prebuilt_file",
bazel_module: { bp2build_available: false },
}
apex {
name: "com.android.apogee",
- compile_multilib: "` + compile_multilib + `",
- multilib: {
- both: {
- native_shared_libs: [
- "native_shared_lib_1",
- ],
+ bazel_module: { bp2build_available: false },
+ prebuilts: ["prebuilt_file"]
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+ "base_apex_name": `"com.android.apogee"`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ "prebuilts": `[":prebuilt_file"]`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_PrebuiltsOverride(t *testing.T) {
+ runOverrideApexTestCase(t, Bp2buildTestCase{
+ Description: "override_apex - ooverride",
+ 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 },
+}`,
},
- first: {
- native_shared_libs: [
- "native_shared_lib_2",
- ],
+ Blueprint: `
+prebuilt_etc {
+ name: "prebuilt_file",
+ bazel_module: { bp2build_available: false },
+}
+
+prebuilt_etc {
+ name: "prebuilt_file2",
+ bazel_module: { bp2build_available: false },
+}
+
+apex {
+ name: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+ prebuilts: ["prebuilt_file"]
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+ prebuilts: ["prebuilt_file2"]
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+ "base_apex_name": `"com.android.apogee"`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ "prebuilts": `[":prebuilt_file2"]`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_PrebuiltsOverrideEmptyList(t *testing.T) {
+ runOverrideApexTestCase(t, Bp2buildTestCase{
+ Description: "override_apex - override with empty list",
+ 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 },
+}`,
},
- lib32: {
- native_shared_libs: [
- "native_shared_lib_3",
- ],
+ Blueprint: `
+prebuilt_etc {
+ name: "prebuilt_file",
+ bazel_module: { bp2build_available: false },
+}
+
+apex {
+ name: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+ prebuilts: ["prebuilt_file"]
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+ prebuilts: [],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+ "base_apex_name": `"com.android.apogee"`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ "prebuilts": `[]`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_NoLoggingParentOverride(t *testing.T) {
+ runOverrideApexTestCase(t, Bp2buildTestCase{
+ Description: "override_apex - logging_parent - no override",
+ 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 },
+}`,
},
- lib64: {
- native_shared_libs: [
- "native_shared_lib_4",
- ],
+ Blueprint: `
+apex {
+ name: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+ logging_parent: "foo.bar.baz",
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+ "base_apex_name": `"com.android.apogee"`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ "logging_parent": `"foo.bar.baz"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_LoggingParentOverride(t *testing.T) {
+ runOverrideApexTestCase(t, Bp2buildTestCase{
+ Description: "override_apex - logging_parent - override",
+ 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 },
+ logging_parent: "foo.bar.baz",
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+ logging_parent: "foo.bar.baz.override",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+ "base_apex_name": `"com.android.apogee"`,
+ "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ "logging_parent": `"foo.bar.baz.override"`,
+ }),
+ }})
+}
+
+func TestBp2BuildOverrideApex_CertificateNil(t *testing.T) {
+ runOverrideApexTestCase(t, Bp2buildTestCase{
+ Description: "override_apex - don't set default certificate",
+ ModuleTypeUnderTest: "override_apex",
+ ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
+android_app_certificate {
+ name: "com.android.apogee.certificate",
+ certificate: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [
+ "com.android.apogee-file_contexts",
+ ],
+ bazel_module: { bp2build_available: false },
+}
+
+apex {
+ name: "com.android.apogee",
+ manifest: "apogee_manifest.json",
+ file_contexts: ":com.android.apogee-file_contexts",
+ certificate: ":com.android.apogee.certificate",
+ bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+ // certificate is deliberately omitted, and not converted to bazel,
+ // because the overridden apex shouldn't be using the base apex's cert.
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+ "base_apex_name": `"com.android.apogee"`,
+ "file_contexts": `":com.android.apogee-file_contexts"`,
+ "manifest": `"apogee_manifest.json"`,
+ }),
+ }})
+}
+
+func TestApexCertificateIsModule(t *testing.T) {
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - certificate is module",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
+android_app_certificate {
+ name: "com.android.apogee.certificate",
+ certificate: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+}
+
+apex {
+ name: "com.android.apogee",
+ manifest: "apogee_manifest.json",
+ file_contexts: ":com.android.apogee-file_contexts",
+ certificate: ":com.android.apogee.certificate",
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee-file_contexts"),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+ "certificate": `":com.android.apogee.certificate"`,
+ "file_contexts": `":com.android.apogee-file_contexts"`,
+ "manifest": `"apogee_manifest.json"`,
+ }),
+ }})
+}
+
+func TestApexWithStubLib(t *testing.T) {
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - static variant of stub lib should not have apex_available tag",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
+cc_library{
+ name: "foo",
+ stubs: { symbol_file: "foo.map.txt", versions: ["28", "29", "current"] },
+ apex_available: ["myapex"],
+}
+
+cc_binary{
+ name: "bar",
+ static_libs: ["foo"],
+ apex_available: ["myapex"],
+}
+
+apex {
+ name: "myapex",
+ manifest: "myapex_manifest.json",
+ file_contexts: ":myapex-file_contexts",
+ binaries: ["bar"],
+ native_shared_libs: ["foo"],
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "myapex-file_contexts"),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_binary", "bar", AttrNameToString{
+ "local_includes": `["."]`,
+ "deps": `[":foo_bp2build_cc_library_static"]`,
+ "tags": `["apex_available=myapex"]`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "stubs_symbol_file": `"foo.map.txt"`,
+ "tags": `["apex_available=myapex"]`,
+ }),
+ MakeBazelTarget("cc_stub_suite", "foo_stub_libs", AttrNameToString{
+ "soname": `"foo.so"`,
+ "source_library_label": `"//:foo"`,
+ "symbol_file": `"foo.map.txt"`,
+ "versions": `[
+ "28",
+ "29",
+ "current",
+ ]`,
+ }),
+ MakeBazelTarget("apex", "myapex", AttrNameToString{
+ "file_contexts": `":myapex-file_contexts"`,
+ "manifest": `"myapex_manifest.json"`,
+ "binaries": `[":bar"]`,
+ "native_shared_libs_32": `select({
+ "//build/bazel/platforms/arch:arm": [":foo"],
+ "//build/bazel/platforms/arch:x86": [":foo"],
+ "//conditions:default": [],
+ })`,
+ "native_shared_libs_64": `select({
+ "//build/bazel/platforms/arch:arm64": [":foo"],
+ "//build/bazel/platforms/arch:x86_64": [":foo"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestApexCertificateIsSrc(t *testing.T) {
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - certificate is src",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
+apex {
+ name: "com.android.apogee",
+ manifest: "apogee_manifest.json",
+ file_contexts: ":com.android.apogee-file_contexts",
+ certificate: "com.android.apogee.certificate",
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee-file_contexts"),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+ "certificate_name": `"com.android.apogee.certificate"`,
+ "file_contexts": `":com.android.apogee-file_contexts"`,
+ "manifest": `"apogee_manifest.json"`,
+ }),
+ }})
+}
+
+func TestBp2BuildOverrideApex_CertificateIsModule(t *testing.T) {
+ runOverrideApexTestCase(t, Bp2buildTestCase{
+ Description: "override_apex - certificate is module",
+ ModuleTypeUnderTest: "override_apex",
+ ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
+android_app_certificate {
+ name: "com.android.apogee.certificate",
+ certificate: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [
+ "com.android.apogee-file_contexts",
+ ],
+ bazel_module: { bp2build_available: false },
+}
+
+apex {
+ name: "com.android.apogee",
+ manifest: "apogee_manifest.json",
+ file_contexts: ":com.android.apogee-file_contexts",
+ certificate: ":com.android.apogee.certificate",
+ 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",
+ certificate: ":com.google.android.apogee.certificate",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+ "base_apex_name": `"com.android.apogee"`,
+ "file_contexts": `":com.android.apogee-file_contexts"`,
+ "certificate": `":com.google.android.apogee.certificate"`,
+ "manifest": `"apogee_manifest.json"`,
+ }),
+ }})
+}
+
+func TestBp2BuildOverrideApex_CertificateIsSrc(t *testing.T) {
+ runOverrideApexTestCase(t, Bp2buildTestCase{
+ Description: "override_apex - certificate is src",
+ ModuleTypeUnderTest: "override_apex",
+ ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
+android_app_certificate {
+ name: "com.android.apogee.certificate",
+ certificate: "com.android.apogee",
+ bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+ name: "com.android.apogee-file_contexts",
+ srcs: [
+ "com.android.apogee-file_contexts",
+ ],
+ bazel_module: { bp2build_available: false },
+}
+
+apex {
+ name: "com.android.apogee",
+ manifest: "apogee_manifest.json",
+ file_contexts: ":com.android.apogee-file_contexts",
+ certificate: ":com.android.apogee.certificate",
+ bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+ name: "com.google.android.apogee",
+ base: ":com.android.apogee",
+ certificate: "com.google.android.apogee.certificate",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+ "base_apex_name": `"com.android.apogee"`,
+ "file_contexts": `":com.android.apogee-file_contexts"`,
+ "certificate_name": `"com.google.android.apogee.certificate"`,
+ "manifest": `"apogee_manifest.json"`,
+ }),
+ }})
+}
+
+func TestApexTestBundleSimple(t *testing.T) {
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex_test - simple",
+ ModuleTypeUnderTest: "apex_test",
+ ModuleTypeUnderTestFactory: apex.TestApexBundleFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
+cc_test { name: "cc_test_1", bazel_module: { bp2build_available: false } }
+
+apex_test {
+ name: "test_com.android.apogee",
+ file_contexts: "file_contexts_file",
+ tests: ["cc_test_1"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "test_com.android.apogee", AttrNameToString{
+ "file_contexts": `"file_contexts_file"`,
+ "manifest": `"apex_manifest.json"`,
+ "testonly": `True`,
+ "tests": `[":cc_test_1"]`,
+ }),
+ }})
+}
+
+func TestApexBundle_overridePlusProductVars(t *testing.T) {
+ // Reproduction of b/271424349
+ // Tests that overriding an apex that uses product variables correctly copies the product var
+ // selects over to the override.
+ runOverrideApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - overriding a module that uses product vars",
+ ModuleTypeUnderTest: "override_apex",
+ ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+ Blueprint: `
+soong_config_string_variable {
+ name: "library_linking_strategy",
+ values: [
+ "prefer_static",
+ ],
+}
+
+soong_config_module_type {
+ name: "library_linking_strategy_apex_defaults",
+ module_type: "apex_defaults",
+ config_namespace: "ANDROID",
+ variables: ["library_linking_strategy"],
+ properties: [
+ "manifest",
+ "min_sdk_version",
+ ],
+}
+
+library_linking_strategy_apex_defaults {
+ name: "higher_min_sdk_when_prefer_static",
+ soong_config_variables: {
+ library_linking_strategy: {
+ // Use the R min_sdk_version
+ prefer_static: {},
+ // Override the R min_sdk_version to min_sdk_version that supports dcla
+ conditions_default: {
+ min_sdk_version: "31",
+ },
+ },
+ },
+}
+
+filegroup {
+ name: "foo-file_contexts",
+ srcs: [
+ "com.android.apogee-file_contexts",
+ ],
+ bazel_module: { bp2build_available: false },
+}
+
+apex {
+ name: "foo",
+ defaults: ["higher_min_sdk_when_prefer_static"],
+ min_sdk_version: "30",
+ package_name: "pkg_name",
+ file_contexts: ":foo-file_contexts",
+}
+override_apex {
+ name: "override_foo",
+ base: ":foo",
+ package_name: "override_pkg_name",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "foo", AttrNameToString{
+ "file_contexts": `":foo-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ "min_sdk_version": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "30",
+ "//conditions:default": "31",
+ })`,
+ "package_name": `"pkg_name"`,
+ }), MakeBazelTarget("apex", "override_foo", AttrNameToString{
+ "base_apex_name": `"foo"`,
+ "file_contexts": `":foo-file_contexts"`,
+ "manifest": `"apex_manifest.json"`,
+ "min_sdk_version": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "30",
+ "//conditions:default": "31",
+ })`,
+ "package_name": `"override_pkg_name"`,
+ }),
+ }})
+}
+
+func TestApexBundleSimple_customCannedFsConfig(t *testing.T) {
+ runApexTestCase(t, Bp2buildTestCase{
+ Description: "apex - custom canned_fs_config",
+ ModuleTypeUnderTest: "apex",
+ ModuleTypeUnderTestFactory: apex.BundleFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
+apex {
+ name: "com.android.apogee",
+ canned_fs_config: "custom.canned_fs_config",
+ file_contexts: "file_contexts_file",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+ "canned_fs_config": `"custom.canned_fs_config"`,
+ "file_contexts": `"file_contexts_file"`,
+ "manifest": `"apex_manifest.json"`,
+ }),
+ }})
}
diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
index 1d949901..f9a68c9 100644
--- a/bp2build/apex_key_conversion_test.go
+++ b/bp2build/apex_key_conversion_test.go
@@ -21,30 +21,77 @@
"testing"
)
-func runApexKeyTestCase(t *testing.T, tc bp2buildTestCase) {
+func runApexKeyTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, registerApexKeyModuleTypes, tc)
+ RunBp2BuildTestCase(t, registerApexKeyModuleTypes, tc)
}
func registerApexKeyModuleTypes(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
}
-func TestApexKeySimple(t *testing.T) {
- runApexKeyTestCase(t, bp2buildTestCase{
- description: "apex key - simple example",
- moduleTypeUnderTest: "apex_key",
- moduleTypeUnderTestFactory: apex.ApexKeyFactory,
- filesystem: map[string]string{},
- blueprint: `
+func TestApexKeySimple_KeysAreSrcFilesInSameDir(t *testing.T) {
+ runApexKeyTestCase(t, Bp2buildTestCase{
+ Description: "apex key - keys are src files, use key attributes",
+ ModuleTypeUnderTest: "apex_key",
+ ModuleTypeUnderTestFactory: apex.ApexKeyFactory,
+ Filesystem: map[string]string{
+ "com.android.apogee.avbpubkey": "",
+ "com.android.apogee.pem": "",
+ },
+ Blueprint: `
apex_key {
name: "com.android.apogee.key",
public_key: "com.android.apogee.avbpubkey",
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"`,
}),
}})
}
+
+func TestApexKeySimple_KeysAreSrcFilesNotInDir(t *testing.T) {
+ runApexKeyTestCase(t, Bp2buildTestCase{
+ Description: "apex key - keys are not src or module, use key_name attributes",
+ ModuleTypeUnderTest: "apex_key",
+ ModuleTypeUnderTestFactory: apex.ApexKeyFactory,
+ Filesystem: map[string]string{
+ // deliberately left empty
+ },
+ Blueprint: `
+apex_key {
+ name: "com.android.apogee.key",
+ public_key: "com.android.apogee.avbpubkey",
+ private_key: "com.android.apogee.pem",
+}
+`,
+ ExpectedBazelTargets: []string{MakeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", AttrNameToString{
+ "private_key_name": `"com.android.apogee.pem"`,
+ "public_key_name": `"com.android.apogee.avbpubkey"`,
+ }),
+ }})
+}
+
+func TestApexKey_KeysAreModules(t *testing.T) {
+ runApexKeyTestCase(t, Bp2buildTestCase{
+ Description: "apex key - keys are modules, use key attributes",
+ ModuleTypeUnderTest: "apex_key",
+ ModuleTypeUnderTestFactory: apex.ApexKeyFactory,
+ Filesystem: map[string]string{},
+ Blueprint: `
+apex_key {
+ name: "com.android.apogee.key",
+ public_key: ":com.android.apogee.avbpubkey",
+ private_key: ":com.android.apogee.pem",
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee.avbpubkey") +
+ simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee.pem"),
+ 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/api_domain_conversion_test.go b/bp2build/api_domain_conversion_test.go
new file mode 100644
index 0000000..224008f
--- /dev/null
+++ b/bp2build/api_domain_conversion_test.go
@@ -0,0 +1,68 @@
+// 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 (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+)
+
+func registerApiDomainModuleTypes(ctx android.RegistrationContext) {
+ android.RegisterApiDomainBuildComponents(ctx)
+ cc.RegisterNdkModuleTypes(ctx)
+ cc.RegisterLibraryBuildComponents(ctx)
+}
+
+func TestApiDomainContributionsTest(t *testing.T) {
+ bp := `
+ api_domain {
+ name: "system",
+ cc_api_contributions: [
+ "libfoo.ndk",
+ "libbar",
+ ],
+ }
+ `
+ fs := map[string]string{
+ "libfoo/Android.bp": `
+ ndk_library {
+ name: "libfoo",
+ }
+ `,
+ "libbar/Android.bp": `
+ cc_library {
+ name: "libbar",
+ }
+ `,
+ }
+ expectedBazelTarget := MakeBazelTargetNoRestrictions(
+ "api_domain",
+ "system",
+ AttrNameToString{
+ "cc_api_contributions": `[
+ "//libfoo:libfoo.ndk.contribution",
+ "//libbar:libbar.contribution",
+ ]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ },
+ )
+ RunApiBp2BuildTestCase(t, registerApiDomainModuleTypes, Bp2buildTestCase{
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{expectedBazelTarget},
+ Filesystem: fs,
+ })
+}
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index b0c3899..d1dfb9d 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -17,19 +17,57 @@
import (
"fmt"
"os"
+ "path/filepath"
"strings"
"android/soong/android"
"android/soong/bazel"
+ "android/soong/shared"
)
+func deleteFilesExcept(ctx *CodegenContext, rootOutputPath android.OutputPath, except []BazelFile) {
+ // Delete files that should no longer be present.
+ bp2buildDirAbs := shared.JoinPath(ctx.topDir, rootOutputPath.String())
+
+ filesToDelete := make(map[string]struct{})
+ err := filepath.Walk(bp2buildDirAbs,
+ func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ if !info.IsDir() {
+ relPath, err := filepath.Rel(bp2buildDirAbs, path)
+ if err != nil {
+ return err
+ }
+ filesToDelete[relPath] = struct{}{}
+ }
+ return nil
+ })
+ if err != nil {
+ fmt.Printf("ERROR reading %s: %s", bp2buildDirAbs, err)
+ os.Exit(1)
+ }
+
+ for _, bazelFile := range except {
+ filePath := filepath.Join(bazelFile.Dir, bazelFile.Basename)
+ delete(filesToDelete, filePath)
+ }
+ for f, _ := range filesToDelete {
+ absPath := shared.JoinPath(bp2buildDirAbs, f)
+ if err := os.RemoveAll(absPath); err != nil {
+ fmt.Printf("ERROR deleting %s: %s", absPath, err)
+ os.Exit(1)
+ }
+ }
+}
+
// Codegen is the backend of bp2build. The code generator is responsible for
// writing .bzl files that are equivalent to Android.bp files that are capable
// of being built with Bazel.
-func Codegen(ctx *CodegenContext) CodegenMetrics {
+func Codegen(ctx *CodegenContext) *CodegenMetrics {
// This directory stores BUILD files that could be eventually checked-in.
bp2buildDir := android.PathForOutput(ctx, "bp2build")
- android.RemoveAllOutputDir(bp2buildDir)
res, errs := GenerateBazelTargets(ctx, true)
if len(errs) > 0 {
@@ -40,19 +78,50 @@
fmt.Printf("ERROR: Encountered %d error(s): \nERROR: %s", len(errs), strings.Join(errMsgs, "\n"))
os.Exit(1)
}
- bp2buildFiles := CreateBazelFiles(nil, res.buildFileToTargets, ctx.mode)
+ bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode)
writeFiles(ctx, bp2buildDir, bp2buildFiles)
+ // Delete files under the bp2build root which weren't just written. An
+ // alternative would have been to delete the whole directory and write these
+ // files. However, this would regenerate files which were otherwise unchanged
+ // since the last bp2build run, which would have negative incremental
+ // performance implications.
+ deleteFilesExcept(ctx, bp2buildDir, bp2buildFiles)
- soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
- writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(ctx.Config(), res.metrics))
+ injectionFiles, err := CreateSoongInjectionDirFiles(ctx, res.metrics)
+ if err != nil {
+ fmt.Printf("%s\n", err.Error())
+ os.Exit(1)
+ }
+ writeFiles(ctx, android.PathForOutput(ctx, bazel.SoongInjectionDirName), injectionFiles)
+ return &res.metrics
+}
- return res.metrics
+// Wrapper function that will be responsible for all files in soong_injection directory
+// This includes
+// 1. config value(s) that are hardcoded in Soong
+// 2. product_config variables
+func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) ([]BazelFile, error) {
+ var ret []BazelFile
+
+ productConfigFiles, err := CreateProductConfigFiles(ctx)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, productConfigFiles...)
+ injectionFiles, err := soongInjectionFiles(ctx.Config(), metrics)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, injectionFiles...)
+ return ret, nil
}
// Get the output directory and create it if it doesn't exist.
func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
dirPath := outputDir.Join(ctx, dir)
- android.CreateOutputDirIfNonexistent(dirPath, os.ModePerm)
+ if err := android.CreateOutputDirIfNonexistent(dirPath, os.ModePerm); err != nil {
+ fmt.Printf("ERROR: path %s: %s", dirPath, err.Error())
+ }
return dirPath
}
@@ -60,13 +129,13 @@
func writeFiles(ctx android.PathContext, outputDir android.OutputPath, files []BazelFile) {
for _, f := range files {
p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename)
- if err := writeFile(ctx, p, f.Contents); err != nil {
+ if err := writeFile(p, f.Contents); err != nil {
panic(fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err))
}
}
}
-func writeFile(ctx android.PathContext, pathToFile android.OutputPath, content string) error {
+func writeFile(pathToFile android.OutputPath, content string) error {
// These files are made editable to allow users to modify and iterate on them
// in the source tree.
return android.WriteFileToOutputDir(pathToFile, []byte(content), 0644)
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
new file mode 100644
index 0000000..3abef9d
--- /dev/null
+++ b/bp2build/bp2build_product_config.go
@@ -0,0 +1,119 @@
+package bp2build
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+func CreateProductConfigFiles(
+ ctx *CodegenContext) ([]BazelFile, error) {
+ cfg := &ctx.config
+ targetProduct := "unknown"
+ if cfg.HasDeviceProduct() {
+ targetProduct = cfg.DeviceProduct()
+ }
+ targetBuildVariant := "user"
+ if cfg.Eng() {
+ targetBuildVariant = "eng"
+ } else if cfg.Debuggable() {
+ targetBuildVariant = "userdebug"
+ }
+
+ productVariablesFileName := cfg.ProductVariablesFileName
+ if !strings.HasPrefix(productVariablesFileName, "/") {
+ productVariablesFileName = filepath.Join(ctx.topDir, productVariablesFileName)
+ }
+ bytes, err := os.ReadFile(productVariablesFileName)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO(b/249685973): the name is product_config_platforms because product_config
+ // was already used for other files. Deduplicate them.
+ currentProductFolder := fmt.Sprintf("product_config_platforms/products/%s-%s", targetProduct, targetBuildVariant)
+
+ productReplacer := strings.NewReplacer(
+ "{PRODUCT}", targetProduct,
+ "{VARIANT}", targetBuildVariant,
+ "{PRODUCT_FOLDER}", currentProductFolder)
+
+ result := []BazelFile{
+ newFile(
+ currentProductFolder,
+ "soong.variables.bzl",
+ `variables = json.decode("""`+strings.ReplaceAll(string(bytes), "\\", "\\\\")+`""")`),
+ newFile(
+ currentProductFolder,
+ "BUILD",
+ productReplacer.Replace(`
+package(default_visibility=[
+ "@soong_injection//product_config_platforms:__subpackages__",
+ "@//build/bazel/product_config:__subpackages__",
+])
+load(":soong.variables.bzl", _soong_variables = "variables")
+load("@//build/bazel/product_config:android_product.bzl", "android_product")
+
+android_product(
+ name = "{PRODUCT}-{VARIANT}",
+ soong_variables = _soong_variables,
+)
+`)),
+ newFile(
+ "product_config_platforms",
+ "BUILD.bazel",
+ productReplacer.Replace(`
+package(default_visibility = [
+ "@//build/bazel/product_config:__subpackages__",
+ "@soong_injection//product_config_platforms:__subpackages__",
+])
+`)),
+ newFile(
+ "product_config_platforms",
+ "product_labels.bzl",
+ productReplacer.Replace(`
+# This file keeps a list of all the products in the android source tree, because they're
+# discovered as part of a preprocessing step before bazel runs.
+# TODO: When we start generating the platforms for more than just the
+# currently lunched product, they should all be listed here
+product_labels = [
+ "@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}"
+]
+`)),
+ newFile(
+ "product_config_platforms",
+ "common.bazelrc",
+ productReplacer.Replace(`
+build --platforms @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+
+build:android --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
+build:linux_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+build:linux_bionic_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_bionic_x86_64
+build:linux_musl_x86 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86
+build:linux_musl_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86_64
+`)),
+ newFile(
+ "product_config_platforms",
+ "linux.bazelrc",
+ productReplacer.Replace(`
+build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+`)),
+ newFile(
+ "product_config_platforms",
+ "darwin.bazelrc",
+ productReplacer.Replace(`
+build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_darwin_x86_64
+`)),
+ newFile(
+ "product_config_platforms",
+ "platform_mappings",
+ productReplacer.Replace(`
+flags:
+ --cpu=k8
+ @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
+`)),
+ }
+
+ return result, nil
+}
diff --git a/bp2build/bpf_conversion_test.go b/bp2build/bpf_conversion_test.go
new file mode 100644
index 0000000..1259f9e
--- /dev/null
+++ b/bp2build/bpf_conversion_test.go
@@ -0,0 +1,65 @@
+// 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 (
+ "android/soong/android"
+ "android/soong/bpf"
+
+ "testing"
+)
+
+func runBpfTestCase(t *testing.T, tc Bp2buildTestCase) {
+ t.Helper()
+ (&tc).ModuleTypeUnderTest = "bpf"
+ (&tc).ModuleTypeUnderTestFactory = bpf.BpfFactory
+ RunBp2BuildTestCase(t, registerBpfModuleTypes, tc)
+}
+
+func registerBpfModuleTypes(ctx android.RegistrationContext) {}
+
+func TestBpfSupportedAttrs(t *testing.T) {
+ runBpfTestCase(t, Bp2buildTestCase{
+ Description: "Bpf module only converts supported attributes",
+ Filesystem: map[string]string{},
+ Blueprint: `
+bpf {
+ name: "bpfTestOut.o",
+ srcs: ["bpfTestSrcOne.c",
+ "bpfTestSrcTwo.c"],
+ btf: true,
+ cflags: ["-bpfCflagOne",
+ "-bpfCflagTwo"],
+ include_dirs: ["ia/ib/ic"],
+ sub_dir: "sa/ab",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("bpf", "bpfTestOut.o", AttrNameToString{
+ "absolute_includes": `["ia/ib/ic"]`,
+ "btf": `True`,
+ "copts": `[
+ "-bpfCflagOne",
+ "-bpfCflagTwo",
+ ]`,
+ "srcs": `[
+ "bpfTestSrcOne.c",
+ "bpfTestSrcTwo.c",
+ ]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index a96a3fc..b7678a4 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -28,7 +28,6 @@
"android/soong/android"
"android/soong/bazel"
"android/soong/starlark_fmt"
-
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -43,7 +42,6 @@
content string
ruleClass string
bzlLoadLocation string
- handcrafted bool
}
// IsLoadedFromStarlark determines if the BazelTarget's rule class is loaded from a .bzl file,
@@ -62,28 +60,30 @@
}
}
+// PackageName returns the package of the Bazel target.
+// Defaults to root of tree.
+func (t BazelTarget) PackageName() string {
+ if t.packageName == "" {
+ return "."
+ }
+ return t.packageName
+}
+
// BazelTargets is a typedef for a slice of BazelTarget objects.
type BazelTargets []BazelTarget
-// HasHandcraftedTargetsreturns true if a set of bazel targets contain
-// handcrafted ones.
-func (targets BazelTargets) hasHandcraftedTargets() bool {
+func (targets BazelTargets) packageRule() *BazelTarget {
for _, target := range targets {
- if target.handcrafted {
- return true
+ if target.ruleClass == "package" {
+ return &target
}
}
- return false
+ return nil
}
// sort a list of BazelTargets in-place, by name, and by generated/handcrafted types.
func (targets BazelTargets) sort() {
sort.Slice(targets, func(i, j int) bool {
- if targets[i].handcrafted != targets[j].handcrafted {
- // Handcrafted targets will be generated after the bp2build generated targets.
- return targets[j].handcrafted
- }
- // This will cover all bp2build generated targets.
return targets[i].name < targets[j].name
})
}
@@ -94,19 +94,9 @@
func (targets BazelTargets) String() string {
var res string
for i, target := range targets {
- // There is only at most 1 handcrafted "target", because its contents
- // represent the entire BUILD file content from the tree. See
- // build_conversion.go#getHandcraftedBuildContent for more information.
- //
- // Add a header to make it easy to debug where the handcrafted targets
- // are in a generated BUILD file.
- if target.handcrafted {
- res += "# -----------------------------\n"
- res += "# Section: Handcrafted targets. \n"
- res += "# -----------------------------\n\n"
+ if target.ruleClass != "package" {
+ res += target.content
}
-
- res += target.content
if i != len(targets)-1 {
res += "\n\n"
}
@@ -155,32 +145,36 @@
type CodegenContext struct {
config android.Config
- context android.Context
+ context *android.Context
mode CodegenMode
additionalDeps []string
unconvertedDepMode unconvertedDepsMode
+ topDir string
}
-func (c *CodegenContext) Mode() CodegenMode {
- return c.mode
+func (ctx *CodegenContext) Mode() CodegenMode {
+ return ctx.mode
}
// CodegenMode is an enum to differentiate code-generation modes.
type CodegenMode int
const (
- // Bp2Build: generate BUILD files with targets buildable by Bazel directly.
+ // Bp2Build - generate BUILD files with targets buildable by Bazel directly.
//
// This mode is used for the Soong->Bazel build definition conversion.
Bp2Build CodegenMode = iota
- // QueryView: generate BUILD files with targets representing fully mutated
+ // QueryView - generate BUILD files with targets representing fully mutated
// Soong modules, representing the fully configured Soong module graph with
- // variants and dependency endges.
+ // variants and dependency edges.
//
// This mode is used for discovering and introspecting the existing Soong
// module graph.
QueryView
+
+ // ApiBp2build - generate BUILD files for API contribution targets
+ ApiBp2build
)
type unconvertedDepsMode int
@@ -199,6 +193,8 @@
return "Bp2Build"
case QueryView:
return "QueryView"
+ case ApiBp2build:
+ return "ApiBp2build"
default:
return fmt.Sprintf("%d", mode)
}
@@ -217,12 +213,12 @@
return ctx.additionalDeps
}
-func (ctx *CodegenContext) Config() android.Config { return ctx.config }
-func (ctx *CodegenContext) Context() android.Context { return ctx.context }
+func (ctx *CodegenContext) Config() android.Config { return ctx.config }
+func (ctx *CodegenContext) Context() *android.Context { return ctx.context }
// NewCodegenContext creates a wrapper context that conforms to PathContext for
// writing BUILD files in the output directory.
-func NewCodegenContext(config android.Config, context android.Context, mode CodegenMode) *CodegenContext {
+func NewCodegenContext(config android.Config, context *android.Context, mode CodegenMode, topDir string) *CodegenContext {
var unconvertedDeps unconvertedDepsMode
if config.IsEnvTrue("BP2BUILD_ERROR_UNCONVERTED") {
unconvertedDeps = errorModulesUnconvertedDeps
@@ -232,6 +228,7 @@
config: config,
mode: mode,
unconvertedDepMode: unconvertedDeps,
+ topDir: topDir,
}
}
@@ -239,7 +236,7 @@
// the generated attributes are sorted to ensure determinism.
func propsToAttributes(props map[string]string) string {
var attributes string
- for _, propName := range android.SortedStringKeys(props) {
+ for _, propName := range android.SortedKeys(props) {
attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
}
return attributes
@@ -256,14 +253,9 @@
func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
buildFileToTargets := make(map[string]BazelTargets)
- buildFileToAppend := make(map[string]bool)
// Simple metrics tracking for bp2build
- metrics := CodegenMetrics{
- ruleClassCount: make(map[string]uint64),
- convertedModuleTypeCount: make(map[string]uint64),
- totalModuleTypeCount: make(map[string]uint64),
- }
+ metrics := CreateCodegenMetrics()
dirs := make(map[string]bool)
@@ -288,61 +280,50 @@
// Handle modules converted to handcrafted targets.
//
// Since these modules are associated with some handcrafted
- // target in a BUILD file, we simply append the entire contents
- // of that BUILD file to the generated BUILD file.
- //
- // The append operation is only done once, even if there are
- // multiple modules from the same directory associated to
- // targets in the same BUILD file (or package).
+ // target in a BUILD file, we don't autoconvert them.
// Log the module.
- metrics.AddConvertedModule(m, moduleType, Handcrafted)
-
- pathToBuildFile := getBazelPackagePath(b)
- if _, exists := buildFileToAppend[pathToBuildFile]; exists {
- // Append the BUILD file content once per package, at most.
- return
- }
- t, err := getHandcraftedBuildContent(ctx, b, pathToBuildFile)
- if err != nil {
- errs = append(errs, fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
- return
- }
- targets = append(targets, t)
- // TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
- // something more targeted based on the rule type and target
- buildFileToAppend[pathToBuildFile] = true
+ metrics.AddConvertedModule(m, moduleType, dir, Handcrafted)
} else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
// Handle modules converted to generated targets.
// Log the module.
- metrics.AddConvertedModule(aModule, moduleType, Generated)
+ metrics.AddConvertedModule(aModule, moduleType, dir, Generated)
// Handle modules with unconverted deps. By default, emit a warning.
if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 {
- msg := fmt.Sprintf("%q depends on unconverted modules: %s", m.Name(), strings.Join(unconvertedDeps, ", "))
- if ctx.unconvertedDepMode == warnUnconvertedDeps {
+ msg := fmt.Sprintf("%s %s:%s depends on unconverted modules: %s",
+ moduleType, bpCtx.ModuleDir(m), m.Name(), strings.Join(unconvertedDeps, ", "))
+ switch ctx.unconvertedDepMode {
+ case warnUnconvertedDeps:
metrics.moduleWithUnconvertedDepsMsgs = append(metrics.moduleWithUnconvertedDepsMsgs, msg)
- } else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+ case errorModulesUnconvertedDeps:
errs = append(errs, fmt.Errorf(msg))
return
}
}
if unconvertedDeps := aModule.GetMissingBp2buildDeps(); len(unconvertedDeps) > 0 {
- msg := fmt.Sprintf("%q depends on missing modules: %s", m.Name(), strings.Join(unconvertedDeps, ", "))
- if ctx.unconvertedDepMode == warnUnconvertedDeps {
+ msg := fmt.Sprintf("%s %s:%s depends on missing modules: %s",
+ moduleType, bpCtx.ModuleDir(m), m.Name(), strings.Join(unconvertedDeps, ", "))
+ switch ctx.unconvertedDepMode {
+ case warnUnconvertedDeps:
metrics.moduleWithMissingDepsMsgs = append(metrics.moduleWithMissingDepsMsgs, msg)
- } else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+ case errorModulesUnconvertedDeps:
errs = append(errs, fmt.Errorf(msg))
return
}
}
- targets = generateBazelTargets(bpCtx, aModule)
+ var targetErrs []error
+ targets, targetErrs = generateBazelTargets(bpCtx, aModule)
+ errs = append(errs, targetErrs...)
for _, t := range targets {
// A module can potentially generate more than 1 Bazel
// target, each of a different rule class.
metrics.IncrementRuleClassCount(t.ruleClass)
}
+ } else if _, ok := ctx.Config().BazelModulesForceEnabledByFlag()[m.Name()]; ok && m.Name() != "" {
+ err := fmt.Errorf("Force Enabled Module %s not converted", m.Name())
+ errs = append(errs, err)
} else {
metrics.AddUnconvertedModule(moduleType)
return
@@ -354,14 +335,24 @@
// be mapped cleanly to a bazel label.
return
}
- t := generateSoongModuleTarget(bpCtx, m)
+ t, err := generateSoongModuleTarget(bpCtx, m)
+ if err != nil {
+ errs = append(errs, err)
+ }
targets = append(targets, t)
+ case ApiBp2build:
+ if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
+ targets, errs = generateBazelTargets(bpCtx, aModule)
+ }
default:
errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
return
}
- buildFileToTargets[dir] = append(buildFileToTargets[dir], targets...)
+ for _, target := range targets {
+ targetDir := target.PackageName()
+ buildFileToTargets[targetDir] = append(buildFileToTargets[targetDir], target)
+ }
})
if len(errs) > 0 {
@@ -371,7 +362,16 @@
if generateFilegroups {
// Add a filegroup target that exposes all sources in the subtree of this package
// NOTE: This also means we generate a BUILD file for every Android.bp file (as long as it has at least one module)
- for dir, _ := range dirs {
+ //
+ // This works because: https://bazel.build/reference/be/functions#exports_files
+ // "As a legacy behaviour, also files mentioned as input to a rule are exported with the
+ // default visibility until the flag --incompatible_no_implicit_file_export is flipped. However, this behavior
+ // should not be relied upon and actively migrated away from."
+ //
+ // TODO(b/198619163): We should change this to export_files(glob(["**/*"])) instead, but doing that causes these errors:
+ // "Error in exports_files: generated label '//external/avb:avbtool' conflicts with existing py_binary rule"
+ // So we need to solve all the "target ... is both a rule and a file" warnings first.
+ for dir := range dirs {
buildFileToTargets[dir] = append(buildFileToTargets[dir], BazelTarget{
name: "bp2build_all_srcs",
content: `filegroup(name = "bp2build_all_srcs", srcs = glob(["**/*"]))`,
@@ -386,35 +386,18 @@
}, errs
}
-func getBazelPackagePath(b android.Bazelable) string {
- label := b.HandcraftedLabel()
- pathToBuildFile := strings.TrimPrefix(label, "//")
- pathToBuildFile = strings.Split(pathToBuildFile, ":")[0]
- return pathToBuildFile
-}
-
-func getHandcraftedBuildContent(ctx *CodegenContext, b android.Bazelable, pathToBuildFile string) (BazelTarget, error) {
- p := android.ExistentPathForSource(ctx, pathToBuildFile, HandcraftedBuildFileName)
- if !p.Valid() {
- return BazelTarget{}, fmt.Errorf("Could not find file %q for handcrafted target.", pathToBuildFile)
- }
- c, err := b.GetBazelBuildFileContents(ctx.Config(), pathToBuildFile, HandcraftedBuildFileName)
- if err != nil {
- return BazelTarget{}, err
- }
- // TODO(b/181575318): once this is more targeted, we need to include name, rule class, etc
- return BazelTarget{
- content: c,
- handcrafted: true,
- }, nil
-}
-
-func generateBazelTargets(ctx bpToBuildContext, m android.Module) []BazelTarget {
+func generateBazelTargets(ctx bpToBuildContext, m android.Module) ([]BazelTarget, []error) {
var targets []BazelTarget
+ var errs []error
for _, m := range m.Bp2buildTargets() {
- targets = append(targets, generateBazelTarget(ctx, m))
+ target, err := generateBazelTarget(ctx, m)
+ if err != nil {
+ errs = append(errs, err)
+ return targets, errs
+ }
+ targets = append(targets, target)
}
- return targets
+ return targets, errs
}
type bp2buildModule interface {
@@ -425,13 +408,16 @@
BazelAttributes() []interface{}
}
-func generateBazelTarget(ctx bpToBuildContext, m bp2buildModule) BazelTarget {
+func generateBazelTarget(ctx bpToBuildContext, m bp2buildModule) (BazelTarget, error) {
ruleClass := m.BazelRuleClass()
bzlLoadLocation := m.BazelRuleLoadLocation()
// extract the bazel attributes from the module.
attrs := m.BazelAttributes()
- props := extractModuleProperties(attrs, true)
+ props, err := extractModuleProperties(attrs, true)
+ if err != nil {
+ return BazelTarget{}, err
+ }
// name is handled in a special manner
delete(props.Attrs, "name")
@@ -439,26 +425,26 @@
// Return the Bazel target with rule class and attributes, ready to be
// code-generated.
attributes := propsToAttributes(props.Attrs)
+ var content string
targetName := m.TargetName()
+ if targetName != "" {
+ content = fmt.Sprintf(ruleTargetTemplate, ruleClass, targetName, attributes)
+ } else {
+ content = fmt.Sprintf(unnamedRuleTargetTemplate, ruleClass, attributes)
+ }
return BazelTarget{
name: targetName,
packageName: m.TargetPackage(),
ruleClass: ruleClass,
bzlLoadLocation: bzlLoadLocation,
- content: fmt.Sprintf(
- bazelTarget,
- ruleClass,
- targetName,
- attributes,
- ),
- handcrafted: false,
- }
+ content: content,
+ }, nil
}
// Convert a module and its deps and props into a Bazel macro/rule
// representation in the BUILD file.
-func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
- props := getBuildProperties(ctx, m)
+func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) (BazelTarget, error) {
+ props, err := getBuildProperties(ctx, m)
// TODO(b/163018919): DirectDeps can have duplicate (module, variant)
// items, if the modules are added using different DependencyTag. Figure
@@ -470,43 +456,44 @@
})
}
- for p, _ := range ignoredPropNames {
+ for p := range ignoredPropNames {
delete(props.Attrs, p)
}
attributes := propsToAttributes(props.Attrs)
depLabelList := "[\n"
- for depLabel, _ := range depLabels {
+ for depLabel := range depLabels {
depLabelList += fmt.Sprintf(" %q,\n", depLabel)
}
depLabelList += " ]"
targetName := targetNameWithVariant(ctx, m)
return BazelTarget{
- name: targetName,
+ name: targetName,
+ packageName: ctx.ModuleDir(m),
content: fmt.Sprintf(
- soongModuleTarget,
+ soongModuleTargetTemplate,
targetName,
ctx.ModuleName(m),
canonicalizeModuleType(ctx.ModuleType(m)),
ctx.ModuleSubDir(m),
depLabelList,
attributes),
- }
+ }, err
}
-func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) BazelAttributes {
+func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) (BazelAttributes, error) {
// TODO: this omits properties for blueprint modules (blueprint_go_binary,
// bootstrap_go_binary, bootstrap_go_package), which will have to be handled separately.
if aModule, ok := m.(android.Module); ok {
return extractModuleProperties(aModule.GetProperties(), false)
}
- return BazelAttributes{}
+ return BazelAttributes{}, nil
}
// Generically extract module properties and types into a map, keyed by the module property name.
-func extractModuleProperties(props []interface{}, checkForDuplicateProperties bool) BazelAttributes {
+func extractModuleProperties(props []interface{}, checkForDuplicateProperties bool) (BazelAttributes, error) {
ret := map[string]string{}
// Iterate over this android.Module's property structs.
@@ -519,24 +506,29 @@
// manipulate internal props, if needed.
if isStructPtr(propertiesValue.Type()) {
structValue := propertiesValue.Elem()
- for k, v := range extractStructProperties(structValue, 0) {
+ ok, err := extractStructProperties(structValue, 0)
+ if err != nil {
+ return BazelAttributes{}, err
+ }
+ for k, v := range ok {
if existing, exists := ret[k]; checkForDuplicateProperties && exists {
- panic(fmt.Errorf(
+ return BazelAttributes{}, fmt.Errorf(
"%s (%v) is present in properties whereas it should be consolidated into a commonAttributes",
- k, existing))
+ k, existing)
}
ret[k] = v
}
} else {
- panic(fmt.Errorf(
- "properties must be a pointer to a struct, got %T",
- propertiesValue.Interface()))
+ return BazelAttributes{},
+ fmt.Errorf(
+ "properties must be a pointer to a struct, got %T",
+ propertiesValue.Interface())
}
}
return BazelAttributes{
Attrs: ret,
- }
+ }, nil
}
func isStructPtr(t reflect.Type) bool {
@@ -594,7 +586,12 @@
}
// Sort and print the struct props by the key.
- structProps := extractStructProperties(propertyValue, indent)
+ structProps, err := extractStructProperties(propertyValue, indent)
+
+ if err != nil {
+ return "", err
+ }
+
if len(structProps) == 0 {
return "", nil
}
@@ -613,11 +610,13 @@
// which each property value correctly pretty-printed and indented at the right nest level,
// since property structs can be nested. In Starlark, nested structs are represented as nested
// dicts: https://docs.bazel.build/skylark/lib/dict.html
-func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
+func extractStructProperties(structValue reflect.Value, indent int) (map[string]string, error) {
if structValue.Kind() != reflect.Struct {
- panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
+ return map[string]string{}, fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind())
}
+ var err error
+
ret := map[string]string{}
structType := structValue.Type()
for i := 0; i < structValue.NumField(); i++ {
@@ -638,7 +637,10 @@
fieldValue = fieldValue.Elem()
}
if fieldValue.Type().Kind() == reflect.Struct {
- propsToMerge := extractStructProperties(fieldValue, indent)
+ propsToMerge, err := extractStructProperties(fieldValue, indent)
+ if err != nil {
+ return map[string]string{}, err
+ }
for prop, value := range propsToMerge {
ret[prop] = value
}
@@ -647,20 +649,20 @@
}
propertyName := proptools.PropertyNameForField(field.Name)
- prettyPrintedValue, err := prettyPrint(fieldValue, indent+1, false)
+ var prettyPrintedValue string
+ prettyPrintedValue, err = prettyPrint(fieldValue, indent+1, false)
if err != nil {
- panic(
- fmt.Errorf(
- "Error while parsing property: %q. %s",
- propertyName,
- err))
+ return map[string]string{}, fmt.Errorf(
+ "Error while parsing property: %q. %s",
+ propertyName,
+ err)
}
if prettyPrintedValue != "" {
ret[propertyName] = prettyPrintedValue
}
}
- return ret
+ return ret, nil
}
func isZero(value reflect.Value) bool {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 7c83109..73ee26b 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -201,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"})
@@ -209,7 +209,7 @@
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
- codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
+ codegenCtx := NewCodegenContext(config, ctx.Context, QueryView, "")
bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
android.FailIfErrored(t, err)
if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
@@ -229,30 +229,76 @@
}
func TestGenerateBazelTargetModules(t *testing.T) {
- testCases := []bp2buildTestCase{
+ testCases := []Bp2buildTestCase{
{
- description: "string ptr props",
- blueprint: `custom {
+ Description: "string prop empty",
+ Blueprint: `custom {
+ name: "foo",
+ string_literal_prop: "",
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "foo", AttrNameToString{
+ "string_literal_prop": `""`,
+ }),
+ },
+ },
+ {
+ Description: `string prop "PROP"`,
+ Blueprint: `custom {
+ name: "foo",
+ string_literal_prop: "PROP",
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "foo", AttrNameToString{
+ "string_literal_prop": `"PROP"`,
+ }),
+ },
+ },
+ {
+ Description: `string prop arch variant`,
+ Blueprint: `custom {
+ name: "foo",
+ arch: {
+ arm: { string_literal_prop: "ARM" },
+ arm64: { string_literal_prop: "ARM64" },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "foo", AttrNameToString{
+ "string_literal_prop": `select({
+ "//build/bazel/platforms/arch:arm": "ARM",
+ "//build/bazel/platforms/arch:arm64": "ARM64",
+ "//conditions:default": None,
+ })`,
+ }),
+ },
+ },
+ {
+ Description: "string ptr props",
+ Blueprint: `custom {
name: "foo",
string_ptr_prop: "",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("custom", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "foo", AttrNameToString{
"string_ptr_prop": `""`,
}),
},
},
{
- description: "string props",
- blueprint: `custom {
+ Description: "string list props",
+ Blueprint: `custom {
name: "foo",
string_list_prop: ["a", "b"],
string_ptr_prop: "a",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("custom", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "foo", AttrNameToString{
"string_list_prop": `[
"a",
"b",
@@ -262,15 +308,15 @@
},
},
{
- description: "control characters",
- blueprint: `custom {
+ Description: "control characters",
+ Blueprint: `custom {
name: "foo",
string_list_prop: ["\t", "\n"],
string_ptr_prop: "a\t\n\r",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("custom", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "foo", AttrNameToString{
"string_list_prop": `[
"\t",
"\n",
@@ -280,8 +326,8 @@
},
},
{
- description: "handles dep",
- blueprint: `custom {
+ Description: "handles dep",
+ Blueprint: `custom {
name: "has_dep",
arch_paths: [":dep"],
bazel_module: { bp2build_available: true },
@@ -292,31 +338,31 @@
arch_paths: ["abc"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("custom", "dep", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "dep", AttrNameToString{
"arch_paths": `["abc"]`,
}),
- makeBazelTarget("custom", "has_dep", attrNameToString{
+ MakeBazelTarget("custom", "has_dep", AttrNameToString{
"arch_paths": `[":dep"]`,
}),
},
},
{
- description: "non-existent dep",
- blueprint: `custom {
+ Description: "non-existent dep",
+ Blueprint: `custom {
name: "has_dep",
arch_paths: [":dep"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("custom", "has_dep", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "has_dep", AttrNameToString{
"arch_paths": `[":dep__BP2BUILD__MISSING__DEP"]`,
}),
},
},
{
- description: "arch-variant srcs",
- blueprint: `custom {
+ Description: "arch-variant srcs",
+ Blueprint: `custom {
name: "arch_paths",
arch: {
x86: { arch_paths: ["x86.txt"] },
@@ -345,8 +391,8 @@
},
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("custom", "arch_paths", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "arch_paths", AttrNameToString{
"arch_paths": `select({
"//build/bazel/platforms/arch:arm": [
"arm.txt",
@@ -380,13 +426,6 @@
"darwin.txt",
"not_windows.txt",
],
- "//build/bazel/platforms/os:linux": [
- "host.txt",
- "linux.txt",
- "glibc.txt",
- "linux_glibc.txt",
- "not_windows.txt",
- ],
"//build/bazel/platforms/os:linux_bionic": [
"host.txt",
"linux.txt",
@@ -394,6 +433,13 @@
"linux_bionic.txt",
"not_windows.txt",
],
+ "//build/bazel/platforms/os:linux_glibc": [
+ "host.txt",
+ "linux.txt",
+ "glibc.txt",
+ "linux_glibc.txt",
+ "not_windows.txt",
+ ],
"//build/bazel/platforms/os:linux_musl": [
"host.txt",
"linux.txt",
@@ -411,8 +457,8 @@
},
},
{
- description: "arch-variant deps",
- blueprint: `custom {
+ Description: "arch-variant deps",
+ Blueprint: `custom {
name: "has_dep",
arch: {
x86: {
@@ -427,11 +473,11 @@
arch_paths: ["abc"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("custom", "dep", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "dep", AttrNameToString{
"arch_paths": `["abc"]`,
}),
- makeBazelTarget("custom", "has_dep", attrNameToString{
+ MakeBazelTarget("custom", "has_dep", AttrNameToString{
"arch_paths": `select({
"//build/bazel/platforms/arch:x86": [":dep"],
"//conditions:default": [],
@@ -440,27 +486,27 @@
},
},
{
- description: "embedded props",
- blueprint: `custom {
+ Description: "embedded props",
+ Blueprint: `custom {
name: "embedded_props",
embedded_prop: "abc",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("custom", "embedded_props", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "embedded_props", AttrNameToString{
"embedded_attr": `"abc"`,
}),
},
},
{
- description: "ptr to embedded props",
- blueprint: `custom {
+ Description: "ptr to embedded props",
+ Blueprint: `custom {
name: "ptr_to_embedded_props",
other_embedded_prop: "abc",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("custom", "ptr_to_embedded_props", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "ptr_to_embedded_props", AttrNameToString{
"other_embedded_attr": `"abc"`,
}),
},
@@ -469,8 +515,8 @@
dir := "."
for _, testCase := range testCases {
- t.Run(testCase.description, func(t *testing.T) {
- config := android.TestConfig(buildDir, nil, testCase.blueprint, nil)
+ t.Run(testCase.Description, func(t *testing.T) {
+ config := android.TestConfig(buildDir, nil, testCase.Blueprint, nil)
ctx := android.NewTestContext(config)
registerCustomModuleForBp2buildConversion(ctx)
@@ -484,14 +530,14 @@
return
}
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
android.FailIfErrored(t, err)
- if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
- t.Errorf("Expected %d bazel target (%s),\ngot %d (%s)", expectedCount, testCase.expectedBazelTargets, actualCount, bazelTargets)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.ExpectedBazelTargets); actualCount != expectedCount {
+ t.Errorf("Expected %d bazel target (%s),\ngot %d (%s)", expectedCount, testCase.ExpectedBazelTargets, actualCount, bazelTargets)
} else {
- for i, expectedBazelTarget := range testCase.expectedBazelTargets {
+ for i, expectedBazelTarget := range testCase.ExpectedBazelTargets {
actualBazelTarget := bazelTargets[i]
if actualBazelTarget.content != expectedBazelTarget {
t.Errorf(
@@ -506,6 +552,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
@@ -615,6 +870,7 @@
{
bp: `custom {
name: "bar",
+ host_supported: true,
one_to_many_prop: true,
bazel_module: { bp2build_available: true },
}`,
@@ -639,7 +895,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"})
@@ -647,7 +903,7 @@
_, errs = ctx.ResolveDependencies(config)
android.FailIfErrored(t, errs)
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
android.FailIfErrored(t, err)
if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount {
@@ -675,43 +931,43 @@
}
func TestModuleTypeBp2Build(t *testing.T) {
- testCases := []bp2buildTestCase{
+ testCases := []Bp2buildTestCase{
{
- description: "filegroup with does not specify srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: `filegroup {
+ Description: "filegroup with does not specify srcs",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
name: "fg_foo",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
},
},
{
- description: "filegroup with no srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: `filegroup {
+ Description: "filegroup with no srcs",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
name: "fg_foo",
srcs: [],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
},
},
{
- description: "filegroup with srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: `filegroup {
+ Description: "filegroup with srcs",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
name: "fg_foo",
srcs: ["a", "b"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"a",
"b",
@@ -720,77 +976,44 @@
},
},
{
- description: "filegroup with excludes srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: `filegroup {
+ Description: "filegroup with dot-slash-prefixed srcs",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
+ name: "fg_foo",
+ srcs: ["./a", "./b"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "a",
+ "b",
+ ]`,
+ }),
+ },
+ },
+ {
+ Description: "filegroup with excludes srcs",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
name: "fg_foo",
srcs: ["a", "b"],
exclude_srcs: ["a"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `["b"]`,
}),
},
},
{
- description: "filegroup with glob",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: `filegroup {
- name: "fg_foo",
- srcs: ["**/*.txt"],
- bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
- "srcs": `[
- "other/a.txt",
- "other/b.txt",
- "other/subdir/a.txt",
- ]`,
- }),
- },
- filesystem: map[string]string{
- "other/a.txt": "",
- "other/b.txt": "",
- "other/subdir/a.txt": "",
- "other/file": "",
- },
- },
- {
- description: "filegroup with glob in subdir",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- dir: "other",
- filesystem: map[string]string{
- "other/Android.bp": `filegroup {
- name: "fg_foo",
- srcs: ["**/*.txt"],
- bazel_module: { bp2build_available: true },
-}`,
- "other/a.txt": "",
- "other/b.txt": "",
- "other/subdir/a.txt": "",
- "other/file": "",
- },
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
- "srcs": `[
- "a.txt",
- "b.txt",
- "subdir/a.txt",
- ]`,
- }),
- },
- },
- {
- description: "depends_on_other_dir_module",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: `filegroup {
+ Description: "depends_on_other_dir_module",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
name: "fg_foo",
srcs: [
":foo",
@@ -798,15 +1021,15 @@
],
bazel_module: { bp2build_available: true },
}`,
- filesystem: map[string]string{
+ Filesystem: map[string]string{
"other/Android.bp": `filegroup {
name: "foo",
srcs: ["a", "b"],
bazel_module: { bp2build_available: true },
}`,
},
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"//other:foo",
"c",
@@ -815,11 +1038,11 @@
},
},
{
- description: "depends_on_other_unconverted_module_error",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- unconvertedDepsMode: errorModulesUnconvertedDeps,
- blueprint: `filegroup {
+ Description: "depends_on_other_unconverted_module_error",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ UnconvertedDepsMode: errorModulesUnconvertedDeps,
+ Blueprint: `filegroup {
name: "foobar",
srcs: [
":foo",
@@ -827,25 +1050,46 @@
],
bazel_module: { bp2build_available: true },
}`,
- expectedErr: fmt.Errorf(`"foobar" depends on unconverted modules: foo`),
- filesystem: map[string]string{
+ ExpectedErr: fmt.Errorf(`filegroup .:foobar depends on unconverted modules: foo`),
+ Filesystem: map[string]string{
"other/Android.bp": `filegroup {
name: "foo",
srcs: ["a", "b"],
}`,
},
},
+ {
+ Description: "depends_on_other_missing_module_error",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ UnconvertedDepsMode: errorModulesUnconvertedDeps,
+ Blueprint: `filegroup {
+ name: "foobar",
+ srcs: [
+ "c",
+ "//other:foo",
+ "//other:goo",
+ ],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedErr: fmt.Errorf(`filegroup .:foobar depends on missing modules: //other:goo`),
+ Filesystem: map[string]string{"other/Android.bp": `filegroup {
+ name: "foo",
+ srcs: ["a"],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ },
+ },
}
for _, testCase := range testCases {
- t.Run(testCase.description, func(t *testing.T) {
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, testCase)
+ t.Run(testCase.Description, func(t *testing.T) {
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, testCase)
})
}
}
-type bp2buildMutator = func(android.TopDownMutatorContext)
-
func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
testCases := []struct {
moduleTypeUnderTest string
@@ -889,7 +1133,7 @@
{
description: "generates more than 1 target if needed",
moduleTypeUnderTest: "custom",
- moduleTypeUnderTestFactory: customModuleFactory,
+ moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
bp: `custom {
name: "foo",
one_to_many_prop: true,
@@ -912,7 +1156,7 @@
_, errs = ctx.ResolveDependencies(config)
android.FailIfErrored(t, errs)
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
android.FailIfErrored(t, err)
if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
@@ -931,6 +1175,8 @@
bp2buildConfig allowlists.Bp2BuildConfig
checkDir string
fs map[string]string
+ forceEnabledModules []string
+ expectedErrorMessages []string
}{
{
description: "test bp2build config package and subpackages config",
@@ -993,6 +1239,24 @@
`,
},
},
+ {
+ description: "test force-enabled errors out",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ expectedCount: map[string]int{
+ "migrated": 0,
+ "not_migrated": 0,
+ },
+ bp2buildConfig: allowlists.Bp2BuildConfig{
+ "migrated/but_not_really": allowlists.Bp2BuildDefaultFalse,
+ "not_migrated": allowlists.Bp2BuildDefaultFalse,
+ },
+ fs: map[string]string{
+ "migrated/Android.bp": `filegroup { name: "a" }`,
+ },
+ forceEnabledModules: []string{"a"},
+ expectedErrorMessages: []string{"Force Enabled Module a not converted"},
+ },
}
dir := "."
@@ -1008,6 +1272,7 @@
fs[f] = []byte(content)
}
config := android.TestConfig(buildDir, nil, "", fs)
+ config.AddForceEnabledModules(testCase.forceEnabledModules)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
allowlist := android.NewBp2BuildAllowlist().SetDefaultConfig(testCase.bp2buildConfig)
@@ -1019,12 +1284,12 @@
_, errs = ctx.ResolveDependencies(config)
android.FailIfErrored(t, errs)
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
// For each directory, test that the expected number of generated targets is correct.
for dir, expectedCount := range testCase.expectedCount {
bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
- android.FailIfErrored(t, err)
+ android.CheckErrorsAgainstExpectations(t, err, testCase.expectedErrorMessages)
if actualCount := len(bazelTargets); actualCount != expectedCount {
t.Fatalf(
"%s: Expected %d bazel target for %s package, got %d",
@@ -1039,27 +1304,25 @@
}
func TestCombineBuildFilesBp2buildTargets(t *testing.T) {
- testCases := []bp2buildTestCase{
+ testCases := []Bp2buildTestCase{
{
- description: "filegroup bazel_module.label",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: `filegroup {
+ Description: "filegroup bazel_module.label",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
name: "fg_foo",
bazel_module: { label: "//other:fg_foo" },
}`,
- expectedBazelTargets: []string{
- `// BUILD file`,
- },
- filesystem: map[string]string{
+ ExpectedBazelTargets: []string{},
+ Filesystem: map[string]string{
"other/BUILD.bazel": `// BUILD file`,
},
},
{
- description: "multiple bazel_module.label same BUILD",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: `filegroup {
+ Description: "multiple bazel_module.label same BUILD",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
name: "fg_foo",
bazel_module: { label: "//other:fg_foo" },
}
@@ -1068,20 +1331,18 @@
name: "foo",
bazel_module: { label: "//other:foo" },
}`,
- expectedBazelTargets: []string{
- `// BUILD file`,
- },
- filesystem: map[string]string{
+ ExpectedBazelTargets: []string{},
+ Filesystem: map[string]string{
"other/BUILD.bazel": `// BUILD file`,
},
},
{
- description: "filegroup bazel_module.label and bp2build in subdir",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- dir: "other",
- blueprint: ``,
- filesystem: map[string]string{
+ Description: "filegroup bazel_module.label and bp2build in subdir",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Dir: "other",
+ Blueprint: ``,
+ Filesystem: map[string]string{
"other/Android.bp": `filegroup {
name: "fg_foo",
bazel_module: {
@@ -1096,20 +1357,19 @@
}`,
"other/BUILD.bazel": `// definition for fg_bar`,
},
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
- `// definition for fg_bar`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
},
},
{
- description: "filegroup bazel_module.label and filegroup bp2build",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
+ Description: "filegroup bazel_module.label and filegroup bp2build",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
- filesystem: map[string]string{
+ Filesystem: map[string]string{
"other/BUILD.bazel": `// BUILD file`,
},
- blueprint: `filegroup {
+ Blueprint: `filegroup {
name: "fg_foo",
bazel_module: {
label: "//other:fg_foo",
@@ -1122,29 +1382,28 @@
bp2build_available: true,
},
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_bar", map[string]string{}),
- `// BUILD file`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_bar", map[string]string{}),
},
},
}
dir := "."
for _, testCase := range testCases {
- t.Run(testCase.description, func(t *testing.T) {
+ t.Run(testCase.Description, func(t *testing.T) {
fs := make(map[string][]byte)
toParse := []string{
"Android.bp",
}
- for f, content := range testCase.filesystem {
+ for f, content := range testCase.Filesystem {
if strings.HasSuffix(f, "Android.bp") {
toParse = append(toParse, f)
}
fs[f] = []byte(content)
}
- config := android.TestConfig(buildDir, nil, testCase.blueprint, fs)
+ config := android.TestConfig(buildDir, nil, testCase.Blueprint, fs)
ctx := android.NewTestContext(config)
- ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ ctx.RegisterModuleType(testCase.ModuleTypeUnderTest, testCase.ModuleTypeUnderTestFactory)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, toParse)
@@ -1157,24 +1416,21 @@
}
checkDir := dir
- if testCase.dir != "" {
- checkDir = testCase.dir
+ if testCase.Dir != "" {
+ checkDir = testCase.Dir
}
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
android.FailIfErrored(t, err)
bazelTargets.sort()
actualCount := len(bazelTargets)
- expectedCount := len(testCase.expectedBazelTargets)
+ expectedCount := len(testCase.ExpectedBazelTargets)
if actualCount != expectedCount {
t.Errorf("Expected %d bazel target, got %d\n%s", expectedCount, actualCount, bazelTargets)
}
- if !strings.Contains(bazelTargets.String(), "Section: Handcrafted targets. ") {
- t.Errorf("Expected string representation of bazelTargets to contain handcrafted section header.")
- }
for i, target := range bazelTargets {
actualContent := target.content
- expectedContent := testCase.expectedBazelTargets[i]
+ expectedContent := testCase.ExpectedBazelTargets[i]
if expectedContent != actualContent {
t.Errorf(
"Expected generated Bazel target to be '%s', got '%s'",
@@ -1187,19 +1443,239 @@
}
}
-func TestGlobExcludeSrcs(t *testing.T) {
- testCases := []bp2buildTestCase{
+func TestGlob(t *testing.T) {
+ testCases := []Bp2buildTestCase{
{
- description: "filegroup top level exclude_srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: `filegroup {
+ Description: "filegroup with glob",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
+ name: "fg_foo",
+ srcs: ["**/*.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "other/a.txt",
+ "other/b.txt",
+ "other/subdir/a.txt",
+ ]`,
+ }),
+ },
+ Filesystem: map[string]string{
+ "other/a.txt": "",
+ "other/b.txt": "",
+ "other/subdir/a.txt": "",
+ "other/file": "",
+ },
+ },
+ {
+ Description: "filegroup with glob in subdir",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Dir: "other",
+ Filesystem: map[string]string{
+ "other/Android.bp": `filegroup {
+ name: "fg_foo",
+ srcs: ["**/*.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ "other/a.txt": "",
+ "other/b.txt": "",
+ "other/subdir/a.txt": "",
+ "other/file": "",
+ },
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "a.txt",
+ "b.txt",
+ "subdir/a.txt",
+ ]`,
+ }),
+ },
+ },
+ {
+ Description: "filegroup with glob with no kept BUILD files",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ KeepBuildFileForDirs: []string{
+ // empty
+ },
+ Blueprint: `filegroup {
+ name: "fg_foo",
+ srcs: ["**/*.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ Filesystem: map[string]string{
+ "a.txt": "",
+ "b.txt": "",
+ "foo/BUILD": "",
+ "foo/a.txt": "",
+ "foo/bar/BUILD": "",
+ "foo/bar/b.txt": "",
+ },
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "a.txt",
+ "b.txt",
+ "foo/a.txt",
+ "foo/bar/b.txt",
+ ]`,
+ }),
+ },
+ },
+ {
+ Description: "filegroup with glob with kept BUILD file",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ KeepBuildFileForDirs: []string{
+ "foo",
+ },
+ Blueprint: `filegroup {
+ name: "fg_foo",
+ srcs: ["**/*.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ Filesystem: map[string]string{
+ "a.txt": "",
+ "b.txt": "",
+ "foo/BUILD": "",
+ "foo/a.txt": "",
+ "foo/bar/BUILD": "",
+ "foo/bar/b.txt": "",
+ },
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "a.txt",
+ "b.txt",
+ "//foo:a.txt",
+ "//foo:bar/b.txt",
+ ]`,
+ }),
+ },
+ },
+ {
+ Description: "filegroup with glob with kept BUILD.bazel file",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ KeepBuildFileForDirs: []string{
+ "foo",
+ },
+ Blueprint: `filegroup {
+ name: "fg_foo",
+ srcs: ["**/*.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ Filesystem: map[string]string{
+ "a.txt": "",
+ "b.txt": "",
+ "foo/BUILD.bazel": "",
+ "foo/a.txt": "",
+ "foo/bar/BUILD.bazel": "",
+ "foo/bar/b.txt": "",
+ },
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "a.txt",
+ "b.txt",
+ "//foo:a.txt",
+ "//foo:bar/b.txt",
+ ]`,
+ }),
+ },
+ },
+ {
+ Description: "filegroup with glob with Android.bp file as boundary",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
+ name: "fg_foo",
+ srcs: ["**/*.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ Filesystem: map[string]string{
+ "a.txt": "",
+ "b.txt": "",
+ "foo/Android.bp": "",
+ "foo/a.txt": "",
+ "foo/bar/Android.bp": "",
+ "foo/bar/b.txt": "",
+ },
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "a.txt",
+ "b.txt",
+ "//foo:a.txt",
+ "//foo/bar:b.txt",
+ ]`,
+ }),
+ },
+ },
+ {
+ Description: "filegroup with glob in subdir with kept BUILD and BUILD.bazel file",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Dir: "other",
+ KeepBuildFileForDirs: []string{
+ "other/foo",
+ "other/foo/bar",
+ // deliberately not other/foo/baz/BUILD.
+ },
+ Filesystem: map[string]string{
+ "other/Android.bp": `filegroup {
+ name: "fg_foo",
+ srcs: ["**/*.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ "other/a.txt": "",
+ "other/b.txt": "",
+ "other/foo/BUILD": "",
+ "other/foo/a.txt": "",
+ "other/foo/bar/BUILD.bazel": "",
+ "other/foo/bar/b.txt": "",
+ "other/foo/baz/BUILD": "",
+ "other/foo/baz/c.txt": "",
+ },
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "a.txt",
+ "b.txt",
+ "//other/foo:a.txt",
+ "//other/foo/bar:b.txt",
+ "//other/foo:baz/c.txt",
+ ]`,
+ }),
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.Description, func(t *testing.T) {
+ RunBp2BuildTestCaseSimple(t, testCase)
+ })
+ }
+}
+
+func TestGlobExcludeSrcs(t *testing.T) {
+ testCases := []Bp2buildTestCase{
+ {
+ Description: "filegroup top level exclude_srcs",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `filegroup {
name: "fg_foo",
srcs: ["**/*.txt"],
exclude_srcs: ["c.txt"],
bazel_module: { bp2build_available: true },
}`,
- filesystem: map[string]string{
+ Filesystem: map[string]string{
"a.txt": "",
"b.txt": "",
"c.txt": "",
@@ -1207,8 +1683,8 @@
"dir/e.txt": "",
"dir/f.txt": "",
},
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"a.txt",
"b.txt",
@@ -1219,12 +1695,12 @@
},
},
{
- description: "filegroup in subdir exclude_srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: "",
- dir: "dir",
- filesystem: map[string]string{
+ Description: "filegroup in subdir exclude_srcs",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: "",
+ Dir: "dir",
+ Filesystem: map[string]string{
"dir/Android.bp": `filegroup {
name: "fg_foo",
srcs: ["**/*.txt"],
@@ -1238,8 +1714,8 @@
"dir/subdir/e.txt": "",
"dir/subdir/f.txt": "",
},
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"srcs": `[
"a.txt",
"//dir/subdir:e.txt",
@@ -1251,35 +1727,51 @@
}
for _, testCase := range testCases {
- t.Run(testCase.description, func(t *testing.T) {
- runBp2BuildTestCaseSimple(t, testCase)
+ t.Run(testCase.Description, func(t *testing.T) {
+ RunBp2BuildTestCaseSimple(t, testCase)
})
}
}
func TestCommonBp2BuildModuleAttrs(t *testing.T) {
- testCases := []bp2buildTestCase{
+ testCases := []Bp2buildTestCase{
{
- description: "Required into data test",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + `
+ Description: "Required into data test",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + `
filegroup {
name: "fg_foo",
required: ["reqd"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"data": `[":reqd"]`,
}),
},
},
{
- description: "Required via arch into data test",
- moduleTypeUnderTest: "python_library",
- moduleTypeUnderTestFactory: python.PythonLibraryFactory,
- blueprint: simpleModuleDoNotConvertBp2build("python_library", "reqdx86") +
+ Description: "Required into data test, cyclic self reference is filtered out",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + `
+filegroup {
+ name: "fg_foo",
+ required: ["reqd", "fg_foo"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+ "data": `[":reqd"]`,
+ }),
+ },
+ },
+ {
+ Description: "Required via arch into data test",
+ ModuleTypeUnderTest: "python_library",
+ ModuleTypeUnderTestFactory: python.PythonLibraryFactory,
+ Blueprint: simpleModuleDoNotConvertBp2build("python_library", "reqdx86") +
simpleModuleDoNotConvertBp2build("python_library", "reqdarm") + `
python_library {
name: "fg_foo",
@@ -1293,54 +1785,56 @@
},
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("py_library", "fg_foo", map[string]string{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_library", "fg_foo", map[string]string{
"data": `select({
"//build/bazel/platforms/arch:arm": [":reqdarm"],
"//build/bazel/platforms/arch:x86": [":reqdx86"],
"//conditions:default": [],
})`,
"srcs_version": `"PY3"`,
+ "imports": `["."]`,
}),
},
},
{
- description: "Required appended to data test",
- moduleTypeUnderTest: "python_library",
- moduleTypeUnderTestFactory: python.PythonLibraryFactory,
- filesystem: map[string]string{
+ Description: "Required appended to data test",
+ ModuleTypeUnderTest: "python_library",
+ ModuleTypeUnderTestFactory: python.PythonLibraryFactory,
+ Filesystem: map[string]string{
"data.bin": "",
"src.py": "",
},
- blueprint: simpleModuleDoNotConvertBp2build("python_library", "reqd") + `
+ Blueprint: simpleModuleDoNotConvertBp2build("python_library", "reqd") + `
python_library {
name: "fg_foo",
data: ["data.bin"],
required: ["reqd"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("py_library", "fg_foo", map[string]string{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_library", "fg_foo", map[string]string{
"data": `[
"data.bin",
":reqd",
]`,
"srcs_version": `"PY3"`,
+ "imports": `["."]`,
}),
},
},
{
- description: "All props-to-attrs at once together test",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + `
+ Description: "All props-to-attrs at once together test",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + `
filegroup {
name: "fg_foo",
required: ["reqd"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
"data": `[":reqd"]`,
}),
},
@@ -1348,8 +1842,59 @@
}
for _, tc := range testCases {
- t.Run(tc.description, func(t *testing.T) {
- runBp2BuildTestCaseSimple(t, tc)
+ t.Run(tc.Description, func(t *testing.T) {
+ RunBp2BuildTestCaseSimple(t, tc)
})
}
}
+
+func TestLicensesAttrConversion(t *testing.T) {
+ RunBp2BuildTestCase(t,
+ func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("license", android.LicenseFactory)
+ },
+ Bp2buildTestCase{
+ Description: "Test that licenses: attribute is converted",
+ ModuleTypeUnderTest: "filegroup",
+ ModuleTypeUnderTestFactory: android.FileGroupFactory,
+ Blueprint: `
+license {
+ name: "my_license",
+}
+filegroup {
+ name: "my_filegroup",
+ licenses: ["my_license"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "my_filegroup", AttrNameToString{
+ "applicable_licenses": `[":my_license"]`,
+ }),
+ MakeBazelTargetNoRestrictions("android_license", "my_license", AttrNameToString{}),
+ },
+ })
+}
+
+func TestGenerateApiBazelTargets(t *testing.T) {
+ bp := `
+ custom {
+ name: "foo",
+ api: "foo.txt",
+ }
+ `
+ expectedBazelTarget := MakeBazelTarget(
+ "custom_api_contribution",
+ "foo",
+ AttrNameToString{
+ "api": `"foo.txt"`,
+ },
+ )
+ registerCustomModule := func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
+ }
+ RunApiBp2BuildTestCase(t, registerCustomModule, Bp2buildTestCase{
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{expectedBazelTarget},
+ Description: "Generating API contribution Bazel targets for custom module",
+ })
+}
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
index 992cc1c..e774fdf 100644
--- a/bp2build/bzl_conversion.go
+++ b/bp2build/bzl_conversion.go
@@ -83,7 +83,7 @@
func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
var loadStmts string
var moduleRuleMap string
- for _, bzlFileName := range android.SortedStringKeys(bzlLoads) {
+ for _, bzlFileName := range android.SortedKeys(bzlLoads) {
loadStmt := "load(\"//build/bazel/queryview_rules:"
loadStmt += bzlFileName
loadStmt += ".bzl\""
@@ -104,7 +104,7 @@
rules := make(map[string][]rule)
// TODO: allow registration of a bzl rule when registring a factory
- for _, moduleType := range android.SortedStringKeys(moduleTypeFactories) {
+ for _, moduleType := range android.SortedKeys(moduleTypeFactories) {
factory := moduleTypeFactories[moduleType]
factoryName := runtime.FuncForPC(reflect.ValueOf(factory).Pointer()).Name()
pkg := strings.Split(factoryName, ".")[0]
@@ -221,7 +221,7 @@
}
properties := make([]property, 0, len(propertiesByName))
- for _, key := range android.SortedStringKeys(propertiesByName) {
+ for _, key := range android.SortedKeys(propertiesByName) {
properties = append(properties, propertiesByName[key])
}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index f3345a6..a8e557d 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -15,11 +15,12 @@
package bp2build
import (
- "android/soong/android"
"io/ioutil"
"os"
"strings"
"testing"
+
+ "android/soong/android"
)
func setUp() {
@@ -84,6 +85,7 @@
"soong_module_name": attr.string(mandatory = True),
"soong_module_variant": attr.string(),
"soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "api": attr.string(),
"arch_paths": attr.string_list(),
"arch_paths_exclude": attr.string_list(),
# bazel_module start
@@ -103,6 +105,7 @@
"one_to_many_prop": attr.bool(),
"other_embedded_prop": attr.string(),
"string_list_prop": attr.string_list(),
+ "string_literal_prop": attr.string(),
"string_prop": attr.string(),
"string_ptr_prop": attr.string(),
},
@@ -117,6 +120,7 @@
"soong_module_name": attr.string(mandatory = True),
"soong_module_variant": attr.string(),
"soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "api": attr.string(),
"arch_paths": attr.string_list(),
"arch_paths_exclude": attr.string_list(),
"bool_prop": attr.bool(),
@@ -132,6 +136,7 @@
"one_to_many_prop": attr.bool(),
"other_embedded_prop": attr.string(),
"string_list_prop": attr.string_list(),
+ "string_literal_prop": attr.string(),
"string_prop": attr.string(),
"string_ptr_prop": attr.string(),
},
@@ -146,6 +151,7 @@
"soong_module_name": attr.string(mandatory = True),
"soong_module_variant": attr.string(),
"soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "api": attr.string(),
"arch_paths": attr.string_list(),
"arch_paths_exclude": attr.string_list(),
"bool_prop": attr.bool(),
@@ -161,6 +167,7 @@
"one_to_many_prop": attr.bool(),
"other_embedded_prop": attr.string(),
"string_list_prop": attr.string_list(),
+ "string_literal_prop": attr.string(),
"string_prop": attr.string(),
"string_ptr_prop": attr.string(),
# test_prop start
@@ -189,7 +196,7 @@
content: "irrelevant",
},
}
- files := CreateBazelFiles(ruleShims, make(map[string]BazelTargets), QueryView)
+ files := CreateBazelFiles(android.NullConfig("out", "out/soong"), ruleShims, make(map[string]BazelTargets), QueryView)
var actualSoongModuleBzl BazelFile
for _, f := range files {
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index 037564b..0315732 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -31,19 +31,21 @@
type testBazelTarget struct {
typ string
name string
- attrs attrNameToString
+ 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
}
type ccBinaryBp2buildTestCase struct {
description string
+ filesystem map[string]string
blueprint string
targets []testBazelTarget
}
@@ -65,43 +67,36 @@
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),
+ Filesystem: testCase.filesystem,
+ })
})
}
-func runCcHostBinaryTestCase(t *testing.T, tc ccBinaryBp2buildTestCase) {
+func runCcHostBinaryTestCase(t *testing.T, testCase ccBinaryBp2buildTestCase) {
t.Helper()
- testCase := tc
- for i, tar := range testCase.targets {
- switch tar.typ {
- case "cc_binary", "proto_library", "cc_lite_proto_library":
- 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) {
- runBp2BuildTestCase(t, registerCcBinaryModuleTypes, bp2buildTestCase{
- expectedBazelTargets: generateBazelTargetsForTest(testCase.targets),
- moduleTypeUnderTest: moduleTypeUnderTest,
- moduleTypeUnderTestFactory: cc.BinaryHostFactory,
- description: fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
- blueprint: hostBinaryReplacer.Replace(testCase.blueprint),
+ description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description)
+ t.Run(description, func(t *testing.T) {
+ RunBp2BuildTestCase(t, registerCcBinaryModuleTypes, Bp2buildTestCase{
+ ExpectedBazelTargets: generateBazelTargetsForTest(testCase.targets, android.HostSupported),
+ ModuleTypeUnderTest: moduleTypeUnderTest,
+ ModuleTypeUnderTestFactory: cc.BinaryHostFactory,
+ Description: description,
+ Blueprint: hostBinaryReplacer.Replace(testCase.blueprint),
+ Filesystem: testCase.filesystem,
})
})
}
@@ -109,6 +104,9 @@
func TestBasicCcBinary(t *testing.T) {
runCcBinaryTests(t, ccBinaryBp2buildTestCase{
description: "basic -- properties -> attrs with little/no transformation",
+ filesystem: map[string]string{
+ soongCcVersionLibBpPath: soongCcVersionLibBp,
+ },
blueprint: `
{rule_name} {
name: "foo",
@@ -134,7 +132,7 @@
}
`,
targets: []testBazelTarget{
- {"cc_binary", "foo", attrNameToString{
+ {"cc_binary", "foo", AttrNameToString{
"absolute_includes": `["absolute_dir"]`,
"asflags": `["-Dasflag"]`,
"conlyflags": `["-Dconlyflag"]`,
@@ -154,9 +152,10 @@
"keep_symbols_list": ["symbol"],
"none": True,
}`,
- "sdk_version": `"current"`,
- "min_sdk_version": `"29"`,
- "use_version_lib": `True`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
+ "use_version_lib": `True`,
+ "whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
},
},
},
@@ -174,7 +173,7 @@
}
`,
targets: []testBazelTarget{
- {"cc_binary", "foo", attrNameToString{
+ {"cc_binary", "foo", AttrNameToString{
"features": `["-static_flag"]`,
"linkopts": `["-shared"]`,
},
@@ -194,7 +193,7 @@
}
`,
targets: []testBazelTarget{
- {"cc_binary", "foo", attrNameToString{
+ {"cc_binary", "foo", AttrNameToString{
"linkshared": `False`,
},
},
@@ -202,26 +201,65 @@
})
}
-func TestCcBinaryVersionScript(t *testing.T) {
+func TestCcBinaryVersionScriptAndDynamicList(t *testing.T) {
runCcBinaryTests(t, ccBinaryBp2buildTestCase{
- description: `version script`,
+ description: `version script and dynamic list`,
blueprint: `
{rule_name} {
name: "foo",
include_build_directory: false,
version_script: "vs",
+ dynamic_list: "dynamic.list",
}
`,
targets: []testBazelTarget{
- {"cc_binary", "foo", attrNameToString{
- "additional_linker_inputs": `["vs"]`,
- "linkopts": `["-Wl,--version-script,$(location vs)"]`,
+ {"cc_binary", "foo", AttrNameToString{
+ "additional_linker_inputs": `[
+ "vs",
+ "dynamic.list",
+ ]`,
+ "linkopts": `[
+ "-Wl,--version-script,$(location vs)",
+ "-Wl,--dynamic-list,$(location dynamic.list)",
+ ]`,
},
},
},
})
}
+func TestCcBinaryLdflagsSplitBySpaceExceptSoongAdded(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ ldflags: [
+ "--nospace_flag",
+ "-z spaceflag",
+ ],
+ version_script: "version_script",
+ dynamic_list: "dynamic.list",
+ include_build_directory: false,
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "additional_linker_inputs": `[
+ "version_script",
+ "dynamic.list",
+ ]`,
+ "linkopts": `[
+ "--nospace_flag",
+ "-z",
+ "spaceflag",
+ "-Wl,--version-script,$(location version_script)",
+ "-Wl,--dynamic-list,$(location dynamic.list)",
+ ]`,
+ }}},
+ })
+}
+
func TestCcBinarySplitSrcsByLang(t *testing.T) {
runCcHostBinaryTestCase(t, ccBinaryBp2buildTestCase{
description: "split srcs by lang",
@@ -238,7 +276,7 @@
}
` + simpleModuleDoNotConvertBp2build("filegroup", "fg_foo"),
targets: []testBazelTarget{
- {"cc_binary", "foo", attrNameToString{
+ {"cc_binary", "foo", AttrNameToString{
"srcs": `[
"cpponly.cpp",
":fg_foo_cpp_srcs",
@@ -293,7 +331,7 @@
simpleModuleDoNotConvertBp2build("cc_library", "shared_dep") +
simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep"),
targets: []testBazelTarget{
- {"cc_binary", "foo", attrNameToString{
+ {"cc_binary", "foo", AttrNameToString{
"deps": `[
":implementation_static_dep",
":static_dep",
@@ -322,21 +360,21 @@
baseTestCases := []struct {
description string
soongProperty string
- bazelAttr attrNameToString
+ bazelAttr AttrNameToString
}{
{
description: "nocrt: true",
soongProperty: `nocrt: true,`,
- bazelAttr: attrNameToString{"link_crt": `False`},
+ bazelAttr: AttrNameToString{"features": `["-link_crt"]`},
},
{
description: "nocrt: false",
soongProperty: `nocrt: false,`,
- bazelAttr: attrNameToString{},
+ bazelAttr: AttrNameToString{},
},
{
description: "nocrt: not set",
- bazelAttr: attrNameToString{},
+ bazelAttr: AttrNameToString{},
},
}
@@ -365,21 +403,21 @@
baseTestCases := []struct {
description string
soongProperty string
- bazelAttr attrNameToString
+ bazelAttr AttrNameToString
}{
{
description: "no_libcrt: true",
soongProperty: `no_libcrt: true,`,
- bazelAttr: attrNameToString{"use_libcrt": `False`},
+ bazelAttr: AttrNameToString{"features": `["-use_libcrt"]`},
},
{
description: "no_libcrt: false",
soongProperty: `no_libcrt: false,`,
- bazelAttr: attrNameToString{"use_libcrt": `True`},
+ bazelAttr: AttrNameToString{},
},
{
description: "no_libcrt: not set",
- bazelAttr: attrNameToString{},
+ bazelAttr: AttrNameToString{},
},
}
@@ -408,35 +446,35 @@
baseTestCases := []struct {
description string
soongProperty string
- bazelAttr attrNameToString
+ bazelAttr AttrNameToString
}{
{
description: "pack_relocation: true",
soongProperty: `pack_relocations: true,`,
- bazelAttr: attrNameToString{},
+ bazelAttr: AttrNameToString{},
},
{
description: "pack_relocations: false",
soongProperty: `pack_relocations: false,`,
- bazelAttr: attrNameToString{"features": `["disable_pack_relocations"]`},
+ bazelAttr: AttrNameToString{"features": `["disable_pack_relocations"]`},
},
{
description: "pack_relocations: not set",
- bazelAttr: attrNameToString{},
+ bazelAttr: AttrNameToString{},
},
{
description: "pack_relocation: true",
soongProperty: `allow_undefined_symbols: true,`,
- bazelAttr: attrNameToString{"features": `["-no_undefined_symbols"]`},
+ bazelAttr: AttrNameToString{"features": `["-no_undefined_symbols"]`},
},
{
description: "allow_undefined_symbols: false",
soongProperty: `allow_undefined_symbols: false,`,
- bazelAttr: attrNameToString{},
+ bazelAttr: AttrNameToString{},
},
{
description: "allow_undefined_symbols: not set",
- bazelAttr: attrNameToString{},
+ bazelAttr: AttrNameToString{},
},
}
@@ -470,11 +508,11 @@
include_build_directory: false,
}`,
targets: []testBazelTarget{
- {"proto_library", "foo_proto", attrNameToString{
+ {"proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
- }}, {"cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ }}, {"cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[":foo_proto"]`,
- }}, {"cc_binary", "foo", attrNameToString{
+ }}, {"cc_binary", "foo", AttrNameToString{
"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":foo_cc_proto_lite"]`,
}},
@@ -493,11 +531,11 @@
include_build_directory: false,
}`,
targets: []testBazelTarget{
- {"proto_library", "foo_proto", attrNameToString{
+ {"proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
- }}, {"cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ }}, {"cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[":foo_proto"]`,
- }}, {"cc_binary", "foo", attrNameToString{
+ }}, {"cc_binary", "foo", AttrNameToString{
"deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":foo_cc_proto_lite"]`,
"linkshared": `False`,
@@ -505,3 +543,456 @@
},
})
}
+
+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",
+ ]`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryRuntimeLibs(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary with runtime libs",
+ blueprint: `
+cc_library {
+ name: "bar",
+ srcs: ["b.cc"],
+}
+
+{rule_name} {
+ name: "foo",
+ srcs: ["a.cc"],
+ runtime_libs: ["bar"],
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_library_static", "bar_bp2build_cc_library_static", AttrNameToString{
+ "local_includes": `["."]`,
+ "srcs": `["b.cc"]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ },
+ },
+ {"cc_library_shared", "bar", AttrNameToString{
+ "local_includes": `["."]`,
+ "srcs": `["b.cc"]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ },
+ },
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "srcs": `["a.cc"]`,
+ "runtime_deps": `[":bar"]`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcBinaryWithInstructionSet(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: "instruction set",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ arch: {
+ arm: {
+ instruction_set: "arm",
+ }
+ }
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/arch:arm": [
+ "arm_isa_arm",
+ "-arm_isa_thumb",
+ ],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryEmptySuffix(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: "binary with empty suffix",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ suffix: "",
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "suffix": `""`,
+ }},
+ },
+ })
+}
+
+func TestCcBinarySuffix(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: "binary with suffix",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ suffix: "-suf",
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "suffix": `"-suf"`,
+ }},
+ },
+ })
+}
+
+func TestCcArchVariantBinarySuffix(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: "binary with suffix",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ arch: {
+ arm64: { suffix: "-64" },
+ arm: { suffix: "-32" },
+ },
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "suffix": `select({
+ "//build/bazel/platforms/arch:arm": "-32",
+ "//build/bazel/platforms/arch:arm64": "-64",
+ "//conditions:default": None,
+ })`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithSyspropSrcs(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary with sysprop sources",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ srcs: [
+ "bar.sysprop",
+ "baz.sysprop",
+ "blah.cpp",
+ ],
+ min_sdk_version: "5",
+}`,
+ targets: []testBazelTarget{
+ {"sysprop_library", "foo_sysprop_library", AttrNameToString{
+ "srcs": `[
+ "bar.sysprop",
+ "baz.sysprop",
+ ]`,
+ }},
+ {"cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{
+ "dep": `":foo_sysprop_library"`,
+ "min_sdk_version": `"5"`,
+ }},
+ {"cc_binary", "foo", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithSyspropSrcsSomeConfigs(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary with sysprop sources in some configs but not others",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ srcs: [
+ "blah.cpp",
+ ],
+ target: {
+ android: {
+ srcs: ["bar.sysprop"],
+ },
+ },
+ min_sdk_version: "5",
+}`,
+ targets: []testBazelTarget{
+ {"sysprop_library", "foo_sysprop_library", AttrNameToString{
+ "srcs": `select({
+ "//build/bazel/platforms/os:android": ["bar.sysprop"],
+ "//conditions:default": [],
+ })`,
+ }},
+ {"cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{
+ "dep": `":foo_sysprop_library"`,
+ "min_sdk_version": `"5"`,
+ }},
+ {"cc_binary", "foo", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `select({
+ "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"],
+ "//conditions:default": [],
+ })`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithIntegerOverflowProperty(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary with integer overflow property specified",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ sanitize: {
+ integer_overflow: true,
+ },
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `["ubsan_integer_overflow"]`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithMiscUndefinedProperty(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary with miscellaneous properties specified",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ sanitize: {
+ misc_undefined: ["undefined", "nullability"],
+ },
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ]`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithUBSanPropertiesArchSpecific(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary has correct feature select when UBSan props are specified in arch specific blocks",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ sanitize: {
+ misc_undefined: ["undefined", "nullability"],
+ },
+ target: {
+ android: {
+ sanitize: {
+ misc_undefined: ["alignment"],
+ },
+ },
+ linux_glibc: {
+ sanitize: {
+ integer_overflow: true,
+ },
+ },
+ },
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ] + select({
+ "//build/bazel/platforms/os:android": ["ubsan_alignment"],
+ "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"],
+ "//conditions:default": [],
+ })`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithThinLto(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary has correct features when thin LTO is enabled",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ lto: {
+ thin: true,
+ },
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `["android_thin_lto"]`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithLtoNever(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary has correct features when LTO is explicitly disabled",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ lto: {
+ never: true,
+ },
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `["-android_thin_lto"]`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithThinLtoArchSpecific(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary has correct features when LTO differs across arch and os variants",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ },
+ },
+ },
+ arch: {
+ riscv64: {
+ lto: {
+ thin: false,
+ },
+ },
+ },
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+ "//conditions:default": [],
+ })`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary has correct features when LTO disabled by default but enabled on a particular variant",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ lto: {
+ never: true,
+ },
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ never: false,
+ },
+ },
+ },
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["android_thin_lto"],
+ "//conditions:default": ["-android_thin_lto"],
+ })`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryWithThinLtoAndWholeProgramVtables(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary has correct features when thin LTO is enabled with whole_program_vtables",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ lto: {
+ thin: true,
+ },
+ whole_program_vtables: true,
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `[
+ "android_thin_lto",
+ "android_thin_lto_whole_program_vtables",
+ ]`,
+ }},
+ },
+ })
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 2775a10..7165ac4 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -27,7 +27,16 @@
soongCcLibraryPreamble = `
cc_defaults {
name: "linux_bionic_supported",
-}`
+}
+`
+
+ soongCcVersionLibBpPath = "build/soong/cc/libbuildversion/Android.bp"
+ soongCcVersionLibBp = `
+cc_library_static {
+ name: "libbuildversion",
+ bazel_module: { bp2build_available: false },
+}
+`
soongCcProtoLibraries = `
cc_library {
@@ -43,9 +52,9 @@
soongCcProtoPreamble = soongCcLibraryPreamble + soongCcProtoLibraries
)
-func runCcLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+func runCcLibraryTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, registerCcLibraryModuleTypes, tc)
+ RunBp2BuildTestCase(t, registerCcLibraryModuleTypes, tc)
}
func registerCcLibraryModuleTypes(ctx android.RegistrationContext) {
@@ -57,14 +66,15 @@
}
func TestCcLibrarySimple(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - simple example",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
- "android.cpp": "",
- "bionic.cpp": "",
- "darwin.cpp": "",
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library - simple example",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ soongCcVersionLibBpPath: soongCcVersionLibBp,
+ "android.cpp": "",
+ "bionic.cpp": "",
+ "darwin.cpp": "",
// Refer to cc.headerExts for the supported header extensions in Soong.
"header.h": "",
"header.hh": "",
@@ -81,7 +91,7 @@
"x86_64.cpp": "",
"foo-dir/a.h": "",
},
- blueprint: soongCcLibraryPreamble +
+ Blueprint: soongCcLibraryPreamble +
simpleModuleDoNotConvertBp2build("cc_library_headers", "some-headers") + `
cc_library {
name: "foo-lib",
@@ -120,7 +130,7 @@
use_version_lib: true,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
"copts": `["-Wall"]`,
"export_includes": `["foo-dir"]`,
"implementation_deps": `[":some-headers"]`,
@@ -139,30 +149,31 @@
"android.cpp",
],
"//build/bazel/platforms/os:darwin": ["darwin.cpp"],
- "//build/bazel/platforms/os:linux": ["linux.cpp"],
"//build/bazel/platforms/os:linux_bionic": ["bionic.cpp"],
+ "//build/bazel/platforms/os:linux_glibc": ["linux.cpp"],
"//conditions:default": [],
})`,
- "sdk_version": `"current"`,
- "min_sdk_version": `"29"`,
- "use_version_lib": `True`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
+ "use_version_lib": `True`,
+ "implementation_whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
}),
})
}
func TestCcLibraryTrimmedLdAndroid(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - trimmed example of //bionic/linker:ld-android",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library - trimmed example of //bionic/linker:ld-android",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"ld-android.cpp": "",
"linked_list.h": "",
"linker.h": "",
"linker_block_allocator.h": "",
"linker_cfi.h": "",
},
- blueprint: soongCcLibraryPreamble +
+ Blueprint: soongCcLibraryPreamble +
simpleModuleDoNotConvertBp2build("cc_library_headers", "libc_headers") + `
cc_library {
name: "fake-ld-android",
@@ -193,7 +204,7 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("fake-ld-android", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("fake-ld-android", AttrNameToString{
"srcs": `["ld_android.cpp"]`,
"copts": `[
"-Wall",
@@ -219,12 +230,12 @@
}
func TestCcLibraryExcludeSrcs(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- dir: "external",
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Dir: "external",
+ Filesystem: map[string]string{
"external/math/cosf.c": "",
"external/math/erf.c": "",
"external/math/erf_data.c": "",
@@ -257,8 +268,8 @@
}
`,
},
- blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: makeCcLibraryTargets("fake-libarm-optimized-routines-math", attrNameToString{
+ Blueprint: soongCcLibraryPreamble,
+ ExpectedBazelTargets: makeCcLibraryTargets("fake-libarm-optimized-routines-math", AttrNameToString{
"copts": `select({
"//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"],
"//conditions:default": [],
@@ -270,16 +281,16 @@
}
func TestCcLibrarySharedStaticProps(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library shared/static props",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library shared/static props",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"both.cpp": "",
"sharedonly.cpp": "",
"staticonly.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "a",
srcs: ["both.cpp"],
@@ -354,8 +365,8 @@
bazel_module: { bp2build_available: false },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
"copts": `[
"bothflag",
"staticflag",
@@ -377,7 +388,7 @@
":whole_and_static_lib_for_both",
":whole_static_lib_for_static",
]`}),
- makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
"copts": `[
"bothflag",
"sharedflag",
@@ -405,16 +416,16 @@
}
func TestCcLibraryDeps(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library shared/static props",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library shared/static props",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"both.cpp": "",
"sharedonly.cpp": "",
"staticonly.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "a",
srcs: ["both.cpp"],
@@ -462,8 +473,8 @@
simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep_for_static") +
simpleModuleDoNotConvertBp2build("cc_library", "shared_dep_for_both") +
simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep_for_both"),
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
"copts": `[
"bothflag",
"staticflag",
@@ -495,7 +506,7 @@
":whole_static_dep_for_static",
]`,
}),
- makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
"copts": `[
"bothflag",
"sharedflag",
@@ -532,11 +543,11 @@
}
func TestCcLibraryWholeStaticLibsAlwaysLink(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- dir: "foo/bar",
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Dir: "foo/bar",
+ Filesystem: map[string]string{
"foo/bar/Android.bp": `
cc_library {
name: "a",
@@ -558,15 +569,15 @@
cc_prebuilt_library_static { name: "whole_static_lib_for_both" }
`,
},
- blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ Blueprint: soongCcLibraryPreamble,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
"whole_archive_deps": `[
":whole_static_lib_for_both_alwayslink",
":whole_static_lib_for_static_alwayslink",
]`,
}),
- makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
"whole_archive_deps": `[
":whole_static_lib_for_both_alwayslink",
":whole_static_lib_for_shared_alwayslink",
@@ -578,12 +589,12 @@
}
func TestCcLibrarySharedStaticPropsInArch(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library shared/static props in arch",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- dir: "foo/bar",
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library shared/static props in arch",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Dir: "foo/bar",
+ Filesystem: map[string]string{
"foo/bar/arm.cpp": "",
"foo/bar/x86.cpp": "",
"foo/bar/sharedonly.cpp": "",
@@ -652,9 +663,9 @@
cc_library_static { name: "android_dep_for_shared" }
`,
},
- blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ Blueprint: soongCcLibraryPreamble,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
"copts": `[
"bothflag",
"staticflag",
@@ -678,7 +689,7 @@
"//conditions:default": [],
})`,
}),
- makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
"copts": `[
"bothflag",
"sharedflag",
@@ -728,12 +739,12 @@
}
func TestCcLibrarySharedStaticPropsWithMixedSources(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library shared/static props with c/cpp/s mixed sources",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- dir: "foo/bar",
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library shared/static props with c/cpp/s mixed sources",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Dir: "foo/bar",
+ Filesystem: map[string]string{
"foo/bar/both_source.cpp": "",
"foo/bar/both_source.cc": "",
"foo/bar/both_source.c": "",
@@ -805,9 +816,9 @@
}
`,
},
- blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ Blueprint: soongCcLibraryPreamble,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
"local_includes": `["."]`,
"srcs": `[
"both_source.cpp",
@@ -832,7 +843,7 @@
":static_filegroup_c_srcs",
]`,
}),
- makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
"local_includes": `["."]`,
"srcs": `[
"both_source.cpp",
@@ -859,40 +870,47 @@
})}})
}
-func TestCcLibraryNonConfiguredVersionScript(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library non-configured version script",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- dir: "foo/bar",
- filesystem: map[string]string{
+func TestCcLibraryNonConfiguredVersionScriptAndDynamicList(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library non-configured version script and dynamic list",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Dir: "foo/bar",
+ Filesystem: map[string]string{
"foo/bar/Android.bp": `
cc_library {
name: "a",
srcs: ["a.cpp"],
version_script: "v.map",
+ dynamic_list: "dynamic.list",
bazel_module: { bp2build_available: true },
include_build_directory: false,
}
`,
},
- blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
- "additional_linker_inputs": `["v.map"]`,
- "linkopts": `["-Wl,--version-script,$(location v.map)"]`,
- "srcs": `["a.cpp"]`,
+ Blueprint: soongCcLibraryPreamble,
+ ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
+ "additional_linker_inputs": `[
+ "v.map",
+ "dynamic.list",
+ ]`,
+ "linkopts": `[
+ "-Wl,--version-script,$(location v.map)",
+ "-Wl,--dynamic-list,$(location dynamic.list)",
+ ]`,
+ "srcs": `["a.cpp"]`,
}),
},
)
}
-func TestCcLibraryConfiguredVersionScript(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library configured version script",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- dir: "foo/bar",
- filesystem: map[string]string{
+func TestCcLibraryConfiguredVersionScriptAndDynamicList(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library configured version script and dynamic list",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Dir: "foo/bar",
+ Filesystem: map[string]string{
"foo/bar/Android.bp": `
cc_library {
name: "a",
@@ -900,9 +918,11 @@
arch: {
arm: {
version_script: "arm.map",
+ dynamic_list: "dynamic_arm.list",
},
arm64: {
version_script: "arm64.map",
+ dynamic_list: "dynamic_arm64.list",
},
},
@@ -911,16 +931,28 @@
}
`,
},
- blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ Blueprint: soongCcLibraryPreamble,
+ ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
"additional_linker_inputs": `select({
- "//build/bazel/platforms/arch:arm": ["arm.map"],
- "//build/bazel/platforms/arch:arm64": ["arm64.map"],
+ "//build/bazel/platforms/arch:arm": [
+ "arm.map",
+ "dynamic_arm.list",
+ ],
+ "//build/bazel/platforms/arch:arm64": [
+ "arm64.map",
+ "dynamic_arm64.list",
+ ],
"//conditions:default": [],
})`,
"linkopts": `select({
- "//build/bazel/platforms/arch:arm": ["-Wl,--version-script,$(location arm.map)"],
- "//build/bazel/platforms/arch:arm64": ["-Wl,--version-script,$(location arm64.map)"],
+ "//build/bazel/platforms/arch:arm": [
+ "-Wl,--version-script,$(location arm.map)",
+ "-Wl,--dynamic-list,$(location dynamic_arm.list)",
+ ],
+ "//build/bazel/platforms/arch:arm64": [
+ "-Wl,--version-script,$(location arm64.map)",
+ "-Wl,--dynamic-list,$(location dynamic_arm64.list)",
+ ],
"//conditions:default": [],
})`,
"srcs": `["a.cpp"]`,
@@ -929,12 +961,52 @@
)
}
+func TestCcLibraryLdflagsSplitBySpaceExceptSoongAdded(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "version_script": "",
+ "dynamic.list": "",
+ },
+ Blueprint: `
+cc_library {
+ name: "foo",
+ ldflags: [
+ "--nospace_flag",
+ "-z spaceflag",
+ ],
+ version_script: "version_script",
+ dynamic_list: "dynamic.list",
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "additional_linker_inputs": `[
+ "version_script",
+ "dynamic.list",
+ ]`,
+ "linkopts": `[
+ "--nospace_flag",
+ "-z",
+ "spaceflag",
+ "-Wl,--version-script,$(location version_script)",
+ "-Wl,--dynamic-list,$(location dynamic.list)",
+ ]`,
+ }),
+ },
+ })
+}
+
func TestCcLibrarySharedLibs(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library shared_libs",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library shared_libs",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "mylib",
bazel_module: { bp2build_available: false },
@@ -946,7 +1018,7 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
"implementation_dynamic_deps": `[":mylib"]`,
}),
},
@@ -955,14 +1027,15 @@
func TestCcLibraryFeatures(t *testing.T) {
expected_targets := []string{}
- expected_targets = append(expected_targets, makeCcLibraryTargets("a", attrNameToString{
+ expected_targets = append(expected_targets, makeCcLibraryTargets("a", AttrNameToString{
"features": `[
"disable_pack_relocations",
"-no_undefined_symbols",
]`,
- "srcs": `["a.cpp"]`,
+ "native_coverage": `False`,
+ "srcs": `["a.cpp"]`,
})...)
- expected_targets = append(expected_targets, makeCcLibraryTargets("b", attrNameToString{
+ expected_targets = append(expected_targets, makeCcLibraryTargets("b", AttrNameToString{
"features": `select({
"//build/bazel/platforms/arch:x86_64": [
"disable_pack_relocations",
@@ -970,9 +1043,10 @@
],
"//conditions:default": [],
})`,
- "srcs": `["b.cpp"]`,
+ "native_coverage": `False`,
+ "srcs": `["b.cpp"]`,
})...)
- expected_targets = append(expected_targets, makeCcLibraryTargets("c", attrNameToString{
+ expected_targets = append(expected_targets, makeCcLibraryTargets("c", AttrNameToString{
"features": `select({
"//build/bazel/platforms/os:darwin": [
"disable_pack_relocations",
@@ -983,17 +1057,18 @@
"srcs": `["c.cpp"]`,
})...)
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library pack_relocations test",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library pack_relocations test",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "a",
srcs: ["a.cpp"],
pack_relocations: false,
allow_undefined_symbols: true,
include_build_directory: false,
+ native_coverage: false,
}
cc_library {
@@ -1006,6 +1081,7 @@
},
},
include_build_directory: false,
+ native_coverage: false,
}
cc_library {
@@ -1019,23 +1095,23 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: expected_targets,
+ ExpectedBazelTargets: expected_targets,
})
}
func TestCcLibrarySpacesInCopts(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library spaces in copts",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library spaces in copts",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "a",
cflags: ["-include header.h",],
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
"copts": `[
"-include",
"header.h",
@@ -1046,11 +1122,11 @@
}
func TestCcLibraryCppFlagsGoesIntoCopts(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library cppflags usage",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library cppflags usage",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `cc_library {
name: "a",
srcs: ["a.cpp"],
cflags: ["-Wall"],
@@ -1071,7 +1147,7 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
"copts": `["-Wall"]`,
"cppflags": `[
"-fsigned-char",
@@ -1090,11 +1166,11 @@
}
func TestCcLibraryExcludeLibs(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{},
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library {
name: "foo_static",
srcs: ["common.c"],
@@ -1168,7 +1244,7 @@
bazel_module: { bp2build_available: false },
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo_static", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("foo_static", AttrNameToString{
"implementation_deps": `select({
"//build/bazel/platforms/arch:arm": [],
"//conditions:default": [":arm_static_lib_excludes_bp2build_cc_library_static"],
@@ -1196,15 +1272,49 @@
)
}
+func TestCcLibraryProductVariablesHeaderLibs(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "foo_static",
+ srcs: ["common.c"],
+ product_variables: {
+ malloc_not_svelte: {
+ header_libs: ["malloc_not_svelte_header_lib"],
+ },
+ },
+ include_build_directory: false,
+}
+
+cc_library {
+ name: "malloc_not_svelte_header_lib",
+ bazel_module: { bp2build_available: false },
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("foo_static", AttrNameToString{
+ "implementation_deps": `select({
+ "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_header_lib"],
+ "//conditions:default": [],
+ })`,
+ "srcs_c": `["common.c"]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ }),
+ },
+ )
+}
+
func TestCCLibraryNoCrtTrue(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - nocrt: true emits attribute",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library - nocrt: true disables feature",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1212,8 +1322,8 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
- "link_crt": `False`,
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
+ "features": `["-link_crt"]`,
"srcs": `["impl.cpp"]`,
}),
},
@@ -1221,14 +1331,14 @@
}
func TestCCLibraryNoCrtFalse(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - nocrt: false - does not emit attribute",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library - nocrt: false - does not emit attribute",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1236,21 +1346,21 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
"srcs": `["impl.cpp"]`,
}),
})
}
func TestCCLibraryNoCrtArchVariant(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - nocrt in select",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library - nocrt in select",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1265,18 +1375,24 @@
include_build_directory: false,
}
`,
- expectedErr: fmt.Errorf("module \"foo-lib\": nocrt is not supported for arch variants"),
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/arch:arm": ["-link_crt"],
+ "//conditions:default": [],
+ })`,
+ "srcs": `["impl.cpp"]`,
+ }),
})
}
func TestCCLibraryNoLibCrtTrue(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1284,26 +1400,27 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
- "srcs": `["impl.cpp"]`,
- "use_libcrt": `False`,
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
+ "features": `["-use_libcrt"]`,
+ "srcs": `["impl.cpp"]`,
}),
})
}
-func makeCcLibraryTargets(name string, attrs attrNameToString) []string {
+func makeCcLibraryTargets(name string, attrs AttrNameToString) []string {
STATIC_ONLY_ATTRS := map[string]bool{}
SHARED_ONLY_ATTRS := map[string]bool{
"link_crt": true,
"additional_linker_inputs": true,
"linkopts": true,
"strip": true,
- "stubs_symbol_file": true,
- "stubs_versions": true,
"inject_bssl_hash": true,
+ "stubs_symbol_file": true,
+ "use_version_lib": true,
}
- sharedAttrs := attrNameToString{}
- staticAttrs := attrNameToString{}
+
+ sharedAttrs := AttrNameToString{}
+ staticAttrs := AttrNameToString{}
for key, val := range attrs {
if _, staticOnly := STATIC_ONLY_ATTRS[key]; !staticOnly {
sharedAttrs[key] = val
@@ -1312,20 +1429,20 @@
staticAttrs[key] = val
}
}
- sharedTarget := makeBazelTarget("cc_library_shared", name, sharedAttrs)
- staticTarget := makeBazelTarget("cc_library_static", name+"_bp2build_cc_library_static", staticAttrs)
+ sharedTarget := MakeBazelTarget("cc_library_shared", name, sharedAttrs)
+ staticTarget := MakeBazelTarget("cc_library_static", name+"_bp2build_cc_library_static", staticAttrs)
return []string{staticTarget, sharedTarget}
}
func TestCCLibraryNoLibCrtFalse(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1333,21 +1450,20 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
- "srcs": `["impl.cpp"]`,
- "use_libcrt": `True`,
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
+ "srcs": `["impl.cpp"]`,
}),
})
}
func TestCCLibraryNoLibCrtArchVariant(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1362,25 +1478,25 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
"srcs": `["impl.cpp"]`,
- "use_libcrt": `select({
- "//build/bazel/platforms/arch:arm": False,
- "//build/bazel/platforms/arch:x86": False,
- "//conditions:default": None,
+ "features": `select({
+ "//build/bazel/platforms/arch:arm": ["-use_libcrt"],
+ "//build/bazel/platforms/arch:x86": ["-use_libcrt"],
+ "//conditions:default": [],
})`,
}),
})
}
func TestCCLibraryNoLibCrtArchAndTargetVariant(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1400,30 +1516,28 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
- "srcs": `["impl.cpp"]`,
- "use_libcrt": `select({
- "//build/bazel/platforms/os_arch:android_arm": False,
- "//build/bazel/platforms/os_arch:android_x86": False,
- "//build/bazel/platforms/os_arch:darwin_arm64": False,
- "//build/bazel/platforms/os_arch:darwin_x86_64": False,
- "//build/bazel/platforms/os_arch:linux_glibc_x86": False,
- "//build/bazel/platforms/os_arch:linux_musl_x86": False,
- "//build/bazel/platforms/os_arch:windows_x86": False,
- "//conditions:default": None,
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/arch:arm": ["-use_libcrt"],
+ "//build/bazel/platforms/arch:x86": ["-use_libcrt"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:darwin": ["-use_libcrt"],
+ "//conditions:default": [],
})`,
+ "srcs": `["impl.cpp"]`,
}),
})
}
func TestCCLibraryNoLibCrtArchAndTargetVariantConflict(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1444,18 +1558,12 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
"srcs": `["impl.cpp"]`,
- "use_libcrt": `select({
- "//build/bazel/platforms/os_arch:android_arm": False,
- "//build/bazel/platforms/os_arch:android_x86_64": False,
- "//build/bazel/platforms/os_arch:darwin_arm64": True,
- "//build/bazel/platforms/os_arch:darwin_x86_64": False,
- "//build/bazel/platforms/os_arch:linux_bionic_x86_64": False,
- "//build/bazel/platforms/os_arch:linux_glibc_x86_64": False,
- "//build/bazel/platforms/os_arch:linux_musl_x86_64": False,
- "//build/bazel/platforms/os_arch:windows_x86_64": False,
- "//conditions:default": None,
+ "features": `select({
+ "//build/bazel/platforms/arch:arm": ["-use_libcrt"],
+ "//build/bazel/platforms/arch:x86_64": ["-use_libcrt"],
+ "//conditions:default": [],
})`,
}),
})
@@ -1463,38 +1571,38 @@
func TestCcLibraryStrip(t *testing.T) {
expectedTargets := []string{}
- expectedTargets = append(expectedTargets, makeCcLibraryTargets("all", attrNameToString{
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("all", AttrNameToString{
"strip": `{
"all": True,
}`,
})...)
- expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols", attrNameToString{
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols", AttrNameToString{
"strip": `{
"keep_symbols": True,
}`,
})...)
- expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols_and_debug_frame", attrNameToString{
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols_and_debug_frame", AttrNameToString{
"strip": `{
"keep_symbols_and_debug_frame": True,
}`,
})...)
- expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols_list", attrNameToString{
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols_list", AttrNameToString{
"strip": `{
"keep_symbols_list": ["symbol"],
}`,
})...)
- expectedTargets = append(expectedTargets, makeCcLibraryTargets("none", attrNameToString{
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("none", AttrNameToString{
"strip": `{
"none": True,
}`,
})...)
- expectedTargets = append(expectedTargets, makeCcLibraryTargets("nothing", attrNameToString{})...)
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("nothing", AttrNameToString{})...)
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library strip args",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library strip args",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "nothing",
include_build_directory: false,
@@ -1535,16 +1643,16 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: expectedTargets,
+ ExpectedBazelTargets: expectedTargets,
})
}
func TestCcLibraryStripWithArch(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library strip args",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library strip args",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "multi-arch",
target: {
@@ -1569,7 +1677,7 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("multi-arch", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("multi-arch", AttrNameToString{
"strip": `{
"keep_symbols": select({
"//build/bazel/platforms/arch:arm64": True,
@@ -1593,18 +1701,18 @@
}
func TestCcLibrary_SystemSharedLibsRootEmpty(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library system_shared_libs empty at root",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_libs empty at root",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "root_empty",
system_shared_libs: [],
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("root_empty", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("root_empty", AttrNameToString{
"system_dynamic_deps": `[]`,
}),
},
@@ -1612,11 +1720,11 @@
}
func TestCcLibrary_SystemSharedLibsStaticEmpty(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library system_shared_libs empty for static variant",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_libs empty for static variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "static_empty",
static: {
@@ -1625,21 +1733,21 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "static_empty_bp2build_cc_library_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "static_empty_bp2build_cc_library_static", AttrNameToString{
"system_dynamic_deps": "[]",
}),
- makeBazelTarget("cc_library_shared", "static_empty", attrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "static_empty", AttrNameToString{}),
},
})
}
func TestCcLibrary_SystemSharedLibsSharedEmpty(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library system_shared_libs empty for shared variant",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_libs empty for shared variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "shared_empty",
shared: {
@@ -1648,9 +1756,9 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", attrNameToString{}),
- makeBazelTarget("cc_library_shared", "shared_empty", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", AttrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "shared_empty", AttrNameToString{
"system_dynamic_deps": "[]",
}),
},
@@ -1658,11 +1766,11 @@
}
func TestCcLibrary_SystemSharedLibsSharedBionicEmpty(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library system_shared_libs empty for shared, bionic variant",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_libs empty for shared, bionic variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "shared_empty",
target: {
@@ -1675,9 +1783,9 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", attrNameToString{}),
- makeBazelTarget("cc_library_shared", "shared_empty", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", AttrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "shared_empty", AttrNameToString{
"system_dynamic_deps": "[]",
}),
},
@@ -1689,11 +1797,16 @@
// The correct behavior would be if bp2build wrote `system_dynamic_deps = []`
// only for linux_bionic, but `android` had `["libc", "libdl", "libm"].
// b/195791252 tracks the fix.
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library system_shared_libs empty for linux_bionic variant",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_libs empty for linux_bionic variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library {
name: "target_linux_bionic_empty",
target: {
@@ -1704,19 +1817,27 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("target_linux_bionic_empty", attrNameToString{
- "system_dynamic_deps": `[]`,
+ ExpectedBazelTargets: makeCcLibraryTargets("target_linux_bionic_empty", AttrNameToString{
+ "system_dynamic_deps": `select({
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+ "//conditions:default": [],
+ })`,
}),
},
)
}
func TestCcLibrary_SystemSharedLibsBionicEmpty(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library system_shared_libs empty for bionic variant",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_libs empty for bionic variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library {
name: "target_bionic_empty",
target: {
@@ -1727,19 +1848,75 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("target_bionic_empty", attrNameToString{
- "system_dynamic_deps": `[]`,
+ ExpectedBazelTargets: makeCcLibraryTargets("target_bionic_empty", AttrNameToString{
+ "system_dynamic_deps": `select({
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+ "//conditions:default": [],
+ })`,
}),
},
)
}
+func TestCcLibrary_SystemSharedLibsMuslEmpty(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_lib empty for musl variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "target_musl_empty",
+ target: {
+ musl: {
+ system_shared_libs: [],
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("target_musl_empty", AttrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ })
+}
+
+func TestCcLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_lib empty for linux_musl variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "target_linux_musl_empty",
+ target: {
+ linux_musl: {
+ system_shared_libs: [],
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("target_linux_musl_empty", AttrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ })
+}
func TestCcLibrary_SystemSharedLibsSharedAndRoot(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library system_shared_libs set for shared and root",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_libs set for shared and root",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "libc",
bazel_module: { bp2build_available: false },
@@ -1758,11 +1935,11 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"system_dynamic_deps": `[":libc"]`,
}),
- makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
"system_dynamic_deps": `[
":libc",
":libm",
@@ -1773,12 +1950,12 @@
}
func TestCcLibraryOsSelects(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - selects for all os targets",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- filesystem: map[string]string{},
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library - selects for all os targets",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["base.cpp"],
@@ -1808,7 +1985,7 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
"srcs": `["base.cpp"] + select({
"//build/bazel/platforms/os:android": [
"linux.cpp",
@@ -1816,14 +1993,14 @@
"android.cpp",
],
"//build/bazel/platforms/os:darwin": ["darwin.cpp"],
- "//build/bazel/platforms/os:linux": [
- "linux.cpp",
- "linux_glibc.cpp",
- ],
"//build/bazel/platforms/os:linux_bionic": [
"linux.cpp",
"bionic.cpp",
],
+ "//build/bazel/platforms/os:linux_glibc": [
+ "linux.cpp",
+ "linux_glibc.cpp",
+ ],
"//build/bazel/platforms/os:linux_musl": [
"linux.cpp",
"linux_musl.cpp",
@@ -1837,12 +2014,12 @@
}
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 + `
+ 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: {
@@ -1853,7 +2030,7 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: makeCcLibraryTargets("libcrypto", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("libcrypto", AttrNameToString{
"inject_bssl_hash": `select({
"//build/bazel/platforms/os:android": True,
"//conditions:default": None,
@@ -1879,76 +2056,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
@@ -1957,15 +2136,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
@@ -1974,17 +2153,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
@@ -1993,93 +2172,97 @@
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),
+ },
+ })
})
}
}
func TestCcLibraryProtoSimple(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.proto"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
- }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[":foo_proto"]`,
- }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
"deps": `[":libprotobuf-cpp-lite"]`,
- }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
- "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
}),
},
})
}
func TestCcLibraryProtoNoCanonicalPathFromRoot(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.proto"],
proto: { canonical_path_from_root: false},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
"strip_import_prefix": `""`,
- }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[":foo_proto"]`,
- }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
"deps": `[":libprotobuf-cpp-lite"]`,
- }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
- "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
}),
},
})
}
func TestCcLibraryProtoExplicitCanonicalPathFromRoot(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.proto"],
proto: { canonical_path_from_root: true},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
- }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[":foo_proto"]`,
- }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
"deps": `[":libprotobuf-cpp-lite"]`,
- }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
- "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
}),
},
})
}
func TestCcLibraryProtoFull(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.proto"],
proto: {
@@ -2087,26 +2270,27 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
- }), makeBazelTarget("cc_proto_library", "foo_cc_proto", attrNameToString{
+ }), MakeBazelTarget("cc_proto_library", "foo_cc_proto", AttrNameToString{
"deps": `[":foo_proto"]`,
- }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"implementation_whole_archive_deps": `[":foo_cc_proto"]`,
"deps": `[":libprotobuf-cpp-full"]`,
- }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
- "dynamic_deps": `[":libprotobuf-cpp-full"]`,
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-full"]`,
+ "implementation_whole_archive_deps": `[":foo_cc_proto"]`,
}),
},
})
}
func TestCcLibraryProtoLite(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.proto"],
proto: {
@@ -2114,26 +2298,27 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
- }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[":foo_proto"]`,
- }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
"deps": `[":libprotobuf-cpp-lite"]`,
- }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
- "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
}),
},
})
}
func TestCcLibraryProtoExportHeaders(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.proto"],
proto: {
@@ -2141,15 +2326,15 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
- }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[":foo_proto"]`,
- }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":foo_cc_proto_lite"]`,
- }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":foo_cc_proto_lite"]`,
}),
@@ -2157,11 +2342,190 @@
})
}
+func TestCcLibraryProtoIncludeDirs(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ srcs: ["foo.proto"],
+ proto: {
+ include_dirs: ["external/protobuf/src"],
+ },
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+ "srcs": `["foo.proto"]`,
+ "deps": `["//external/protobuf:libprotobuf-proto"]`,
+ }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+ "deps": `[":foo_proto"]`,
+ }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryProtoIncludeDirsUnknown(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ srcs: ["foo.proto"],
+ proto: {
+ include_dirs: ["external/protobuf/abc"],
+ },
+ include_build_directory: false,
+}`,
+ ExpectedErr: fmt.Errorf("module \"foo\": Could not find the proto_library target for include dir: external/protobuf/abc"),
+ })
+}
+
+func TestCcLibraryConvertedProtoFilegroups(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `
+filegroup {
+ name: "a_fg_proto",
+ srcs: ["a_fg.proto"],
+}
+
+cc_library {
+ name: "a",
+ srcs: [
+ ":a_fg_proto",
+ "a.proto",
+ ],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "a_proto", AttrNameToString{
+ "deps": `[":a_fg_proto_bp2build_converted"]`,
+ "srcs": `["a.proto"]`,
+ }), MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{
+ "deps": `[
+ ":a_fg_proto_bp2build_converted",
+ ":a_proto",
+ ]`,
+ }), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":a_cc_proto_lite"]`,
+ }), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":a_cc_proto_lite"]`,
+ }), MakeBazelTargetNoRestrictions("proto_library", "a_fg_proto_bp2build_converted", AttrNameToString{
+ "srcs": `["a_fg.proto"]`,
+ "tags": `[
+ "apex_available=//apex_available:anyapex",
+ "manual",
+ ]`,
+ }), MakeBazelTargetNoRestrictions("filegroup", "a_fg_proto", AttrNameToString{
+ "srcs": `["a_fg.proto"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryConvertedProtoFilegroupsNoProtoFiles(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `
+filegroup {
+ name: "a_fg_proto",
+ srcs: ["a_fg.proto"],
+}
+
+cc_library {
+ name: "a",
+ srcs: [
+ ":a_fg_proto",
+ ],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{
+ "deps": `[":a_fg_proto_bp2build_converted"]`,
+ }), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":a_cc_proto_lite"]`,
+ }), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":a_cc_proto_lite"]`,
+ }), MakeBazelTargetNoRestrictions("proto_library", "a_fg_proto_bp2build_converted", AttrNameToString{
+ "srcs": `["a_fg.proto"]`,
+ "tags": `[
+ "apex_available=//apex_available:anyapex",
+ "manual",
+ ]`,
+ }), MakeBazelTargetNoRestrictions("filegroup", "a_fg_proto", AttrNameToString{
+ "srcs": `["a_fg.proto"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryExternalConvertedProtoFilegroups(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "path/to/A/Android.bp": `
+filegroup {
+ name: "a_fg_proto",
+ srcs: ["a_fg.proto"],
+}`,
+ },
+ Blueprint: soongCcProtoPreamble + `
+cc_library {
+ name: "a",
+ srcs: [
+ ":a_fg_proto",
+ "a.proto",
+ ],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "a_proto", AttrNameToString{
+ "deps": `["//path/to/A:a_fg_proto_bp2build_converted"]`,
+ "srcs": `["a.proto"]`,
+ }), MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{
+ "deps": `[
+ "//path/to/A:a_fg_proto_bp2build_converted",
+ ":a_proto",
+ ]`,
+ }), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":a_cc_proto_lite"]`,
+ }), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":a_cc_proto_lite"]`,
+ }),
+ },
+ })
+}
+
func TestCcLibraryProtoFilegroups(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble +
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble +
simpleModuleDoNotConvertBp2build("filegroup", "a_fg_proto") +
simpleModuleDoNotConvertBp2build("filegroup", "b_protos") +
simpleModuleDoNotConvertBp2build("filegroup", "c-proto-srcs") +
@@ -2201,66 +2565,66 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("proto_library", "a_proto", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "a_proto", AttrNameToString{
"srcs": `[":a_fg_proto"]`,
- }), makeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{
"deps": `[":a_proto"]`,
- }), makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
"deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":a_cc_proto_lite"]`,
"srcs": `[":a_fg_proto_cpp_srcs"]`,
"srcs_as": `[":a_fg_proto_as_srcs"]`,
"srcs_c": `[":a_fg_proto_c_srcs"]`,
- }), makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ }), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":a_cc_proto_lite"]`,
"srcs": `[":a_fg_proto_cpp_srcs"]`,
"srcs_as": `[":a_fg_proto_as_srcs"]`,
"srcs_c": `[":a_fg_proto_c_srcs"]`,
- }), makeBazelTarget("proto_library", "b_proto", attrNameToString{
+ }), MakeBazelTarget("proto_library", "b_proto", AttrNameToString{
"srcs": `[":b_protos"]`,
- }), makeBazelTarget("cc_lite_proto_library", "b_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "b_cc_proto_lite", AttrNameToString{
"deps": `[":b_proto"]`,
- }), makeBazelTarget("cc_library_static", "b_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "b_bp2build_cc_library_static", AttrNameToString{
"deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":b_cc_proto_lite"]`,
"srcs": `[":b_protos_cpp_srcs"]`,
"srcs_as": `[":b_protos_as_srcs"]`,
"srcs_c": `[":b_protos_c_srcs"]`,
- }), makeBazelTarget("cc_library_shared", "b", attrNameToString{
+ }), MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":b_cc_proto_lite"]`,
"srcs": `[":b_protos_cpp_srcs"]`,
"srcs_as": `[":b_protos_as_srcs"]`,
"srcs_c": `[":b_protos_c_srcs"]`,
- }), makeBazelTarget("proto_library", "c_proto", attrNameToString{
+ }), MakeBazelTarget("proto_library", "c_proto", AttrNameToString{
"srcs": `[":c-proto-srcs"]`,
- }), makeBazelTarget("cc_lite_proto_library", "c_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "c_cc_proto_lite", AttrNameToString{
"deps": `[":c_proto"]`,
- }), makeBazelTarget("cc_library_static", "c_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "c_bp2build_cc_library_static", AttrNameToString{
"deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":c_cc_proto_lite"]`,
"srcs": `[":c-proto-srcs_cpp_srcs"]`,
"srcs_as": `[":c-proto-srcs_as_srcs"]`,
"srcs_c": `[":c-proto-srcs_c_srcs"]`,
- }), makeBazelTarget("cc_library_shared", "c", attrNameToString{
+ }), MakeBazelTarget("cc_library_shared", "c", AttrNameToString{
"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":c_cc_proto_lite"]`,
"srcs": `[":c-proto-srcs_cpp_srcs"]`,
"srcs_as": `[":c-proto-srcs_as_srcs"]`,
"srcs_c": `[":c-proto-srcs_c_srcs"]`,
- }), makeBazelTarget("proto_library", "d_proto", attrNameToString{
+ }), MakeBazelTarget("proto_library", "d_proto", AttrNameToString{
"srcs": `[":proto-srcs-d"]`,
- }), makeBazelTarget("cc_lite_proto_library", "d_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "d_cc_proto_lite", AttrNameToString{
"deps": `[":d_proto"]`,
- }), makeBazelTarget("cc_library_static", "d_bp2build_cc_library_static", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "d_bp2build_cc_library_static", AttrNameToString{
"deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":d_cc_proto_lite"]`,
"srcs": `[":proto-srcs-d_cpp_srcs"]`,
"srcs_as": `[":proto-srcs-d_as_srcs"]`,
"srcs_c": `[":proto-srcs-d_c_srcs"]`,
- }), makeBazelTarget("cc_library_shared", "d", attrNameToString{
+ }), MakeBazelTarget("cc_library_shared", "d", AttrNameToString{
"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":d_cc_proto_lite"]`,
"srcs": `[":proto-srcs-d_cpp_srcs"]`,
@@ -2272,12 +2636,13 @@
}
func TestCcLibraryDisabledArchAndTarget(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.cpp"],
+ host_supported: true,
target: {
darwin: {
enabled: false,
@@ -2291,7 +2656,7 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{
"srcs": `["foo.cpp"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os_arch:darwin_arm64": ["@platforms//:incompatible"],
@@ -2306,13 +2671,14 @@
}
func TestCcLibraryDisabledArchAndTargetWithDefault(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.cpp"],
enabled: false,
+ host_supported: true,
target: {
darwin: {
enabled: true,
@@ -2326,7 +2692,7 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+ ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{
"srcs": `["foo.cpp"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os_arch:darwin_arm64": [],
@@ -2338,10 +2704,10 @@
}
func TestCcLibrarySharedDisabled(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
srcs: ["foo.cpp"],
enabled: false,
@@ -2357,10 +2723,10 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ ExpectedBazelTargets: []string{MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"srcs": `["foo.cpp"]`,
"target_compatible_with": `["@platforms//:incompatible"]`,
- }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
"srcs": `["foo.cpp"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
@@ -2372,11 +2738,12 @@
}
func TestCcLibraryStaticDisabledForSomeArch(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- blueprint: soongCcProtoPreamble + `cc_library {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcProtoPreamble + `cc_library {
name: "foo",
+ host_supported: true,
srcs: ["foo.cpp"],
shared: {
enabled: false
@@ -2396,13 +2763,13 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ ExpectedBazelTargets: []string{MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"srcs": `["foo.cpp"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
})`,
- }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
"srcs": `["foo.cpp"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os_arch:darwin_arm64": [],
@@ -2415,12 +2782,25 @@
}
func TestCcLibraryStubs(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library stubs",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- dir: "foo/bar",
- filesystem: map[string]string{
+ expectedBazelTargets := makeCcLibraryTargets("a", AttrNameToString{
+ "stubs_symbol_file": `"a.map.txt"`,
+ })
+ expectedBazelTargets = append(expectedBazelTargets, makeCcStubSuiteTargets("a", AttrNameToString{
+ "soname": `"a.so"`,
+ "source_library_label": `"//foo/bar:a"`,
+ "stubs_symbol_file": `"a.map.txt"`,
+ "stubs_versions": `[
+ "28",
+ "29",
+ "current",
+ ]`,
+ }))
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library stubs",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Dir: "foo/bar",
+ Filesystem: map[string]string{
"foo/bar/Android.bp": `
cc_library {
name: "a",
@@ -2430,30 +2810,1583 @@
}
`,
},
- blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
- "stubs_symbol_file": `"a.map.txt"`,
- "stubs_versions": `[
- "28",
- "29",
- "current",
+ Blueprint: soongCcLibraryPreamble,
+ ExpectedBazelTargets: expectedBazelTargets,
+ },
+ )
+}
+
+func TestCcApiContributionsWithHdrs(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libfoo",
+ stubs: { symbol_file: "libfoo.map.txt", versions: ["28", "29", "current"] },
+ llndk: { symbol_file: "libfoo.map.txt", override_export_include_dirs: ["dir2"]},
+ export_include_dirs: ["dir1"],
+ }
+ `
+ expectedBazelTargets := []string{
+ MakeBazelTarget(
+ "cc_api_library_headers",
+ "libfoo.module-libapi.headers",
+ AttrNameToString{
+ "export_includes": `["dir1"]`,
+ }),
+ MakeBazelTarget(
+ "cc_api_library_headers",
+ "libfoo.vendorapi.headers",
+ AttrNameToString{
+ "export_includes": `["dir2"]`,
+ }),
+ MakeBazelTarget(
+ "cc_api_contribution",
+ "libfoo.contribution",
+ AttrNameToString{
+ "api": `"libfoo.map.txt"`,
+ "library_name": `"libfoo"`,
+ "api_surfaces": `[
+ "module-libapi",
+ "vendorapi",
]`,
+ "hdrs": `[
+ ":libfoo.module-libapi.headers",
+ ":libfoo.vendorapi.headers",
+ ]`,
+ }),
+ }
+ RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
+ Blueprint: bp,
+ Description: "cc API contributions to module-libapi and vendorapi",
+ ExpectedBazelTargets: expectedBazelTargets,
+ })
+}
+
+func TestCcApiSurfaceCombinations(t *testing.T) {
+ testCases := []struct {
+ bp string
+ expectedApi string
+ expectedApiSurfaces string
+ description string
+ }{
+ {
+ bp: `
+ cc_library {
+ name: "a",
+ stubs: {symbol_file: "a.map.txt"},
+ }`,
+ expectedApi: `"a.map.txt"`,
+ expectedApiSurfaces: `["module-libapi"]`,
+ description: "Library that contributes to module-libapi",
+ },
+ {
+ bp: `
+ cc_library {
+ name: "a",
+ llndk: {symbol_file: "a.map.txt"},
+ }`,
+ expectedApi: `"a.map.txt"`,
+ expectedApiSurfaces: `["vendorapi"]`,
+ description: "Library that contributes to vendorapi",
+ },
+ {
+ bp: `
+ cc_library {
+ name: "a",
+ llndk: {symbol_file: "a.map.txt"},
+ stubs: {symbol_file: "a.map.txt"},
+ }`,
+ expectedApi: `"a.map.txt"`,
+ expectedApiSurfaces: `[
+ "module-libapi",
+ "vendorapi",
+ ]`,
+ description: "Library that contributes to module-libapi and vendorapi",
+ },
+ }
+ for _, testCase := range testCases {
+ expectedBazelTargets := []string{
+ MakeBazelTarget(
+ "cc_api_contribution",
+ "a.contribution",
+ AttrNameToString{
+ "library_name": `"a"`,
+ "hdrs": `[]`,
+ "api": testCase.expectedApi,
+ "api_surfaces": testCase.expectedApiSurfaces,
+ },
+ ),
+ }
+ RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
+ Blueprint: testCase.bp,
+ Description: testCase.description,
+ ExpectedBazelTargets: expectedBazelTargets,
+ })
+ }
+}
+
+// llndk struct property in Soong provides users with several options to configure the exported include dirs
+// Test the generated bazel targets for the different configurations
+func TestCcVendorApiHeaders(t *testing.T) {
+ testCases := []struct {
+ bp string
+ expectedIncludes string
+ expectedSystemIncludes string
+ description string
+ }{
+ {
+ bp: `
+ cc_library {
+ name: "a",
+ export_include_dirs: ["include"],
+ export_system_include_dirs: ["base_system_include"],
+ llndk: {
+ symbol_file: "a.map.txt",
+ export_headers_as_system: true,
+ },
+ }
+ `,
+ expectedIncludes: "",
+ expectedSystemIncludes: `[
+ "base_system_include",
+ "include",
+ ]`,
+ description: "Headers are exported as system to API surface",
+ },
+ {
+ bp: `
+ cc_library {
+ name: "a",
+ export_include_dirs: ["include"],
+ export_system_include_dirs: ["base_system_include"],
+ llndk: {
+ symbol_file: "a.map.txt",
+ override_export_include_dirs: ["llndk_include"],
+ },
+ }
+ `,
+ expectedIncludes: `["llndk_include"]`,
+ expectedSystemIncludes: `["base_system_include"]`,
+ description: "Non-system Headers are ovverriden before export to API surface",
+ },
+ {
+ bp: `
+ cc_library {
+ name: "a",
+ export_include_dirs: ["include"],
+ export_system_include_dirs: ["base_system_include"],
+ llndk: {
+ symbol_file: "a.map.txt",
+ override_export_include_dirs: ["llndk_include"],
+ export_headers_as_system: true,
+ },
+ }
+ `,
+ expectedIncludes: "", // includes are set to nil
+ expectedSystemIncludes: `[
+ "base_system_include",
+ "llndk_include",
+ ]`,
+ description: "System Headers are extended before export to API surface",
+ },
+ }
+ for _, testCase := range testCases {
+ attrs := AttrNameToString{}
+ if testCase.expectedIncludes != "" {
+ attrs["export_includes"] = testCase.expectedIncludes
+ }
+ if testCase.expectedSystemIncludes != "" {
+ attrs["export_system_includes"] = testCase.expectedSystemIncludes
+ }
+
+ expectedBazelTargets := []string{
+ MakeBazelTarget("cc_api_library_headers", "a.vendorapi.headers", attrs),
+ // Create a target for cc_api_contribution target
+ MakeBazelTarget("cc_api_contribution", "a.contribution", AttrNameToString{
+ "api": `"a.map.txt"`,
+ "api_surfaces": `["vendorapi"]`,
+ "hdrs": `[":a.vendorapi.headers"]`,
+ "library_name": `"a"`,
+ }),
+ }
+ RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
+ Blueprint: testCase.bp,
+ ExpectedBazelTargets: expectedBazelTargets,
+ })
+ }
+}
+
+func TestCcLibraryStubsAcrossConfigsDuplicatesRemoved(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "stub target generation of the same lib across configs should not result in duplicates",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "bar.map.txt": "",
+ },
+ Blueprint: `
+cc_library {
+ name: "barlib",
+ stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: false },
+}
+cc_library {
+ name: "foolib",
+ shared_libs: ["barlib"],
+ target: {
+ android: {
+ shared_libs: ["barlib"],
+ },
+ },
+ bazel_module: { bp2build_available: true },
+ apex_available: ["foo"],
+}`,
+ ExpectedBazelTargets: makeCcLibraryTargets("foolib", AttrNameToString{
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"],
+ "//conditions:default": [":barlib"],
+ })`,
+ "local_includes": `["."]`,
+ "tags": `["apex_available=foo"]`,
+ }),
+ })
+}
+
+func TestCcLibraryExcludesLibsHost(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "bar.map.txt": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("cc_library", "bazlib") + `
+cc_library {
+ name: "quxlib",
+ stubs: { symbol_file: "bar.map.txt", versions: ["current"] },
+ bazel_module: { bp2build_available: false },
+}
+cc_library {
+ name: "barlib",
+ stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: false },
+}
+cc_library {
+ name: "foolib",
+ shared_libs: ["barlib", "quxlib"],
+ target: {
+ host: {
+ shared_libs: ["bazlib"],
+ exclude_shared_libs: ["barlib"],
+ },
+ },
+ include_build_directory: false,
+ bazel_module: { bp2build_available: true },
+ apex_available: ["foo"],
+}`,
+ ExpectedBazelTargets: makeCcLibraryTargets("foolib", AttrNameToString{
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/platforms/os:darwin": [":bazlib"],
+ "//build/bazel/platforms/os:linux_bionic": [":bazlib"],
+ "//build/bazel/platforms/os:linux_glibc": [":bazlib"],
+ "//build/bazel/platforms/os:linux_musl": [":bazlib"],
+ "//build/bazel/platforms/os:windows": [":bazlib"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:darwin": [":quxlib"],
+ "//build/bazel/platforms/os:linux_bionic": [":quxlib"],
+ "//build/bazel/platforms/os:linux_glibc": [":quxlib"],
+ "//build/bazel/platforms/os:linux_musl": [":quxlib"],
+ "//build/bazel/platforms/os:windows": [":quxlib"],
+ "//build/bazel/rules/apex:android-in_apex": [
+ "@api_surfaces//module-libapi/current:barlib",
+ "@api_surfaces//module-libapi/current:quxlib",
+ ],
+ "//conditions:default": [
+ ":barlib",
+ ":quxlib",
+ ],
+ })`,
+ "tags": `["apex_available=foo"]`,
+ }),
+ })
+}
+
+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",
+ ]`,
+ })...),
+ })
+}
+
+func TestCCLibraryRuntimeDeps(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Blueprint: `cc_library_shared {
+ name: "bar",
+}
+
+cc_library {
+ name: "foo",
+ runtime_libs: ["foo"],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "bar", AttrNameToString{
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "runtime_deps": `[":foo"]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "runtime_deps": `[":foo"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithInstructionSet(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `cc_library {
+ name: "foo",
+ arch: {
+ arm: {
+ instruction_set: "arm",
+ }
+ }
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/arch:arm": [
+ "arm_isa_arm",
+ "-arm_isa_thumb",
+ ],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ })
+}
+
+func TestCcLibraryEmptySuffix(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with empty suffix",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "foo.c": "",
+ },
+ Blueprint: `cc_library {
+ name: "foo",
+ suffix: "",
+ srcs: ["foo.c"],
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "srcs_c": `["foo.c"]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "srcs_c": `["foo.c"]`,
+ "suffix": `""`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySuffix(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with suffix",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "foo.c": "",
+ },
+ Blueprint: `cc_library {
+ name: "foo",
+ suffix: "-suf",
+ srcs: ["foo.c"],
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "srcs_c": `["foo.c"]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "srcs_c": `["foo.c"]`,
+ "suffix": `"-suf"`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryArchVariantSuffix(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with arch-variant suffix",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "foo.c": "",
+ },
+ Blueprint: `cc_library {
+ name: "foo",
+ arch: {
+ arm64: { suffix: "-64" },
+ arm: { suffix: "-32" },
+ },
+ srcs: ["foo.c"],
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "srcs_c": `["foo.c"]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "srcs_c": `["foo.c"]`,
+ "suffix": `select({
+ "//build/bazel/platforms/arch:arm": "-32",
+ "//build/bazel/platforms/arch:arm64": "-64",
+ "//conditions:default": None,
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithAidlSrcs(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with aidl srcs",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+filegroup {
+ name: "A_aidl",
+ srcs: ["aidl/A.aidl"],
+ path: "aidl",
+}
+cc_library {
+ name: "foo",
+ srcs: [
+ ":A_aidl",
+ "B.aidl",
+ ],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("aidl_library", "A_aidl", AttrNameToString{
+ "srcs": `["aidl/A.aidl"]`,
+ "strip_import_prefix": `"aidl"`,
+ "tags": `["apex_available=//apex_available:anyapex"]`,
+ }),
+ MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
+ "srcs": `["B.aidl"]`,
+ }),
+ MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+ "deps": `[
+ ":A_aidl",
+ ":foo_aidl_library",
+ ]`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithNonAdjacentAidlFilegroup(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with non aidl filegroup",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "path/to/A/Android.bp": `
+filegroup {
+ name: "A_aidl",
+ srcs: ["aidl/A.aidl"],
+ path: "aidl",
+}`,
+ },
+ Blueprint: `
+cc_library {
+ name: "foo",
+ srcs: [
+ ":A_aidl",
+ ],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+ "deps": `["//path/to/A:A_aidl"]`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithExportAidlHeaders(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with export aidl headers",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ srcs: [
+ "Foo.aidl",
+ ],
+ aidl: {
+ export_aidl_headers: true,
+ }
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
+ "srcs": `["Foo.aidl"]`,
+ }),
+ MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+ "deps": `[":foo_aidl_library"]`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "whole_archive_deps": `[":foo_cc_aidl_library"]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "whole_archive_deps": `[":foo_cc_aidl_library"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithTargetApex(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with target.apex",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ shared_libs: ["bar", "baz"],
+ static_libs: ["baz", "buh"],
+ target: {
+ apex: {
+ exclude_shared_libs: ["bar"],
+ exclude_static_libs: ["buh"],
+ }
+ }
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
+ })`,
+ "implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
+ })`,
+ "implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithTargetApexAndExportLibHeaders(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with target.apex and export_shared|static_lib_headers",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ shared_libs: ["bar", "baz"],
+ static_libs: ["abc"],
+ export_shared_lib_headers: ["baz"],
+ export_static_lib_headers: ["abc"],
+ target: {
+ apex: {
+ exclude_shared_libs: ["baz", "bar"],
+ exclude_static_libs: ["abc"],
+ }
+ }
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
+ })`,
+ "dynamic_deps": `select({
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":baz__BP2BUILD__MISSING__DEP"],
+ })`,
+ "deps": `select({
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":abc__BP2BUILD__MISSING__DEP"],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithSyspropSrcs(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with sysprop sources",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ srcs: [
+ "bar.sysprop",
+ "baz.sysprop",
+ "blah.cpp",
+ ],
+ min_sdk_version: "5",
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{
+ "srcs": `[
+ "bar.sysprop",
+ "baz.sysprop",
+ ]`,
+ }),
+ MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{
+ "dep": `":foo_sysprop_library"`,
+ "min_sdk_version": `"5"`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithSyspropSrcsSomeConfigs(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with sysprop sources in some configs but not others",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ host_supported: true,
+ srcs: [
+ "blah.cpp",
+ ],
+ target: {
+ android: {
+ srcs: ["bar.sysprop"],
+ },
+ },
+ min_sdk_version: "5",
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("sysprop_library", "foo_sysprop_library", AttrNameToString{
+ "srcs": `select({
+ "//build/bazel/platforms/os:android": ["bar.sysprop"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTargetNoRestrictions("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{
+ "dep": `":foo_sysprop_library"`,
+ "min_sdk_version": `"5"`,
+ }),
+ MakeBazelTargetNoRestrictions("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `select({
+ "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTargetNoRestrictions("cc_library_shared", "foo", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `select({
+ "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithAidlAndLibs(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_aidl_library depends on libs from parent cc_library_static",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ srcs: [
+ "Foo.aidl",
+ ],
+ static_libs: [
+ "bar-static",
+ "baz-static",
+ ],
+ shared_libs: [
+ "bar-shared",
+ "baz-shared",
+ ],
+ export_static_lib_headers: [
+ "baz-static",
+ ],
+ export_shared_lib_headers: [
+ "baz-shared",
+ ],
+}` +
+ simpleModuleDoNotConvertBp2build("cc_library_static", "bar-static") +
+ simpleModuleDoNotConvertBp2build("cc_library_static", "baz-static") +
+ simpleModuleDoNotConvertBp2build("cc_library", "bar-shared") +
+ simpleModuleDoNotConvertBp2build("cc_library", "baz-shared"),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
+ "srcs": `["Foo.aidl"]`,
+ }),
+ MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+ "deps": `[":foo_aidl_library"]`,
+ "implementation_deps": `[
+ ":baz-static",
+ ":bar-static",
+ ]`,
+ "implementation_dynamic_deps": `[
+ ":baz-shared",
+ ":bar-shared",
+ ]`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+ "deps": `[":baz-static"]`,
+ "implementation_deps": `[":bar-static"]`,
+ "dynamic_deps": `[":baz-shared"]`,
+ "implementation_dynamic_deps": `[":bar-shared"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithTidy(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library uses tidy properties",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ srcs: ["foo.cpp"],
+}
+cc_library_static {
+ name: "foo-no-tidy",
+ srcs: ["foo.cpp"],
+ tidy: false,
+}
+cc_library_static {
+ name: "foo-tidy",
+ srcs: ["foo.cpp"],
+ tidy: true,
+ tidy_checks: ["check1", "check2"],
+ tidy_checks_as_errors: ["check1error", "check2error"],
+ tidy_disabled_srcs: ["bar.cpp"],
+ tidy_timeout_srcs: ["baz.cpp"],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "srcs": `["foo.cpp"]`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo-no-tidy", AttrNameToString{
+ "local_includes": `["."]`,
+ "srcs": `["foo.cpp"]`,
+ "tidy": `"never"`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo-tidy", AttrNameToString{
+ "local_includes": `["."]`,
+ "srcs": `["foo.cpp"]`,
+ "tidy": `"local"`,
+ "tidy_checks": `[
+ "check1",
+ "check2",
+ ]`,
+ "tidy_checks_as_errors": `[
+ "check1error",
+ "check2error",
+ ]`,
+ "tidy_disabled_srcs": `["bar.cpp"]`,
+ "tidy_timeout_srcs": `["baz.cpp"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithAfdoEnabled(t *testing.T) {
+ bp := `
+cc_library {
+ name: "foo",
+ afdo: true,
+ include_build_directory: false,
+}`
+
+ // TODO(b/260714900): Add test case for arch-specific afdo profile
+ testCases := []struct {
+ description string
+ filesystem map[string]string
+ expectedBazelTargets []string
+ }{
+ {
+ description: "cc_library with afdo enabled and existing profile",
+ filesystem: map[string]string{
+ "vendor/google_data/pgo_profile/sampling/BUILD": "",
+ "vendor/google_data/pgo_profile/sampling/foo.afdo": "",
+ },
+ expectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "fdo_profile": `"//vendor/google_data/pgo_profile/sampling:foo"`,
+ }),
+ },
+ },
+ {
+ description: "cc_library with afdo enabled and existing profile in AOSP",
+ filesystem: map[string]string{
+ "toolchain/pgo-profiles/sampling/BUILD": "",
+ "toolchain/pgo-profiles/sampling/foo.afdo": "",
+ },
+ expectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "fdo_profile": `"//toolchain/pgo-profiles/sampling:foo"`,
+ }),
+ },
+ },
+ {
+ description: "cc_library with afdo enabled but profile filename doesn't match with module name",
+ filesystem: map[string]string{
+ "toolchain/pgo-profiles/sampling/BUILD": "",
+ "toolchain/pgo-profiles/sampling/bar.afdo": "",
+ },
+ expectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{}),
+ },
+ },
+ {
+ description: "cc_library with afdo enabled but profile doesn't exist",
+ expectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{}),
+ },
+ },
+ {
+ description: "cc_library with afdo enabled and existing profile but BUILD file doesn't exist",
+ filesystem: map[string]string{
+ "vendor/google_data/pgo_profile/sampling/foo.afdo": "",
+ },
+ expectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{}),
+ },
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.description, func(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ ExpectedBazelTargets: testCase.expectedBazelTargets,
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Description: testCase.description,
+ Blueprint: binaryReplacer.Replace(bp),
+ Filesystem: testCase.filesystem,
+ })
+ })
+ }
+}
+
+func TestCcLibraryHeaderAbiChecker(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with header abi checker",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `cc_library {
+ name: "foo",
+ header_abi_checker: {
+ enabled: true,
+ symbol_file: "a.map.txt",
+ exclude_symbol_versions: [
+ "29",
+ "30",
+ ],
+ exclude_symbol_tags: [
+ "tag1",
+ "tag2",
+ ],
+ check_all_apis: true,
+ diff_flags: ["-allow-adding-removing-weak-symbols"],
+ },
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "abi_checker_enabled": `True`,
+ "abi_checker_symbol_file": `"a.map.txt"`,
+ "abi_checker_exclude_symbol_versions": `[
+ "29",
+ "30",
+ ]`,
+ "abi_checker_exclude_symbol_tags": `[
+ "tag1",
+ "tag2",
+ ]`,
+ "abi_checker_check_all_apis": `True`,
+ "abi_checker_diff_flags": `["-allow-adding-removing-weak-symbols"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryApexAvailable(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library apex_available converted to tags",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "a",
+ srcs: ["a.cpp"],
+ apex_available: ["com.android.foo"],
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
+ "tags": `["apex_available=com.android.foo"]`,
+ "srcs": `["a.cpp"]`,
+ "local_includes": `["."]`,
}),
},
)
}
-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 TestCcLibraryApexAvailableMultiple(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library apex_available converted to multiple tags",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "a",
+ srcs: ["a.cpp"],
+ apex_available: ["com.android.foo", "//apex_available:platform", "com.android.bar"],
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
+ "tags": `[
+ "apex_available=com.android.foo",
+ "apex_available=//apex_available:platform",
+ "apex_available=com.android.bar",
+ ]`,
+ "srcs": `["a.cpp"]`,
+ "local_includes": `["."]`,
}),
+ },
+ )
+}
+
+// Export_include_dirs and Export_system_include_dirs have "variant_prepend" tag.
+// In bp2build output, variant info(select) should go before general info.
+// Internal order of the property should be unchanged. (e.g. ["eid1", "eid2"])
+func TestCcLibraryVariantPrependPropOrder(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library variant prepend properties order",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "a",
+ srcs: ["a.cpp"],
+ export_include_dirs: ["eid1", "eid2"],
+ export_system_include_dirs: ["esid1", "esid2"],
+ target: {
+ android: {
+ export_include_dirs: ["android_eid1", "android_eid2"],
+ export_system_include_dirs: ["android_esid1", "android_esid2"],
+ },
+ android_arm: {
+ export_include_dirs: ["android_arm_eid1", "android_arm_eid2"],
+ export_system_include_dirs: ["android_arm_esid1", "android_arm_esid2"],
+ },
+ linux: {
+ export_include_dirs: ["linux_eid1", "linux_eid2"],
+ export_system_include_dirs: ["linux_esid1", "linux_esid2"],
+ },
+ },
+ multilib: {
+ lib32: {
+ export_include_dirs: ["lib32_eid1", "lib32_eid2"],
+ export_system_include_dirs: ["lib32_esid1", "lib32_esid2"],
+ },
+ },
+ arch: {
+ arm: {
+ export_include_dirs: ["arm_eid1", "arm_eid2"],
+ export_system_include_dirs: ["arm_esid1", "arm_esid2"],
+ },
+ }
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
+ "export_includes": `select({
+ "//build/bazel/platforms/os_arch:android_arm": [
+ "android_arm_eid1",
+ "android_arm_eid2",
+ ],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": [
+ "android_eid1",
+ "android_eid2",
+ "linux_eid1",
+ "linux_eid2",
+ ],
+ "//build/bazel/platforms/os:linux_bionic": [
+ "linux_eid1",
+ "linux_eid2",
+ ],
+ "//build/bazel/platforms/os:linux_glibc": [
+ "linux_eid1",
+ "linux_eid2",
+ ],
+ "//build/bazel/platforms/os:linux_musl": [
+ "linux_eid1",
+ "linux_eid2",
+ ],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/arch:arm": [
+ "lib32_eid1",
+ "lib32_eid2",
+ "arm_eid1",
+ "arm_eid2",
+ ],
+ "//build/bazel/platforms/arch:x86": [
+ "lib32_eid1",
+ "lib32_eid2",
+ ],
+ "//conditions:default": [],
+ }) + [
+ "eid1",
+ "eid2",
+ ]`,
+ "export_system_includes": `select({
+ "//build/bazel/platforms/os_arch:android_arm": [
+ "android_arm_esid1",
+ "android_arm_esid2",
+ ],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": [
+ "android_esid1",
+ "android_esid2",
+ "linux_esid1",
+ "linux_esid2",
+ ],
+ "//build/bazel/platforms/os:linux_bionic": [
+ "linux_esid1",
+ "linux_esid2",
+ ],
+ "//build/bazel/platforms/os:linux_glibc": [
+ "linux_esid1",
+ "linux_esid2",
+ ],
+ "//build/bazel/platforms/os:linux_musl": [
+ "linux_esid1",
+ "linux_esid2",
+ ],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/arch:arm": [
+ "lib32_esid1",
+ "lib32_esid2",
+ "arm_esid1",
+ "arm_esid2",
+ ],
+ "//build/bazel/platforms/arch:x86": [
+ "lib32_esid1",
+ "lib32_esid2",
+ ],
+ "//conditions:default": [],
+ }) + [
+ "esid1",
+ "esid2",
+ ]`,
+ "srcs": `["a.cpp"]`,
+ "local_includes": `["."]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ }),
+ },
+ )
+}
+
+func TestCcLibraryWithIntegerOverflowProperty(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library has correct features when integer_overflow property is provided",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ sanitize: {
+ integer_overflow: true,
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "features": `["ubsan_integer_overflow"]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `["ubsan_integer_overflow"]`,
+ "local_includes": `["."]`,
+ }),
+ },
})
}
+
+func TestCcLibraryWithMiscUndefinedProperty(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library has correct features when misc_undefined property is provided",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ sanitize: {
+ misc_undefined: ["undefined", "nullability"],
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithUBSanPropertiesArchSpecific(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library has correct feature select when UBSan props are specified in arch specific blocks",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ sanitize: {
+ misc_undefined: ["undefined", "nullability"],
+ },
+ target: {
+ android: {
+ sanitize: {
+ misc_undefined: ["alignment"],
+ },
+ },
+ linux_glibc: {
+ sanitize: {
+ integer_overflow: true,
+ },
+ },
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ] + select({
+ "//build/bazel/platforms/os:android": ["ubsan_alignment"],
+ "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ] + select({
+ "//build/bazel/platforms/os:android": ["ubsan_alignment"],
+ "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryInApexWithStubSharedLibs(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library with in apex with stub shared_libs and export_shared_lib_headers",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "barlib",
+ stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: false },
+}
+cc_library {
+ name: "bazlib",
+ stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: false },
+}
+cc_library {
+ name: "foo",
+ shared_libs: ["barlib", "bazlib"],
+ export_shared_lib_headers: ["bazlib"],
+ apex_available: [
+ "apex_available:platform",
+ ],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"],
+ "//conditions:default": [":barlib"],
+ })`,
+ "dynamic_deps": `select({
+ "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:bazlib"],
+ "//conditions:default": [":bazlib"],
+ })`,
+ "local_includes": `["."]`,
+ "tags": `["apex_available=apex_available:platform"]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"],
+ "//conditions:default": [":barlib"],
+ })`,
+ "dynamic_deps": `select({
+ "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:bazlib"],
+ "//conditions:default": [":bazlib"],
+ })`,
+ "local_includes": `["."]`,
+ "tags": `["apex_available=apex_available:platform"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithThinLto(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library has correct features when thin LTO is enabled",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ lto: {
+ thin: true,
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "features": `["android_thin_lto"]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `["android_thin_lto"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithLtoNever(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library has correct features when LTO is explicitly disabled",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ lto: {
+ never: true,
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "features": `["-android_thin_lto"]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `["-android_thin_lto"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithThinLtoArchSpecific(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library has correct features when LTO differs across arch and os variants",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ },
+ },
+ },
+ arch: {
+ riscv64: {
+ lto: {
+ thin: false,
+ },
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+ "//conditions:default": [],
+ })`}),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+ "//conditions:default": [],
+ })`}),
+ },
+ })
+}
+
+func TestCcLibraryWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library has correct features when LTO disabled by default but enabled on a particular variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ lto: {
+ never: true,
+ },
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ never: false,
+ },
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["android_thin_lto"],
+ "//conditions:default": ["-android_thin_lto"],
+ })`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["android_thin_lto"],
+ "//conditions:default": ["-android_thin_lto"],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryWithThinLtoWholeProgramVtables(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library has correct features when thin LTO is enabled with whole_program_vtables",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ lto: {
+ thin: true,
+ },
+ whole_program_vtables: true,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "features": `[
+ "android_thin_lto",
+ "android_thin_lto_whole_program_vtables",
+ ]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `[
+ "android_thin_lto",
+ "android_thin_lto_whole_program_vtables",
+ ]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryCppFlagsInProductVariables(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library cppflags in product variables",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `cc_library {
+ name: "a",
+ srcs: ["a.cpp"],
+ cppflags: [
+ "-Wextra",
+ "-DDEBUG_ONLY_CODE=0",
+ ],
+ product_variables: {
+ eng: {
+ cppflags: [
+ "-UDEBUG_ONLY_CODE",
+ "-DDEBUG_ONLY_CODE=1",
+ ],
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
+ "cppflags": `[
+ "-Wextra",
+ "-DDEBUG_ONLY_CODE=0",
+ ] + select({
+ "//build/bazel/product_variables:eng": [
+ "-UDEBUG_ONLY_CODE",
+ "-DDEBUG_ONLY_CODE=1",
+ ],
+ "//conditions:default": [],
+ })`,
+ "srcs": `["a.cpp"]`,
+ }),
+ },
+ )
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index e5bb120..072f5b3 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -59,17 +59,17 @@
cc.RegisterCCBuildComponents(ctx)
}
-func runCcLibraryHeadersTestCase(t *testing.T, tc bp2buildTestCase) {
+func runCcLibraryHeadersTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, registerCcLibraryHeadersModuleTypes, tc)
+ RunBp2BuildTestCase(t, registerCcLibraryHeadersModuleTypes, tc)
}
func TestCcLibraryHeadersSimple(t *testing.T) {
- runCcLibraryHeadersTestCase(t, bp2buildTestCase{
- description: "cc_library_headers test",
- moduleTypeUnderTest: "cc_library_headers",
- moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
- filesystem: map[string]string{
+ runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_headers test",
+ ModuleTypeUnderTest: "cc_library_headers",
+ ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ Filesystem: map[string]string{
"lib-1/lib1a.h": "",
"lib-1/lib1b.h": "",
"lib-2/lib2a.h": "",
@@ -82,19 +82,7 @@
"arch_x86_exported_include_dir/b.h": "",
"arch_x86_64_exported_include_dir/c.h": "",
},
- blueprint: soongCcLibraryHeadersPreamble + `
-cc_library_headers {
- name: "lib-1",
- export_include_dirs: ["lib-1"],
- bazel_module: { bp2build_available: false },
-}
-
-cc_library_headers {
- name: "lib-2",
- export_include_dirs: ["lib-2"],
- bazel_module: { bp2build_available: false },
-}
-
+ Blueprint: soongCcLibraryHeadersPreamble + `
cc_library_headers {
name: "foo_headers",
export_include_dirs: ["dir-1", "dir-2"],
@@ -117,35 +105,96 @@
// TODO: Also support export_header_lib_headers
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
- "export_includes": `[
- "dir-1",
- "dir-2",
- ] + select({
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+ "export_includes": `select({
"//build/bazel/platforms/arch:arm64": ["arch_arm64_exported_include_dir"],
"//build/bazel/platforms/arch:x86": ["arch_x86_exported_include_dir"],
"//build/bazel/platforms/arch:x86_64": ["arch_x86_64_exported_include_dir"],
"//conditions:default": [],
- })`,
- "implementation_deps": `[
- ":lib-1",
- ":lib-2",
+ }) + [
+ "dir-1",
+ "dir-2",
]`,
- "sdk_version": `"current"`,
- "min_sdk_version": `"29"`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
}
+func TestCcApiHeaders(t *testing.T) {
+ fs := map[string]string{
+ "bar/Android.bp": `cc_library_headers { name: "bar_headers", }`,
+ }
+ bp := `
+ cc_library_headers {
+ name: "foo_headers",
+ export_include_dirs: ["dir1", "dir2"],
+ export_header_lib_headers: ["bar_headers"],
+
+ arch: {
+ arm: {
+ export_include_dirs: ["dir_arm"],
+ },
+ x86: {
+ export_include_dirs: ["dir_x86"],
+ },
+ },
+
+ target: {
+ android: {
+ export_include_dirs: ["dir1", "dir_android"],
+ },
+ windows: {
+ export_include_dirs: ["dir_windows"],
+ },
+ }
+ }
+ `
+ expectedBazelTargets := []string{
+ MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.arm", AttrNameToString{
+ "export_includes": `["dir_arm"]`,
+ "arch": `"arm"`,
+ }),
+ MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.x86", AttrNameToString{
+ "export_includes": `["dir_x86"]`,
+ "arch": `"x86"`,
+ }),
+ MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.androidos", AttrNameToString{
+ "export_includes": `["dir_android"]`, // common includes are deduped
+ }),
+ // Windows headers are not exported
+ MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution", AttrNameToString{
+ "export_includes": `[
+ "dir1",
+ "dir2",
+ ]`,
+ "deps": `[
+ "//bar:bar_headers.contribution",
+ ":foo_headers.contribution.arm",
+ ":foo_headers.contribution.x86",
+ ":foo_headers.contribution.androidos",
+ ]`,
+ }),
+ }
+ RunApiBp2BuildTestCase(t, cc.RegisterLibraryHeadersBuildComponents, Bp2buildTestCase{
+ Blueprint: bp,
+ Description: "Header library contributions to API surfaces",
+ ExpectedBazelTargets: expectedBazelTargets,
+ Filesystem: fs,
+ })
+}
+
+// header_libs has "variant_prepend" tag. In bp2build output,
+// variant info(select) should go before general info.
func TestCcLibraryHeadersOsSpecificHeader(t *testing.T) {
- runCcLibraryHeadersTestCase(t, bp2buildTestCase{
- description: "cc_library_headers test with os-specific header_libs props",
- moduleTypeUnderTest: "cc_library_headers",
- moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
- filesystem: map[string]string{},
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_headers test with os-specific header_libs props",
+ ModuleTypeUnderTest: "cc_library_headers",
+ ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryPreamble + `
cc_library_headers {
name: "android-lib",
bazel_module: { bp2build_available: false },
@@ -173,37 +222,53 @@
cc_library_headers {
name: "foo_headers",
header_libs: ["base-lib"],
+ export_header_lib_headers: ["base-lib"],
target: {
- android: { header_libs: ["android-lib"] },
- darwin: { header_libs: ["darwin-lib"] },
- linux_bionic: { header_libs: ["linux_bionic-lib"] },
- linux_glibc: { header_libs: ["linux-lib"] },
- windows: { header_libs: ["windows-lib"] },
+ android: {
+ header_libs: ["android-lib"],
+ export_header_lib_headers: ["android-lib"],
+ },
+ darwin: {
+ header_libs: ["darwin-lib"],
+ export_header_lib_headers: ["darwin-lib"],
+ },
+ linux_bionic: {
+ header_libs: ["linux_bionic-lib"],
+ export_header_lib_headers: ["linux_bionic-lib"],
+ },
+ linux_glibc: {
+ header_libs: ["linux-lib"],
+ export_header_lib_headers: ["linux-lib"],
+ },
+ windows: {
+ header_libs: ["windows-lib"],
+ export_header_lib_headers: ["windows-lib"],
+ },
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
- "implementation_deps": `[":base-lib"] + select({
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+ "deps": `select({
"//build/bazel/platforms/os:android": [":android-lib"],
"//build/bazel/platforms/os:darwin": [":darwin-lib"],
- "//build/bazel/platforms/os:linux": [":linux-lib"],
"//build/bazel/platforms/os:linux_bionic": [":linux_bionic-lib"],
+ "//build/bazel/platforms/os:linux_glibc": [":linux-lib"],
"//build/bazel/platforms/os:windows": [":windows-lib"],
"//conditions:default": [],
- })`,
+ }) + [":base-lib"]`,
}),
},
})
}
func TestCcLibraryHeadersOsSpecficHeaderLibsExportHeaderLibHeaders(t *testing.T) {
- runCcLibraryHeadersTestCase(t, bp2buildTestCase{
- description: "cc_library_headers test with os-specific header_libs and export_header_lib_headers props",
- moduleTypeUnderTest: "cc_library_headers",
- moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
- filesystem: map[string]string{},
- blueprint: soongCcLibraryPreamble + `
+ runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_headers test with os-specific header_libs and export_header_lib_headers props",
+ ModuleTypeUnderTest: "cc_library_headers",
+ ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryPreamble + `
cc_library_headers {
name: "android-lib",
bazel_module: { bp2build_available: false },
@@ -222,28 +287,24 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
"deps": `select({
"//build/bazel/platforms/os:android": [":exported-lib"],
"//conditions:default": [],
})`,
- "implementation_deps": `select({
- "//build/bazel/platforms/os:android": [":android-lib"],
- "//conditions:default": [],
- })`,
}),
},
})
}
func TestCcLibraryHeadersArchAndTargetExportSystemIncludes(t *testing.T) {
- runCcLibraryHeadersTestCase(t, bp2buildTestCase{
- description: "cc_library_headers test with arch-specific and target-specific export_system_include_dirs props",
- moduleTypeUnderTest: "cc_library_headers",
- moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
- filesystem: map[string]string{},
- blueprint: soongCcLibraryPreamble + `cc_library_headers {
+ runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_headers test with arch-specific and target-specific export_system_include_dirs props",
+ ModuleTypeUnderTest: "cc_library_headers",
+ ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryPreamble + `cc_library_headers {
name: "foo_headers",
export_system_include_dirs: [
"shared_include_dir",
@@ -279,29 +340,29 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
- "export_system_includes": `["shared_include_dir"] + select({
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+ "export_system_includes": `select({
+ "//build/bazel/platforms/os:android": ["android_include_dir"],
+ "//build/bazel/platforms/os:darwin": ["darwin_include_dir"],
+ "//build/bazel/platforms/os:linux_glibc": ["linux_include_dir"],
+ "//conditions:default": [],
+ }) + select({
"//build/bazel/platforms/arch:arm": ["arm_include_dir"],
"//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"],
"//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": ["android_include_dir"],
- "//build/bazel/platforms/os:darwin": ["darwin_include_dir"],
- "//build/bazel/platforms/os:linux": ["linux_include_dir"],
- "//conditions:default": [],
- })`,
+ }) + ["shared_include_dir"]`,
}),
},
})
}
func TestCcLibraryHeadersNoCrtIgnored(t *testing.T) {
- runCcLibraryHeadersTestCase(t, bp2buildTestCase{
- description: "cc_library_headers test",
- moduleTypeUnderTest: "cc_library_headers",
- moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
- filesystem: map[string]string{
+ runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_headers test",
+ ModuleTypeUnderTest: "cc_library_headers",
+ ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ Filesystem: map[string]string{
"lib-1/lib1a.h": "",
"lib-1/lib1b.h": "",
"lib-2/lib2a.h": "",
@@ -314,17 +375,104 @@
"arch_x86_exported_include_dir/b.h": "",
"arch_x86_64_exported_include_dir/c.h": "",
},
- blueprint: soongCcLibraryHeadersPreamble + `
+ Blueprint: soongCcLibraryHeadersPreamble + `
cc_library_headers {
name: "lib-1",
export_include_dirs: ["lib-1"],
no_libcrt: true,
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_headers", "lib-1", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_headers", "lib-1", AttrNameToString{
"export_includes": `["lib-1"]`,
}),
},
})
}
+
+func TestCcLibraryHeadersExportedStaticLibHeadersReexported(t *testing.T) {
+ runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_headers exported_static_lib_headers is reexported",
+ ModuleTypeUnderTest: "cc_library_headers",
+ ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+ name: "foo_headers",
+ export_static_lib_headers: ["foo_export"],
+ static_libs: ["foo_export", "foo_no_reexport"],
+ bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+ "deps": `[":foo_export"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryHeadersExportedSharedLibHeadersReexported(t *testing.T) {
+ runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_headers exported_shared_lib_headers is reexported",
+ ModuleTypeUnderTest: "cc_library_headers",
+ ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+ name: "foo_headers",
+ export_shared_lib_headers: ["foo_export"],
+ shared_libs: ["foo_export", "foo_no_reexport"],
+ bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+ "deps": `[":foo_export"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryHeadersExportedHeaderLibHeadersReexported(t *testing.T) {
+ runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_headers exported_header_lib_headers is reexported",
+ ModuleTypeUnderTest: "cc_library_headers",
+ ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+ name: "foo_headers",
+ export_header_lib_headers: ["foo_export"],
+ header_libs: ["foo_export", "foo_no_reexport"],
+ bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+ "deps": `[":foo_export"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryHeadersWholeStaticLibsReexported(t *testing.T) {
+ runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_headers whole_static_libs is reexported",
+ ModuleTypeUnderTest: "cc_library_headers",
+ ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+ name: "foo_headers",
+ whole_static_libs: ["foo_export"],
+ bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+ "deps": `[":foo_export"]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 22c9dfe..9ba9337 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -15,7 +15,6 @@
package bp2build
import (
- "fmt"
"testing"
"android/soong/android"
@@ -35,17 +34,18 @@
ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
}
-func runCcLibrarySharedTestCase(t *testing.T, tc bp2buildTestCase) {
+func runCcLibrarySharedTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "cc_library_shared"
- (&tc).moduleTypeUnderTestFactory = cc.LibrarySharedFactory
- runBp2BuildTestCase(t, registerCcLibrarySharedModuleTypes, tc)
+ t.Parallel()
+ (&tc).ModuleTypeUnderTest = "cc_library_shared"
+ (&tc).ModuleTypeUnderTestFactory = cc.LibrarySharedFactory
+ RunBp2BuildTestCase(t, registerCcLibrarySharedModuleTypes, tc)
}
func TestCcLibrarySharedSimple(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared simple overall test",
- filesystem: map[string]string{
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared simple overall test",
+ Filesystem: map[string]string{
// NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
"include_dir_1/include_dir_1_a.h": "",
"include_dir_1/include_dir_1_b.h": "",
@@ -65,7 +65,7 @@
"implicit_include_1.h": "",
"implicit_include_2.h": "",
},
- blueprint: soongCcLibrarySharedPreamble + `
+ Blueprint: soongCcLibrarySharedPreamble + `
cc_library_headers {
name: "header_lib_1",
export_include_dirs: ["header_lib_1"],
@@ -141,8 +141,8 @@
// TODO: Also support export_header_lib_headers
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
"absolute_includes": `[
"include_dir_1",
"include_dir_2",
@@ -176,18 +176,18 @@
":whole_static_lib_1",
":whole_static_lib_2",
]`,
- "sdk_version": `"current"`,
- "min_sdk_version": `"29"`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
}
func TestCcLibrarySharedArchSpecificSharedLib(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared arch-specific shared_libs with whole_static_libs",
- filesystem: map[string]string{},
- blueprint: soongCcLibrarySharedPreamble + `
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared arch-specific shared_libs with whole_static_libs",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibrarySharedPreamble + `
cc_library_static {
name: "static_dep",
bazel_module: { bp2build_available: false },
@@ -201,8 +201,8 @@
arch: { arm64: { shared_libs: ["shared_dep"], whole_static_libs: ["static_dep"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
"implementation_dynamic_deps": `select({
"//build/bazel/platforms/arch:arm64": [":shared_dep"],
"//conditions:default": [],
@@ -217,10 +217,10 @@
}
func TestCcLibrarySharedOsSpecificSharedLib(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared os-specific shared_libs",
- filesystem: map[string]string{},
- blueprint: soongCcLibrarySharedPreamble + `
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared os-specific shared_libs",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibrarySharedPreamble + `
cc_library_shared {
name: "shared_dep",
bazel_module: { bp2build_available: false },
@@ -230,8 +230,8 @@
target: { android: { shared_libs: ["shared_dep"], } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
"implementation_dynamic_deps": `select({
"//build/bazel/platforms/os:android": [":shared_dep"],
"//conditions:default": [],
@@ -242,10 +242,10 @@
}
func TestCcLibrarySharedBaseArchOsSpecificSharedLib(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared base, arch, and os-specific shared_libs",
- filesystem: map[string]string{},
- blueprint: soongCcLibrarySharedPreamble + `
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared base, arch, and os-specific shared_libs",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibrarySharedPreamble + `
cc_library_shared {
name: "shared_dep",
bazel_module: { bp2build_available: false },
@@ -265,8 +265,8 @@
arch: { arm64: { shared_libs: ["shared_dep3"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
"implementation_dynamic_deps": `[":shared_dep"] + select({
"//build/bazel/platforms/arch:arm64": [":shared_dep3"],
"//conditions:default": [],
@@ -280,22 +280,22 @@
}
func TestCcLibrarySharedSimpleExcludeSrcs(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared simple exclude_srcs",
- filesystem: map[string]string{
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared simple exclude_srcs",
+ Filesystem: map[string]string{
"common.c": "",
"foo-a.c": "",
"foo-excluded.c": "",
},
- blueprint: soongCcLibrarySharedPreamble + `
+ Blueprint: soongCcLibrarySharedPreamble + `
cc_library_shared {
name: "foo_shared",
srcs: ["common.c", "foo-*.c"],
exclude_srcs: ["foo-excluded.c"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
"srcs_c": `[
"common.c",
"foo-a.c",
@@ -306,10 +306,10 @@
}
func TestCcLibrarySharedStrip(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared stripping",
- filesystem: map[string]string{},
- blueprint: soongCcLibrarySharedPreamble + `
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared stripping",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibrarySharedPreamble + `
cc_library_shared {
name: "foo_shared",
strip: {
@@ -321,8 +321,8 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
"strip": `{
"all": True,
"keep_symbols": False,
@@ -338,34 +338,78 @@
})
}
-func TestCcLibrarySharedVersionScript(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared version script",
- filesystem: map[string]string{
+func TestCcLibrarySharedVersionScriptAndDynamicList(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared version script and dynamic list",
+ Filesystem: map[string]string{
"version_script": "",
+ "dynamic.list": "",
},
- blueprint: soongCcLibrarySharedPreamble + `
+ Blueprint: soongCcLibrarySharedPreamble + `
cc_library_shared {
name: "foo_shared",
version_script: "version_script",
+ dynamic_list: "dynamic.list",
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
- "additional_linker_inputs": `["version_script"]`,
- "linkopts": `["-Wl,--version-script,$(location version_script)"]`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+ "additional_linker_inputs": `[
+ "version_script",
+ "dynamic.list",
+ ]`,
+ "linkopts": `[
+ "-Wl,--version-script,$(location version_script)",
+ "-Wl,--dynamic-list,$(location dynamic.list)",
+ ]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryLdflagsSplitBySpaceSoongAdded(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)",
+ Filesystem: map[string]string{
+ "version_script": "",
+ "dynamic.list": "",
+ },
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ ldflags: [
+ "--nospace_flag",
+ "-z spaceflag",
+ ],
+ version_script: "version_script",
+ dynamic_list: "dynamic.list",
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "additional_linker_inputs": `[
+ "version_script",
+ "dynamic.list",
+ ]`,
+ "linkopts": `[
+ "--nospace_flag",
+ "-z",
+ "spaceflag",
+ "-Wl,--version-script,$(location version_script)",
+ "-Wl,--dynamic-list,$(location dynamic.list)",
+ ]`,
}),
},
})
}
func TestCcLibrarySharedNoCrtTrue(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared - nocrt: true emits attribute",
- filesystem: map[string]string{
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared - nocrt: true disables feature",
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library_shared {
name: "foo_shared",
srcs: ["impl.cpp"],
@@ -373,9 +417,9 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
- "link_crt": `False`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+ "features": `["-link_crt"]`,
"srcs": `["impl.cpp"]`,
}),
},
@@ -383,12 +427,12 @@
}
func TestCcLibrarySharedNoCrtFalse(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared - nocrt: false doesn't emit attribute",
- filesystem: map[string]string{
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared - nocrt: false doesn't disable feature",
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library_shared {
name: "foo_shared",
srcs: ["impl.cpp"],
@@ -396,8 +440,8 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
"srcs": `["impl.cpp"]`,
}),
},
@@ -405,12 +449,12 @@
}
func TestCcLibrarySharedNoCrtArchVariant(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared - nocrt in select",
- filesystem: map[string]string{
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared - nocrt in select",
+ Filesystem: map[string]string{
"impl.cpp": "",
},
- blueprint: soongCcLibraryPreamble + `
+ Blueprint: soongCcLibraryPreamble + `
cc_library_shared {
name: "foo_shared",
srcs: ["impl.cpp"],
@@ -425,13 +469,21 @@
include_build_directory: false,
}
`,
- expectedErr: fmt.Errorf("module \"foo_shared\": nocrt is not supported for arch variants"),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/arch:arm": ["-link_crt"],
+ "//conditions:default": [],
+ })`,
+ "srcs": `["impl.cpp"]`,
+ }),
+ },
})
}
func TestCcLibrarySharedProto(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- blueprint: soongCcProtoPreamble + `cc_library_shared {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Blueprint: soongCcProtoPreamble + `cc_library_shared {
name: "foo",
srcs: ["foo.proto"],
proto: {
@@ -439,12 +491,12 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
- }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[":foo_proto"]`,
- }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":foo_cc_proto_lite"]`,
}),
@@ -453,27 +505,31 @@
}
func TestCcLibrarySharedUseVersionLib(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- blueprint: soongCcProtoPreamble + `cc_library_shared {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Filesystem: map[string]string{
+ soongCcVersionLibBpPath: soongCcVersionLibBp,
+ },
+ Blueprint: soongCcProtoPreamble + `cc_library_shared {
name: "foo",
use_version_lib: true,
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_shared", "foo", attrNameToString{
- "use_version_lib": "True",
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "use_version_lib": "True",
+ "implementation_whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
}),
},
})
}
func TestCcLibrarySharedStubs(t *testing.T) {
- runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared stubs",
- moduleTypeUnderTest: "cc_library_shared",
- moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
- dir: "foo/bar",
- filesystem: map[string]string{
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared stubs",
+ ModuleTypeUnderTest: "cc_library_shared",
+ ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+ Dir: "foo/bar",
+ Filesystem: map[string]string{
"foo/bar/Android.bp": `
cc_library_shared {
name: "a",
@@ -483,16 +539,777 @@
}
`,
},
- blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{makeBazelTarget("cc_library_shared", "a", attrNameToString{
- "stubs_symbol_file": `"a.map.txt"`,
+ Blueprint: soongCcLibraryPreamble,
+ ExpectedBazelTargets: []string{makeCcStubSuiteTargets("a", AttrNameToString{
+ "soname": `"a.so"`,
+ "source_library_label": `"//foo/bar:a"`,
+ "stubs_symbol_file": `"a.map.txt"`,
"stubs_versions": `[
"28",
"29",
"current",
]`,
}),
+ MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+ "stubs_symbol_file": `"a.map.txt"`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedStubs_UseImplementationInSameApex(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared stubs",
+ ModuleTypeUnderTest: "cc_library_shared",
+ ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+ Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+ name: "a",
+ stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: false },
+ include_build_directory: false,
+ apex_available: ["made_up_apex"],
+}
+cc_library_shared {
+ name: "b",
+ shared_libs: [":a"],
+ include_build_directory: false,
+ apex_available: ["made_up_apex"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+ "implementation_dynamic_deps": `[":a"]`,
+ "tags": `["apex_available=made_up_apex"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedStubs_UseStubsInDifferentApex(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared stubs",
+ ModuleTypeUnderTest: "cc_library_shared",
+ ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+ Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+ name: "a",
+ stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: false },
+ include_build_directory: false,
+ apex_available: ["apex_a"],
+}
+cc_library_shared {
+ name: "b",
+ shared_libs: [":a"],
+ include_build_directory: false,
+ apex_available: ["apex_b"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"],
+ "//conditions:default": [":a"],
+ })`,
+ "tags": `["apex_available=apex_b"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedStubs_IgnorePlatformAvailable(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared stubs",
+ ModuleTypeUnderTest: "cc_library_shared",
+ ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+ Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+ name: "a",
+ stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: false },
+ include_build_directory: false,
+ apex_available: ["//apex_available:platform", "apex_a"],
+}
+cc_library_shared {
+ name: "b",
+ shared_libs: [":a"],
+ include_build_directory: false,
+ apex_available: ["//apex_available:platform", "apex_b"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"],
+ "//conditions:default": [":a"],
+ })`,
+ "tags": `[
+ "apex_available=//apex_available:platform",
+ "apex_available=apex_b",
+ ]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedStubs_MultipleApexAvailable(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_library_shared",
+ ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+ Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+ name: "a",
+ stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: false },
+ include_build_directory: false,
+ apex_available: ["//apex_available:platform", "apex_a", "apex_b"],
+}
+cc_library_shared {
+ name: "b",
+ shared_libs: [":a"],
+ include_build_directory: false,
+ apex_available: ["//apex_available:platform", "apex_b"],
+}
+
+cc_library_shared {
+ name: "c",
+ shared_libs: [":a"],
+ include_build_directory: false,
+ apex_available: ["//apex_available:platform", "apex_a", "apex_b"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"],
+ "//conditions:default": [":a"],
+ })`,
+ "tags": `[
+ "apex_available=//apex_available:platform",
+ "apex_available=apex_b",
+ ]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "c", AttrNameToString{
+ "implementation_dynamic_deps": `[":a"]`,
+ "tags": `[
+ "apex_available=//apex_available:platform",
+ "apex_available=apex_a",
+ "apex_available=apex_b",
+ ]`,
+ }),
+ },
+ })
+}
+
+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",
+ ]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedClangUnknownFlags(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Blueprint: soongCcProtoPreamble + `cc_library_shared {
+ name: "foo",
+ conlyflags: ["-a", "-finline-functions"],
+ cflags: ["-b","-finline-functions"],
+ cppflags: ["-c", "-finline-functions"],
+ ldflags: ["-d","-finline-functions", "-e"],
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "conlyflags": `["-a"]`,
+ "copts": `["-b"]`,
+ "cppflags": `["-c"]`,
+ "linkopts": `[
+ "-d",
+ "-e",
+ ]`,
+ }),
+ },
+ })
+}
+
+func TestCCLibraryFlagSpaceSplitting(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Blueprint: soongCcProtoPreamble + `cc_library_shared {
+ name: "foo",
+ conlyflags: [ "-include header.h"],
+ cflags: ["-include header.h"],
+ cppflags: ["-include header.h"],
+ version_script: "version_script",
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "additional_linker_inputs": `["version_script"]`,
+ "conlyflags": `[
+ "-include",
+ "header.h",
+ ]`,
+ "copts": `[
+ "-include",
+ "header.h",
+ ]`,
+ "cppflags": `[
+ "-include",
+ "header.h",
+ ]`,
+ "linkopts": `["-Wl,--version-script,$(location version_script)"]`,
+ }),
+ },
+ })
+}
+
+func TestCCLibrarySharedRuntimeDeps(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Blueprint: `cc_library_shared {
+ name: "bar",
+}
+
+cc_library_shared {
+ name: "foo",
+ runtime_libs: ["foo"],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "bar", AttrNameToString{
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "runtime_deps": `[":foo"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedEmptySuffix(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared with empty suffix",
+ Filesystem: map[string]string{
+ "foo.c": "",
+ },
+ Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+ name: "foo_shared",
+ suffix: "",
+ srcs: ["foo.c"],
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+ "srcs_c": `["foo.c"]`,
+ "suffix": `""`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedSuffix(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared with suffix",
+ Filesystem: map[string]string{
+ "foo.c": "",
+ },
+ Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+ name: "foo_shared",
+ suffix: "-suf",
+ srcs: ["foo.c"],
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+ "srcs_c": `["foo.c"]`,
+ "suffix": `"-suf"`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedArchVariantSuffix(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared with arch-variant suffix",
+ Filesystem: map[string]string{
+ "foo.c": "",
+ },
+ Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+ name: "foo_shared",
+ arch: {
+ arm64: { suffix: "-64" },
+ arm: { suffix: "-32" },
+ },
+ srcs: ["foo.c"],
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+ "srcs_c": `["foo.c"]`,
+ "suffix": `select({
+ "//build/bazel/platforms/arch:arm": "-32",
+ "//build/bazel/platforms/arch:arm64": "-64",
+ "//conditions:default": None,
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithSyspropSrcs(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared with sysprop sources",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ srcs: [
+ "bar.sysprop",
+ "baz.sysprop",
+ "blah.cpp",
+ ],
+ min_sdk_version: "5",
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{
+ "srcs": `[
+ "bar.sysprop",
+ "baz.sysprop",
+ ]`,
+ }),
+ MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{
+ "dep": `":foo_sysprop_library"`,
+ "min_sdk_version": `"5"`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithSyspropSrcsSomeConfigs(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared with sysprop sources in some configs but not others",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ srcs: [
+ "blah.cpp",
+ ],
+ target: {
+ android: {
+ srcs: ["bar.sysprop"],
},
},
- )
+ min_sdk_version: "5",
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{
+ "srcs": `select({
+ "//build/bazel/platforms/os:android": ["bar.sysprop"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{
+ "dep": `":foo_sysprop_library"`,
+ "min_sdk_version": `"5"`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `select({
+ "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedHeaderAbiChecker(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared with header abi checker",
+ Blueprint: `cc_library_shared {
+ name: "foo",
+ header_abi_checker: {
+ enabled: true,
+ symbol_file: "a.map.txt",
+ exclude_symbol_versions: [
+ "29",
+ "30",
+ ],
+ exclude_symbol_tags: [
+ "tag1",
+ "tag2",
+ ],
+ check_all_apis: true,
+ diff_flags: ["-allow-adding-removing-weak-symbols"],
+ },
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "abi_checker_enabled": `True`,
+ "abi_checker_symbol_file": `"a.map.txt"`,
+ "abi_checker_exclude_symbol_versions": `[
+ "29",
+ "30",
+ ]`,
+ "abi_checker_exclude_symbol_tags": `[
+ "tag1",
+ "tag2",
+ ]`,
+ "abi_checker_check_all_apis": `True`,
+ "abi_checker_diff_flags": `["-allow-adding-removing-weak-symbols"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithIntegerOverflowProperty(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared has correct features when integer_overflow property is provided",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ sanitize: {
+ integer_overflow: true,
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `["ubsan_integer_overflow"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithMiscUndefinedProperty(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared has correct features when misc_undefined property is provided",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ sanitize: {
+ misc_undefined: ["undefined", "nullability"],
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithUBSanPropertiesArchSpecific(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared has correct feature select when UBSan props are specified in arch specific blocks",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ sanitize: {
+ misc_undefined: ["undefined", "nullability"],
+ },
+ target: {
+ android: {
+ sanitize: {
+ misc_undefined: ["alignment"],
+ },
+ },
+ linux_glibc: {
+ sanitize: {
+ integer_overflow: true,
+ },
+ },
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ] + select({
+ "//build/bazel/platforms/os:android": ["ubsan_alignment"],
+ "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithThinLto(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared has correct features when thin lto is enabled",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ lto: {
+ thin: true,
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `["android_thin_lto"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithLtoNever(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared has correct features when thin lto is enabled",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ lto: {
+ never: true,
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `["-android_thin_lto"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithThinLtoArchSpecific(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared has correct features when LTO differs across arch and os variants",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ },
+ },
+ },
+ arch: {
+ riscv64: {
+ lto: {
+ thin: false,
+ },
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+ "//conditions:default": [],
+ })`}),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared with thin lto disabled by default but enabled on a particular variant",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ lto: {
+ never: true,
+ },
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ never: false,
+ },
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["android_thin_lto"],
+ "//conditions:default": ["-android_thin_lto"],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedWithThinLtoAndWholeProgramVtables(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared has correct features when thin LTO is enabled with whole_program_vtables",
+ Blueprint: `
+cc_library_shared {
+ name: "foo",
+ lto: {
+ thin: true,
+ },
+ whole_program_vtables: true,
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `[
+ "android_thin_lto",
+ "android_thin_lto_whole_program_vtables",
+ ]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedStubsDessertVersionConversion(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared converts dessert codename versions to numerical versions",
+ Blueprint: `
+cc_library_shared {
+ name: "a",
+ include_build_directory: false,
+ stubs: {
+ symbol_file: "a.map.txt",
+ versions: [
+ "Q",
+ "R",
+ "31",
+ ],
+ },
+}
+cc_library_shared {
+ name: "b",
+ include_build_directory: false,
+ stubs: {
+ symbol_file: "b.map.txt",
+ versions: [
+ "Q",
+ "R",
+ "31",
+ "current",
+ ],
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ makeCcStubSuiteTargets("a", AttrNameToString{
+ "soname": `"a.so"`,
+ "source_library_label": `"//:a"`,
+ "stubs_symbol_file": `"a.map.txt"`,
+ "stubs_versions": `[
+ "29",
+ "30",
+ "31",
+ "current",
+ ]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+ "stubs_symbol_file": `"a.map.txt"`,
+ }),
+ makeCcStubSuiteTargets("b", AttrNameToString{
+ "soname": `"b.so"`,
+ "source_library_label": `"//:b"`,
+ "stubs_symbol_file": `"b.map.txt"`,
+ "stubs_versions": `[
+ "29",
+ "30",
+ "31",
+ "current",
+ ]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+ "stubs_symbol_file": `"b.map.txt"`,
+ }),
+ },
+ })
}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 70c34c0..cd4cf51 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -15,11 +15,12 @@
package bp2build
import (
+ "fmt"
+ "testing"
+
"android/soong/android"
"android/soong/cc"
"android/soong/genrule"
-
- "testing"
)
const (
@@ -64,18 +65,18 @@
ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
}
-func runCcLibraryStaticTestCase(t *testing.T, tc bp2buildTestCase) {
+func runCcLibraryStaticTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "cc_library_static"
- (&tc).moduleTypeUnderTestFactory = cc.LibraryStaticFactory
- runBp2BuildTestCase(t, registerCcLibraryStaticModuleTypes, tc)
+ (&tc).ModuleTypeUnderTest = "cc_library_static"
+ (&tc).ModuleTypeUnderTestFactory = cc.LibraryStaticFactory
+ RunBp2BuildTestCase(t, registerCcLibraryStaticModuleTypes, tc)
}
func TestCcLibraryStaticSimple(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static test",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static test",
+ Filesystem: map[string]string{
// NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
"include_dir_1/include_dir_1_a.h": "",
"include_dir_1/include_dir_1_b.h": "",
@@ -95,7 +96,7 @@
"implicit_include_1.h": "",
"implicit_include_2.h": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_headers {
name: "header_lib_1",
export_include_dirs: ["header_lib_1"],
@@ -171,8 +172,8 @@
// TODO: Also support export_header_lib_headers
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"absolute_includes": `[
"include_dir_1",
"include_dir_2",
@@ -204,17 +205,17 @@
":whole_static_lib_1",
":whole_static_lib_2",
]`,
- "sdk_version": `"current"`,
- "min_sdk_version": `"29"`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
}
func TestCcLibraryStaticSubpackage(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static subpackage test",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static subpackage test",
+ Filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
"subpackage/subpackage_header.h": "",
@@ -228,7 +229,7 @@
"subpackage/subsubpackage/subsubsubpackage/subsubsubpackage_header.h": "",
"subpackage/subsubpackage/subsubsubpackage/subdirectory/subdirectory_header.h": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: [],
@@ -236,8 +237,8 @@
"subpackage",
],
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"absolute_includes": `["subpackage"]`,
"local_includes": `["."]`,
}),
@@ -246,22 +247,22 @@
}
func TestCcLibraryStaticExportIncludeDir(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static export include dir",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static export include dir",
+ Filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
"subpackage/subpackage_header.h": "",
"subpackage/subdirectory/subdirectory_header.h": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
export_include_dirs: ["subpackage"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"export_includes": `["subpackage"]`,
}),
},
@@ -269,22 +270,22 @@
}
func TestCcLibraryStaticExportSystemIncludeDir(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static export system include dir",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static export system include dir",
+ Filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
"subpackage/subpackage_header.h": "",
"subpackage/subdirectory/subdirectory_header.h": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
export_system_include_dirs: ["subpackage"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"export_system_includes": `["subpackage"]`,
}),
},
@@ -292,10 +293,10 @@
}
func TestCcLibraryStaticManyIncludeDirs(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static include_dirs, local_include_dirs, export_include_dirs (b/183742505)",
- dir: "subpackage",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static include_dirs, local_include_dirs, export_include_dirs (b/183742505)",
+ Dir: "subpackage",
+ Filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": `
cc_library_static {
@@ -317,9 +318,9 @@
"subpackage2/header.h": "",
"subpackage3/subsubpackage/header.h": "",
},
- blueprint: soongCcLibraryStaticPreamble,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ Blueprint: soongCcLibraryStaticPreamble,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"absolute_includes": `[
"subpackage/subsubpackage",
"subpackage2",
@@ -335,23 +336,23 @@
}
func TestCcLibraryStaticIncludeBuildDirectoryDisabled(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static include_build_directory disabled",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static include_build_directory disabled",
+ Filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
"subpackage/subpackage_header.h": "",
"subpackage/subdirectory/subdirectory_header.h": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended
local_include_dirs: ["subpackage2"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"absolute_includes": `["subpackage"]`,
"local_includes": `["subpackage2"]`,
}),
@@ -360,9 +361,9 @@
}
func TestCcLibraryStaticIncludeBuildDirectoryEnabled(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static include_build_directory enabled",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static include_build_directory enabled",
+ Filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
"subpackage/subpackage_header.h": "",
@@ -370,15 +371,15 @@
"subpackage2/subpackage2_header.h": "",
"subpackage/subdirectory/subdirectory_header.h": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended
local_include_dirs: ["subpackage2"],
include_build_directory: true,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"absolute_includes": `["subpackage"]`,
"local_includes": `[
"subpackage2",
@@ -390,10 +391,10 @@
}
func TestCcLibraryStaticArchSpecificStaticLib(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch-specific static_libs",
- filesystem: map[string]string{},
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static arch-specific static_libs",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "static_dep",
bazel_module: { bp2build_available: false },
@@ -407,8 +408,8 @@
arch: { arm64: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"implementation_deps": `select({
"//build/bazel/platforms/arch:arm64": [":static_dep"],
"//conditions:default": [],
@@ -423,10 +424,10 @@
}
func TestCcLibraryStaticOsSpecificStaticLib(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static os-specific static_libs",
- filesystem: map[string]string{},
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static os-specific static_libs",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "static_dep",
bazel_module: { bp2build_available: false },
@@ -440,8 +441,8 @@
target: { android: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"implementation_deps": `select({
"//build/bazel/platforms/os:android": [":static_dep"],
"//conditions:default": [],
@@ -456,10 +457,10 @@
}
func TestCcLibraryStaticBaseArchOsSpecificStaticLib(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static base, arch and os-specific static_libs",
- filesystem: map[string]string{},
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static base, arch and os-specific static_libs",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "static_dep",
bazel_module: { bp2build_available: false },
@@ -484,8 +485,8 @@
arch: { arm64: { static_libs: ["static_dep4"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"implementation_deps": `[":static_dep"] + select({
"//build/bazel/platforms/arch:arm64": [":static_dep4"],
"//conditions:default": [],
@@ -500,22 +501,22 @@
}
func TestCcLibraryStaticSimpleExcludeSrcs(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static simple exclude_srcs",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static simple exclude_srcs",
+ Filesystem: map[string]string{
"common.c": "",
"foo-a.c": "",
"foo-excluded.c": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c", "foo-*.c"],
exclude_srcs: ["foo-excluded.c"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs_c": `[
"common.c",
"foo-a.c",
@@ -526,21 +527,21 @@
}
func TestCcLibraryStaticOneArchSrcs(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static one arch specific srcs",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static one arch specific srcs",
+ Filesystem: map[string]string{
"common.c": "",
"foo-arm.c": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c"],
arch: { arm: { srcs: ["foo-arm.c"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": ["foo-arm.c"],
"//conditions:default": [],
@@ -551,15 +552,15 @@
}
func TestCcLibraryStaticOneArchSrcsExcludeSrcs(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static one arch specific srcs and exclude_srcs",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static one arch specific srcs and exclude_srcs",
+ Filesystem: map[string]string{
"common.c": "",
"for-arm.c": "",
"not-for-arm.c": "",
"not-for-anything.c": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c", "not-for-*.c"],
@@ -569,8 +570,8 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": ["for-arm.c"],
"//conditions:default": ["not-for-arm.c"],
@@ -581,16 +582,16 @@
}
func TestCcLibraryStaticTwoArchExcludeSrcs(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch specific exclude_srcs for 2 architectures",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static arch specific exclude_srcs for 2 architectures",
+ Filesystem: map[string]string{
"common.c": "",
"for-arm.c": "",
"for-x86.c": "",
"not-for-arm.c": "",
"not-for-x86.c": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c", "not-for-*.c"],
@@ -601,8 +602,8 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": [
"not-for-x86.c",
@@ -623,9 +624,9 @@
}
func TestCcLibraryStaticFourArchExcludeSrcs(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch specific exclude_srcs for 4 architectures",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static arch specific exclude_srcs for 4 architectures",
+ Filesystem: map[string]string{
"common.c": "",
"for-arm.c": "",
"for-arm64.c": "",
@@ -637,7 +638,7 @@
"not-for-x86_64.c": "",
"not-for-everything.c": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c", "not-for-*.c"],
@@ -650,8 +651,8 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": [
"not-for-arm64.c",
@@ -690,14 +691,14 @@
}
func TestCcLibraryStaticOneArchEmpty(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static one arch empty",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static one arch empty",
+ Filesystem: map[string]string{
"common.cc": "",
"foo-no-arm.cc": "",
"foo-excluded.cc": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.cc", "foo-*.cc"],
@@ -707,8 +708,8 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs": `["common.cc"] + select({
"//build/bazel/platforms/arch:arm": [],
"//conditions:default": ["foo-no-arm.cc"],
@@ -719,15 +720,15 @@
}
func TestCcLibraryStaticOneArchEmptyOtherSet(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static one arch empty other set",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static one arch empty other set",
+ Filesystem: map[string]string{
"common.cc": "",
"foo-no-arm.cc": "",
"x86-only.cc": "",
"foo-excluded.cc": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.cc", "foo-*.cc"],
@@ -738,8 +739,8 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs": `["common.cc"] + select({
"//build/bazel/platforms/arch:arm": [],
"//build/bazel/platforms/arch:x86": [
@@ -754,10 +755,10 @@
}
func TestCcLibraryStaticMultipleDepSameName(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static multiple dep same name panic",
- filesystem: map[string]string{},
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static multiple dep same name panic",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "static_dep",
bazel_module: { bp2build_available: false },
@@ -767,8 +768,8 @@
static_libs: ["static_dep", "static_dep"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"implementation_deps": `[":static_dep"]`,
}),
},
@@ -776,14 +777,14 @@
}
func TestCcLibraryStaticOneMultilibSrcsExcludeSrcs(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static 1 multilib srcs and exclude_srcs",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static 1 multilib srcs and exclude_srcs",
+ Filesystem: map[string]string{
"common.c": "",
"for-lib32.c": "",
"not-for-lib32.c": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c", "not-for-*.c"],
@@ -792,8 +793,8 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": ["for-lib32.c"],
"//build/bazel/platforms/arch:x86": ["for-lib32.c"],
@@ -805,16 +806,16 @@
}
func TestCcLibraryStaticTwoMultilibSrcsExcludeSrcs(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static 2 multilib srcs and exclude_srcs",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static 2 multilib srcs and exclude_srcs",
+ Filesystem: map[string]string{
"common.c": "",
"for-lib32.c": "",
"for-lib64.c": "",
"not-for-lib32.c": "",
"not-for-lib64.c": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c", "not-for-*.c"],
@@ -824,8 +825,8 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": [
"not-for-lib64.c",
@@ -858,9 +859,9 @@
}
func TestCcLibrarySTaticArchMultilibSrcsExcludeSrcs(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch and multilib srcs and exclude_srcs",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static arch and multilib srcs and exclude_srcs",
+ Filesystem: map[string]string{
"common.c": "",
"for-arm.c": "",
"for-arm64.c": "",
@@ -877,7 +878,7 @@
"not-for-lib64.c": "",
"not-for-everything.c": "",
},
- blueprint: soongCcLibraryStaticPreamble + `
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c", "not-for-*.c"],
@@ -895,8 +896,8 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": [
"not-for-arm64.c",
@@ -959,8 +960,8 @@
}
func TestCcLibraryStaticGeneratedHeadersAllPartitions(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Blueprint: soongCcLibraryStaticPreamble + `
genrule {
name: "generated_hdr",
cmd: "nothing to see here",
@@ -980,8 +981,8 @@
export_generated_headers: ["export_generated_hdr"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"export_includes": `["."]`,
"local_includes": `["."]`,
"hdrs": `[":export_generated_hdr"]`,
@@ -1002,10 +1003,12 @@
})
}
+// generated_headers has "variant_prepend" tag. In bp2build output,
+// variant info(select) should go before general info.
func TestCcLibraryStaticArchSrcsExcludeSrcsGeneratedFiles(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch srcs/exclude_srcs with generated files",
- filesystem: map[string]string{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static arch srcs/exclude_srcs with generated files",
+ Filesystem: map[string]string{
"common.cpp": "",
"for-x86.cpp": "",
"not-for-x86.cpp": "",
@@ -1016,7 +1019,7 @@
simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg_x86") +
simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg_android"),
},
- blueprint: soongCcLibraryStaticPreamble +
+ Blueprint: soongCcLibraryStaticPreamble +
simpleModuleDoNotConvertBp2build("genrule", "generated_src") +
simpleModuleDoNotConvertBp2build("genrule", "generated_src_not_x86") +
simpleModuleDoNotConvertBp2build("genrule", "generated_src_android") +
@@ -1048,8 +1051,8 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs": `[
"common.cpp",
":generated_src",
@@ -1065,13 +1068,13 @@
"//build/bazel/platforms/os:android": [":generated_src_android"],
"//conditions:default": [],
})`,
- "hdrs": `["//dep:generated_hdr_other_pkg"] + select({
- "//build/bazel/platforms/arch:x86": ["//dep:generated_hdr_other_pkg_x86"],
- "//conditions:default": [],
- }) + select({
+ "hdrs": `select({
"//build/bazel/platforms/os:android": ["//dep:generated_hdr_other_pkg_android"],
"//conditions:default": [],
- })`,
+ }) + select({
+ "//build/bazel/platforms/arch:x86": ["//dep:generated_hdr_other_pkg_x86"],
+ "//conditions:default": [],
+ }) + ["//dep:generated_hdr_other_pkg"]`,
"local_includes": `["."]`,
"export_absolute_includes": `["dep"]`,
}),
@@ -1080,10 +1083,10 @@
}
func TestCcLibraryStaticGetTargetProperties(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
- description: "cc_library_static complex GetTargetProperties",
- blueprint: soongCcLibraryStaticPreamble + `
+ Description: "cc_library_static complex GetTargetProperties",
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
target: {
@@ -1111,8 +1114,8 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"srcs_c": `select({
"//build/bazel/platforms/os:android": ["android_src.c"],
"//conditions:default": [],
@@ -1131,9 +1134,9 @@
}
func TestCcLibraryStaticProductVariableSelects(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static product variable selects",
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static product variable selects",
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c"],
@@ -1150,8 +1153,8 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"copts": `select({
"//build/bazel/product_variables:binder32bit": ["-Wbinder32bit"],
"//conditions:default": [],
@@ -1169,10 +1172,10 @@
}
func TestCcLibraryStaticProductVariableArchSpecificSelects(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch-specific product variable selects",
- filesystem: map[string]string{},
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static arch-specific product variable selects",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.c"],
@@ -1210,8 +1213,8 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"copts": `select({
"//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
"//conditions:default": [],
@@ -1235,10 +1238,10 @@
}
func TestCcLibraryStaticProductVariableStringReplacement(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static product variable string replacement",
- filesystem: map[string]string{},
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static product variable string replacement",
+ Filesystem: map[string]string{},
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
srcs: ["common.S"],
@@ -1249,8 +1252,8 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
"asflags": `select({
"//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
"//conditions:default": [],
@@ -1262,17 +1265,17 @@
}
func TestStaticLibrary_SystemSharedLibsRootEmpty(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_lib empty root",
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_lib empty root",
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "root_empty",
system_shared_libs: [],
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "root_empty", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "root_empty", AttrNameToString{
"system_dynamic_deps": `[]`,
}),
},
@@ -1280,9 +1283,9 @@
}
func TestStaticLibrary_SystemSharedLibsStaticEmpty(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_lib empty static default",
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_lib empty static default",
+ Blueprint: soongCcLibraryStaticPreamble + `
cc_defaults {
name: "static_empty_defaults",
static: {
@@ -1295,8 +1298,8 @@
defaults: ["static_empty_defaults"],
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "static_empty", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "static_empty", AttrNameToString{
"system_dynamic_deps": `[]`,
}),
},
@@ -1304,9 +1307,14 @@
}
func TestStaticLibrary_SystemSharedLibsBionicEmpty(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_lib empty for bionic variant",
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_lib empty for bionic variant",
+ Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library_static {
name: "target_bionic_empty",
target: {
@@ -1317,9 +1325,12 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "target_bionic_empty", attrNameToString{
- "system_dynamic_deps": `[]`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "target_bionic_empty", AttrNameToString{
+ "system_dynamic_deps": `select({
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+ "//conditions:default": [],
+ })`,
}),
},
})
@@ -1330,9 +1341,14 @@
// The correct behavior would be if bp2build wrote `system_dynamic_deps = []`
// only for linux_bionic, but `android` had `["libc", "libdl", "libm"].
// b/195791252 tracks the fix.
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_lib empty for linux_bionic variant",
- blueprint: soongCcLibraryStaticPreamble + `
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_lib empty for linux_bionic variant",
+ Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library_static {
name: "target_linux_bionic_empty",
target: {
@@ -1343,8 +1359,65 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "target_linux_bionic_empty", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "target_linux_bionic_empty", AttrNameToString{
+ "system_dynamic_deps": `select({
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestStaticLibrary_SystemSharedLibsMuslEmpty(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_lib empty for musl variant",
+ Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+ name: "target_musl_empty",
+ target: {
+ musl: {
+ system_shared_libs: [],
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "target_musl_empty", AttrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ },
+ })
+}
+
+func TestStaticLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_lib empty for linux_musl variant",
+ Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+ name: "target_linux_musl_empty",
+ target: {
+ linux_musl: {
+ system_shared_libs: [],
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "target_linux_musl_empty", AttrNameToString{
"system_dynamic_deps": `[]`,
}),
},
@@ -1352,10 +1425,15 @@
}
func TestStaticLibrary_SystemSharedLibsBionic(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_libs set for bionic variant",
- blueprint: soongCcLibraryStaticPreamble +
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_libs set for bionic variant",
+ Blueprint: soongCcLibraryStaticPreamble +
simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library_static {
name: "target_bionic",
target: {
@@ -1366,11 +1444,12 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "target_bionic", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "target_bionic", AttrNameToString{
"system_dynamic_deps": `select({
"//build/bazel/platforms/os:android": [":libc"],
"//build/bazel/platforms/os:linux_bionic": [":libc"],
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
"//conditions:default": [],
})`,
}),
@@ -1379,11 +1458,16 @@
}
func TestStaticLibrary_SystemSharedLibsLinuxRootAndLinuxBionic(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_libs set for root and linux_bionic variant",
- blueprint: soongCcLibraryStaticPreamble +
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_libs set for root and linux_bionic variant",
+ Blueprint: soongCcLibraryStaticPreamble +
simpleModuleDoNotConvertBp2build("cc_library", "libc") +
simpleModuleDoNotConvertBp2build("cc_library", "libm") + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library_static {
name: "target_linux_bionic",
system_shared_libs: ["libc"],
@@ -1395,10 +1479,11 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "target_linux_bionic", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "target_linux_bionic", AttrNameToString{
"system_dynamic_deps": `[":libc"] + select({
"//build/bazel/platforms/os:linux_bionic": [":libm"],
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
"//conditions:default": [],
})`,
}),
@@ -1407,10 +1492,20 @@
}
func TestCcLibrarystatic_SystemSharedLibUsedAsDep(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_lib empty for linux_bionic variant",
- blueprint: soongCcLibraryStaticPreamble +
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_lib empty for linux_bionic variant",
+ Blueprint: soongCcLibraryStaticPreamble +
simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+
+cc_library {
+ name: "libm",
+ stubs: {
+ symbol_file: "libm.map.txt",
+ versions: ["current"],
+ },
+ bazel_module: { bp2build_available: false },
+}
+
cc_library_static {
name: "used_in_bionic_oses",
target: {
@@ -1422,41 +1517,69 @@
},
},
include_build_directory: false,
+ apex_available: ["foo"],
}
cc_library_static {
name: "all",
shared_libs: ["libc"],
include_build_directory: false,
+ apex_available: ["foo"],
}
cc_library_static {
name: "keep_for_empty_system_shared_libs",
shared_libs: ["libc"],
- system_shared_libs: [],
+ system_shared_libs: [],
include_build_directory: false,
+ apex_available: ["foo"],
+}
+
+cc_library_static {
+ name: "used_with_stubs",
+ shared_libs: ["libm"],
+ include_build_directory: false,
+ apex_available: ["foo"],
+}
+
+cc_library_static {
+ name: "keep_with_stubs",
+ shared_libs: ["libm"],
+ system_shared_libs: [],
+ include_build_directory: false,
+ apex_available: ["foo"],
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "all", attrNameToString{
- "implementation_dynamic_deps": `select({
- "//build/bazel/platforms/os:android": [],
- "//build/bazel/platforms/os:linux_bionic": [],
- "//conditions:default": [":libc"],
- })`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "all", AttrNameToString{
+ "tags": `["apex_available=foo"]`,
}),
- makeBazelTarget("cc_library_static", "keep_for_empty_system_shared_libs", attrNameToString{
+ MakeBazelTarget("cc_library_static", "keep_for_empty_system_shared_libs", AttrNameToString{
"implementation_dynamic_deps": `[":libc"]`,
"system_dynamic_deps": `[]`,
+ "tags": `["apex_available=foo"]`,
}),
- makeBazelTarget("cc_library_static", "used_in_bionic_oses", attrNameToString{}),
+ MakeBazelTarget("cc_library_static", "keep_with_stubs", AttrNameToString{
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:libm"],
+ "//conditions:default": [":libm"],
+ })`,
+ "system_dynamic_deps": `[]`,
+ "tags": `["apex_available=foo"]`,
+ }),
+ MakeBazelTarget("cc_library_static", "used_in_bionic_oses", AttrNameToString{
+ "tags": `["apex_available=foo"]`,
+ }),
+ MakeBazelTarget("cc_library_static", "used_with_stubs", AttrNameToString{
+ "tags": `["apex_available=foo"]`,
+ }),
},
})
}
func TestCcLibraryStaticProto(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- blueprint: soongCcProtoPreamble + `cc_library_static {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Blueprint: soongCcProtoPreamble + `cc_library_static {
name: "foo",
srcs: ["foo.proto"],
proto: {
@@ -1464,12 +1587,12 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
- }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[":foo_proto"]`,
- }), makeBazelTarget("cc_library_static", "foo", attrNameToString{
+ }), MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
"deps": `[":libprotobuf-cpp-lite"]`,
"whole_archive_deps": `[":foo_cc_proto_lite"]`,
}),
@@ -1478,34 +1601,434 @@
}
func TestCcLibraryStaticUseVersionLib(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- blueprint: soongCcProtoPreamble + `cc_library_static {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Filesystem: map[string]string{
+ soongCcVersionLibBpPath: soongCcVersionLibBp,
+ },
+ Blueprint: soongCcProtoPreamble + `cc_library_static {
name: "foo",
use_version_lib: true,
+ static_libs: ["libbuildversion"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo", attrNameToString{
- "use_version_lib": "True",
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "implementation_whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticUseVersionLibHasDep(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Filesystem: map[string]string{
+ soongCcVersionLibBpPath: soongCcVersionLibBp,
+ },
+ Blueprint: soongCcProtoPreamble + `cc_library_static {
+ name: "foo",
+ use_version_lib: true,
+ whole_static_libs: ["libbuildversion"],
+ include_build_directory: false,
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
}),
},
})
}
func TestCcLibraryStaticStdInFlags(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- blueprint: soongCcProtoPreamble + `cc_library_static {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Blueprint: soongCcProtoPreamble + `cc_library_static {
name: "foo",
cflags: ["-std=candcpp"],
conlyflags: ["-std=conly"],
cppflags: ["-std=cpp"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_library_static", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
"conlyflags": `["-std=conly"]`,
"cppflags": `["-std=cpp"]`,
}),
},
})
}
+
+func TestCcLibraryStaticStl(t *testing.T) {
+ testCases := []struct {
+ desc string
+ prop string
+ attr AttrNameToString
+ }{
+ {
+ desc: "c++_shared deduped to libc++",
+ prop: `stl: "c++_shared",`,
+ attr: AttrNameToString{
+ "stl": `"libc++"`,
+ },
+ },
+ {
+ desc: "libc++ to libc++",
+ prop: `stl: "libc++",`,
+ attr: AttrNameToString{
+ "stl": `"libc++"`,
+ },
+ },
+ {
+ desc: "c++_static to libc++_static",
+ prop: `stl: "c++_static",`,
+ attr: AttrNameToString{
+ "stl": `"libc++_static"`,
+ },
+ },
+ {
+ desc: "libc++_static to libc++_static",
+ prop: `stl: "libc++_static",`,
+ attr: AttrNameToString{
+ "stl": `"libc++_static"`,
+ },
+ },
+ {
+ desc: "system to system",
+ prop: `stl: "system",`,
+ attr: AttrNameToString{
+ "stl": `"system"`,
+ },
+ },
+ {
+ desc: "none to none",
+ prop: `stl: "none",`,
+ attr: AttrNameToString{
+ "stl": `"none"`,
+ },
+ },
+ {
+ desc: "empty to empty",
+ attr: AttrNameToString{},
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(*testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Blueprint: fmt.Sprintf(`cc_library_static {
+ name: "foo",
+ include_build_directory: false,
+ %s
+}`, tc.prop),
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", tc.attr),
+ },
+ })
+ })
+ }
+}
+
+func TestCCLibraryStaticRuntimeDeps(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Blueprint: `cc_library_shared {
+ name: "bar",
+}
+
+cc_library_static {
+ name: "foo",
+ runtime_libs: ["foo"],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "bar", AttrNameToString{
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "runtime_deps": `[":foo"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithSyspropSrcs(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static with sysprop sources",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ srcs: [
+ "bar.sysprop",
+ "baz.sysprop",
+ "blah.cpp",
+ ],
+ min_sdk_version: "5",
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{
+ "srcs": `[
+ "bar.sysprop",
+ "baz.sysprop",
+ ]`,
+ }),
+ MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{
+ "dep": `":foo_sysprop_library"`,
+ "min_sdk_version": `"5"`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithSyspropSrcsSomeConfigs(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static with sysprop sources in some configs but not others",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ srcs: [
+ "blah.cpp",
+ ],
+ target: {
+ android: {
+ srcs: ["bar.sysprop"],
+ },
+ },
+ min_sdk_version: "5",
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{
+ "srcs": `select({
+ "//build/bazel/platforms/os:android": ["bar.sysprop"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{
+ "dep": `":foo_sysprop_library"`,
+ "min_sdk_version": `"5"`,
+ }),
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "srcs": `["blah.cpp"]`,
+ "local_includes": `["."]`,
+ "min_sdk_version": `"5"`,
+ "whole_archive_deps": `select({
+ "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithIntegerOverflowProperty(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static has correct features when integer_overflow property is provided",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ sanitize: {
+ integer_overflow: true,
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "features": `["ubsan_integer_overflow"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithMiscUndefinedProperty(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static has correct features when misc_undefined property is provided",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ sanitize: {
+ misc_undefined: ["undefined", "nullability"],
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithUBSanPropertiesArchSpecific(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static has correct feature select when UBSan props are specified in arch specific blocks",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ sanitize: {
+ misc_undefined: ["undefined", "nullability"],
+ },
+ target: {
+ android: {
+ sanitize: {
+ misc_undefined: ["alignment"],
+ },
+ },
+ linux_glibc: {
+ sanitize: {
+ integer_overflow: true,
+ },
+ },
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "features": `[
+ "ubsan_undefined",
+ "ubsan_nullability",
+ ] + select({
+ "//build/bazel/platforms/os:android": ["ubsan_alignment"],
+ "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithThinLto(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static has correct features when thin lto is enabled",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ lto: {
+ thin: true,
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "features": `["android_thin_lto"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithLtoNever(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static has correct features when thin lto is enabled",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ lto: {
+ never: true,
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "features": `["-android_thin_lto"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithThinLtoArchSpecific(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static has correct features when LTO differs across arch and os variants",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ },
+ },
+ },
+ arch: {
+ riscv64: {
+ lto: {
+ thin: false,
+ },
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+ "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+ "//conditions:default": [],
+ })`}),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static has correct features when LTO disabled by default but enabled on a particular variant",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ lto: {
+ never: true,
+ },
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ never: false,
+ },
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["android_thin_lto"],
+ "//conditions:default": ["-android_thin_lto"],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticWithThinLtoAndWholeProgramVtables(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static has correct features when thin lto is enabled with whole_program_vtables",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ lto: {
+ thin: true,
+ },
+ whole_program_vtables: true,
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "features": `[
+ "android_thin_lto",
+ "android_thin_lto_whole_program_vtables",
+ ]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index ea58086..eab84e1 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -24,25 +24,26 @@
func registerCcObjectModuleTypes(ctx android.RegistrationContext) {
// Always register cc_defaults module factory
ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() })
+ ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
}
-func runCcObjectTestCase(t *testing.T, tc bp2buildTestCase) {
+func runCcObjectTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "cc_object"
- (&tc).moduleTypeUnderTestFactory = cc.ObjectFactory
- runBp2BuildTestCase(t, registerCcObjectModuleTypes, tc)
+ (&tc).ModuleTypeUnderTest = "cc_object"
+ (&tc).ModuleTypeUnderTestFactory = cc.ObjectFactory
+ RunBp2BuildTestCase(t, registerCcObjectModuleTypes, tc)
}
func TestCcObjectSimple(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- description: "simple cc_object generates cc_object with include header dep",
- filesystem: map[string]string{
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "simple cc_object generates cc_object with include header dep",
+ Filesystem: map[string]string{
"a/b/foo.h": "",
"a/b/bar.h": "",
"a/b/exclude.c": "",
"a/b/c.c": "",
},
- blueprint: `cc_object {
+ Blueprint: `cc_object {
name: "foo",
local_include_dirs: ["include"],
system_shared_libs: [],
@@ -57,10 +58,11 @@
exclude_srcs: ["a/b/exclude.c"],
sdk_version: "current",
min_sdk_version: "29",
+ crt: true,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
"copts": `[
"-fno-addrsig",
"-Wno-gcc-compat",
@@ -73,16 +75,17 @@
]`,
"srcs": `["a/b/c.c"]`,
"system_dynamic_deps": `[]`,
- "sdk_version": `"current"`,
- "min_sdk_version": `"29"`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
+ "crt": "True",
}),
},
})
}
func TestCcObjectDefaults(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- blueprint: `cc_object {
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
srcs: [
@@ -105,8 +108,8 @@
],
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
"copts": `[
"-Werror",
"-fno-addrsig",
@@ -119,13 +122,13 @@
}
func TestCcObjectCcObjetDepsInObjs(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object with cc_object deps in objs props",
- filesystem: map[string]string{
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "cc_object with cc_object deps in objs props",
+ Filesystem: map[string]string{
"a/b/c.c": "",
"x/y/z.c": "",
},
- blueprint: `cc_object {
+ Blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
srcs: ["a/b/c.c"],
@@ -140,14 +143,14 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "bar", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "bar", AttrNameToString{
"copts": `["-fno-addrsig"]`,
"srcs": `["x/y/z.c"]`,
"system_dynamic_deps": `[]`,
- }), makeBazelTarget("cc_object", "foo", attrNameToString{
+ }), MakeBazelTarget("cc_object", "foo", AttrNameToString{
"copts": `["-fno-addrsig"]`,
- "deps": `[":bar"]`,
+ "objs": `[":bar"]`,
"srcs": `["a/b/c.c"]`,
"system_dynamic_deps": `[]`,
}),
@@ -156,21 +159,21 @@
}
func TestCcObjectIncludeBuildDirFalse(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object with include_build_dir: false",
- filesystem: map[string]string{
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "cc_object with include_build_dir: false",
+ Filesystem: map[string]string{
"a/b/c.c": "",
"x/y/z.c": "",
},
- blueprint: `cc_object {
+ Blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
srcs: ["a/b/c.c"],
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
"copts": `["-fno-addrsig"]`,
"srcs": `["a/b/c.c"]`,
"system_dynamic_deps": `[]`,
@@ -180,9 +183,9 @@
}
func TestCcObjectProductVariable(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object with product variable",
- blueprint: `cc_object {
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "cc_object with product variable",
+ Blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
include_build_directory: false,
@@ -194,8 +197,8 @@
srcs: ["src.S"],
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
"asflags": `select({
"//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
"//conditions:default": [],
@@ -209,9 +212,9 @@
}
func TestCcObjectCflagsOneArch(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting cflags for one arch",
- blueprint: `cc_object {
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "cc_object setting cflags for one arch",
+ Blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
srcs: ["a.cpp"],
@@ -226,8 +229,8 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
"copts": `["-fno-addrsig"] + select({
"//build/bazel/platforms/arch:x86": ["-fPIC"],
"//conditions:default": [],
@@ -243,9 +246,9 @@
}
func TestCcObjectCflagsFourArch(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting cflags for 4 architectures",
- blueprint: `cc_object {
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "cc_object setting cflags for 4 architectures",
+ Blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
srcs: ["base.cpp"],
@@ -270,8 +273,8 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
"copts": `["-fno-addrsig"] + select({
"//build/bazel/platforms/arch:arm": ["-Wall"],
"//build/bazel/platforms/arch:arm64": ["-Wall"],
@@ -293,17 +296,17 @@
}
func TestCcObjectLinkerScript(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting linker_script",
- blueprint: `cc_object {
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "cc_object setting linker_script",
+ Blueprint: `cc_object {
name: "foo",
srcs: ["base.cpp"],
linker_script: "bunny.lds",
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
"copts": `["-fno-addrsig"]`,
"linker_script": `"bunny.lds"`,
"srcs": `["base.cpp"]`,
@@ -313,9 +316,9 @@
}
func TestCcObjectDepsAndLinkerScriptSelects(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting deps and linker_script across archs",
- blueprint: `cc_object {
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "cc_object setting deps and linker_script across archs",
+ Blueprint: `cc_object {
name: "foo",
srcs: ["base.cpp"],
arch: {
@@ -359,10 +362,10 @@
bazel_module: { bp2build_available: false },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
"copts": `["-fno-addrsig"]`,
- "deps": `select({
+ "objs": `select({
"//build/bazel/platforms/arch:arm": [":arm_obj"],
"//build/bazel/platforms/arch:x86": [":x86_obj"],
"//build/bazel/platforms/arch:x86_64": [":x86_64_obj"],
@@ -381,9 +384,9 @@
}
func TestCcObjectSelectOnLinuxAndBionicArchs(t *testing.T) {
- runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting srcs based on linux and bionic archs",
- blueprint: `cc_object {
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "cc_object setting srcs based on linux and bionic archs",
+ Blueprint: `cc_object {
name: "foo",
srcs: ["base.cpp"],
target: {
@@ -400,8 +403,8 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("cc_object", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
"copts": `["-fno-addrsig"]`,
"srcs": `["base.cpp"] + select({
"//build/bazel/platforms/os_arch:android_arm64": [
@@ -414,6 +417,7 @@
"bionic_arm64.cpp",
],
"//build/bazel/platforms/os_arch:linux_glibc_x86": ["linux_x86.cpp"],
+ "//build/bazel/platforms/os_arch:linux_musl_arm64": ["linux_arm64.cpp"],
"//build/bazel/platforms/os_arch:linux_musl_x86": ["linux_x86.cpp"],
"//conditions:default": [],
})`,
@@ -421,3 +425,56 @@
},
})
}
+
+func TestCcObjectHeaderLib(t *testing.T) {
+ runCcObjectTestCase(t, Bp2buildTestCase{
+ Description: "simple cc_object generates cc_object with include header dep",
+ Filesystem: map[string]string{
+ "a/b/foo.h": "",
+ "a/b/bar.h": "",
+ "a/b/exclude.c": "",
+ "a/b/c.c": "",
+ },
+ Blueprint: `cc_object {
+ name: "foo",
+ header_libs: ["libheaders"],
+ system_shared_libs: [],
+ cflags: [
+ "-Wno-gcc-compat",
+ "-Wall",
+ "-Werror",
+ ],
+ srcs: [
+ "a/b/*.c"
+ ],
+ exclude_srcs: ["a/b/exclude.c"],
+ sdk_version: "current",
+ min_sdk_version: "29",
+}
+
+cc_library_headers {
+ name: "libheaders",
+ export_include_dirs: ["include"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_object", "foo", AttrNameToString{
+ "copts": `[
+ "-fno-addrsig",
+ "-Wno-gcc-compat",
+ "-Wall",
+ "-Werror",
+ ]`,
+ "deps": `[":libheaders"]`,
+ "local_includes": `["."]`,
+ "srcs": `["a/b/c.c"]`,
+ "system_dynamic_deps": `[]`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
+ }),
+ MakeBazelTarget("cc_library_headers", "libheaders", AttrNameToString{
+ "export_includes": `["include"]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/cc_prebuilt_binary_conversion_test.go b/bp2build/cc_prebuilt_binary_conversion_test.go
new file mode 100644
index 0000000..0e8048c
--- /dev/null
+++ b/bp2build/cc_prebuilt_binary_conversion_test.go
@@ -0,0 +1,125 @@
+// 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 runCcPrebuiltBinaryTestCase(t *testing.T, testCase Bp2buildTestCase) {
+ t.Helper()
+ description := fmt.Sprintf("cc_prebuilt_binary: %s", testCase.Description)
+ testCase.ModuleTypeUnderTest = "cc_prebuilt_binary"
+ testCase.ModuleTypeUnderTestFactory = cc.PrebuiltBinaryFactory
+ testCase.Description = description
+ t.Run(description, func(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCaseSimple(t, testCase)
+ })
+}
+
+func TestPrebuiltBinary(t *testing.T) {
+ runCcPrebuiltBinaryTestCase(t,
+ Bp2buildTestCase{
+ Description: "simple",
+ Filesystem: map[string]string{
+ "bin": "",
+ },
+ Blueprint: `
+cc_prebuilt_binary {
+ name: "bintest",
+ srcs: ["bin"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_binary", "bintest", AttrNameToString{
+ "src": `"bin"`,
+ })},
+ })
+}
+
+func TestPrebuiltBinaryWithStrip(t *testing.T) {
+ runCcPrebuiltBinaryTestCase(t,
+ Bp2buildTestCase{
+ Description: "with strip",
+ Filesystem: map[string]string{
+ "bin": "",
+ },
+ Blueprint: `
+cc_prebuilt_binary {
+ name: "bintest",
+ srcs: ["bin"],
+ strip: { all: true },
+ bazel_module: { bp2build_available: true },
+}`, ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_binary", "bintest", AttrNameToString{
+ "src": `"bin"`,
+ "strip": `{
+ "all": True,
+ }`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltBinaryWithArchVariance(t *testing.T) {
+ runCcPrebuiltBinaryTestCase(t,
+ Bp2buildTestCase{
+ Description: "with arch variance",
+ Filesystem: map[string]string{
+ "bina": "",
+ "binb": "",
+ },
+ Blueprint: `
+cc_prebuilt_binary {
+ name: "bintest",
+ arch: {
+ arm64: { srcs: ["bina"], },
+ arm: { srcs: ["binb"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`, ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_binary", "bintest", AttrNameToString{
+ "src": `select({
+ "//build/bazel/platforms/arch:arm": "binb",
+ "//build/bazel/platforms/arch:arm64": "bina",
+ "//conditions:default": None,
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltBinaryMultipleSrcsFails(t *testing.T) {
+ runCcPrebuiltBinaryTestCase(t,
+ Bp2buildTestCase{
+ Description: "fails because multiple sources",
+ Filesystem: map[string]string{
+ "bina": "",
+ "binb": "",
+ },
+ Blueprint: `
+cc_prebuilt_binary {
+ name: "bintest",
+ srcs: ["bina", "binb"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedErr: fmt.Errorf("Expected at most one source file"),
+ })
+}
+
+// TODO: nosrcs test
diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go
index 41dbe6a..b88960e 100644
--- a/bp2build/cc_prebuilt_library_conversion_test.go
+++ b/bp2build/cc_prebuilt_library_conversion_test.go
@@ -20,26 +20,35 @@
"android/soong/cc"
)
+func runCcPrebuiltLibraryTestCase(t *testing.T, tc Bp2buildTestCase) {
+ t.Helper()
+ (&tc).ModuleTypeUnderTest = "cc_prebuilt_library"
+ (&tc).ModuleTypeUnderTestFactory = cc.PrebuiltLibraryFactory
+ RunBp2BuildTestCaseSimple(t, tc)
+}
+
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{
+ runCcPrebuiltLibraryTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library static and shared simple",
+ Filesystem: map[string]string{
"libf.so": "",
},
- blueprint: `
+ 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{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
"static_library": `"libf.so"`,
}),
- makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "alwayslink": "True",
+ }),
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
"shared_library": `"libf.so"`,
}),
},
@@ -47,16 +56,14 @@
}
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{
+ runCcPrebuiltLibraryTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library with arch variance",
+ Filesystem: map[string]string{
"libf.so": "",
"libg.so": "",
},
- blueprint: `
+ Blueprint: `
cc_prebuilt_library {
name: "libtest",
arch: {
@@ -65,15 +72,21 @@
},
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_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{
+ })`}),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+ "alwayslink": "True",
+ "static_library": `select({
+ "//build/bazel/platforms/arch:arm": "libg.so",
+ "//build/bazel/platforms/arch:arm64": "libf.so",
+ "//conditions:default": None,
+ })`}),
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
"shared_library": `select({
"//build/bazel/platforms/arch:arm": "libg.so",
"//build/bazel/platforms/arch:arm64": "libf.so",
@@ -85,17 +98,15 @@
}
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/": "",
+ runCcPrebuiltLibraryTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library additional attributes",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "testdir/1/include.h": "",
+ "testdir/2/other.h": "",
},
- blueprint: `
+ Blueprint: `
cc_prebuilt_library {
name: "libtest",
srcs: ["libf.so"],
@@ -103,31 +114,36 @@
export_system_include_dirs: ["testdir/2/"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_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"`,
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "export_includes": `["testdir/1/"]`,
+ "export_system_includes": `["testdir/2/"]`,
+ "alwayslink": "True",
+ }),
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+ "shared_library": `"libf.so"`,
+ "export_includes": `["testdir/1/"]`,
+ "export_system_includes": `["testdir/2/"]`,
}),
},
})
}
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{
+ runCcPrebuiltLibraryTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library with shared stanza fails because multiple sources",
+ Filesystem: map[string]string{
"libf.so": "",
"libg.so": "",
},
- blueprint: `
+ Blueprint: `
cc_prebuilt_library {
name: "libtest",
srcs: ["libf.so"],
@@ -136,21 +152,21 @@
},
bazel_module: { bp2build_available: true },
}`,
- expectedErr: fmt.Errorf("Expected at most one source file"),
+ 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{
+ 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: `
+ Blueprint: `
cc_prebuilt_library {
name: "libtest",
srcs: ["libf.so"],
@@ -159,21 +175,19 @@
},
bazel_module: { bp2build_available: true },
}`,
- expectedErr: fmt.Errorf("Expected at most one source file"),
+ 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{
+ runCcPrebuiltLibraryTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library with both shared and static stanzas",
+ Filesystem: map[string]string{
"libf.so": "",
"libg.so": "",
},
- blueprint: `
+ Blueprint: `
cc_prebuilt_library {
name: "libtest",
static: {
@@ -184,11 +198,15 @@
},
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
"static_library": `"libf.so"`,
}),
- makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "alwayslink": "True",
+ }),
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
"shared_library": `"libg.so"`,
}),
},
@@ -197,11 +215,9 @@
// TODO(b/228623543): When this bug is fixed, enable this test
//func TestPrebuiltLibraryOnlyShared(t *testing.T) {
-// runBp2BuildTestCaseSimple(t,
+// runCcPrebuiltLibraryTestCase(t,
// bp2buildTestCase{
// description: "prebuilt library shared only",
-// moduleTypeUnderTest: "cc_prebuilt_library",
-// moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
// filesystem: map[string]string{
// "libf.so": "",
// },
@@ -215,7 +231,7 @@
// bazel_module: { bp2build_available: true },
//}`,
// expectedBazelTargets: []string{
-// makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+// makeBazelTarget("cc_prebuilt_library_shared", "libtest", attrNameToString{
// "shared_library": `"libf.so"`,
// }),
// },
@@ -224,11 +240,9 @@
// TODO(b/228623543): When this bug is fixed, enable this test
//func TestPrebuiltLibraryOnlyStatic(t *testing.T) {
-// runBp2BuildTestCaseSimple(t,
+// runCcPrebuiltLibraryTestCase(t,
// bp2buildTestCase{
// description: "prebuilt library static only",
-// moduleTypeUnderTest: "cc_prebuilt_library",
-// moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
// filesystem: map[string]string{
// "libf.so": "",
// },
@@ -242,9 +256,107 @@
// bazel_module: { bp2build_available: true },
//}`,
// expectedBazelTargets: []string{
-// makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+// makeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
// "static_library": `"libf.so"`,
// }),
+// makeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_always", attrNameToString{
+// "static_library": `"libf.so"`,
+// "alwayslink": "True",
+// }),
// },
// })
//}
+
+func TestPrebuiltLibraryWithExportIncludesArchVariant(t *testing.T) {
+ runCcPrebuiltLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_prebuilt_library correctly translates export_includes with arch variance",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ Blueprint: `
+cc_prebuilt_library {
+ name: "libtest",
+ srcs: ["libf.so"],
+ arch: {
+ arm: { export_include_dirs: ["testdir/1/"], },
+ arm64: { export_include_dirs: ["testdir/2/"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+ "shared_library": `"libf.so"`,
+ "export_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "export_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+ "alwayslink": "True",
+ "static_library": `"libf.so"`,
+ "export_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibraryWithExportSystemIncludesArchVariant(t *testing.T) {
+ runCcPrebuiltLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_prebuilt_ibrary correctly translates export_system_includes with arch variance",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ Blueprint: `
+cc_prebuilt_library {
+ name: "libtest",
+ srcs: ["libf.so"],
+ arch: {
+ arm: { export_system_include_dirs: ["testdir/1/"], },
+ arm64: { export_system_include_dirs: ["testdir/2/"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+ "shared_library": `"libf.so"`,
+ "export_system_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "export_system_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+ "alwayslink": "True",
+ "static_library": `"libf.so"`,
+ "export_system_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/cc_prebuilt_library_shared_conversion_test.go b/bp2build/cc_prebuilt_library_shared_conversion_test.go
new file mode 100644
index 0000000..9e975ae
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_shared_conversion_test.go
@@ -0,0 +1,165 @@
+// 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 (
+ "testing"
+
+ "android/soong/cc"
+)
+
+func runCcPrebuiltLibrarySharedTestCase(t *testing.T, tc Bp2buildTestCase) {
+ t.Parallel()
+ t.Helper()
+ (&tc).ModuleTypeUnderTest = "cc_prebuilt_library_shared"
+ (&tc).ModuleTypeUnderTestFactory = cc.PrebuiltSharedLibraryFactory
+ RunBp2BuildTestCaseSimple(t, tc)
+}
+
+func TestPrebuiltLibrarySharedSimple(t *testing.T) {
+ runCcPrebuiltLibrarySharedTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library shared simple",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ },
+ Blueprint: `
+cc_prebuilt_library_shared {
+ name: "libtest",
+ srcs: ["libf.so"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+ "shared_library": `"libf.so"`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibrarySharedWithArchVariance(t *testing.T) {
+ runCcPrebuiltLibrarySharedTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library shared with arch variance",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ Blueprint: `
+cc_prebuilt_library_shared {
+ name: "libtest",
+ arch: {
+ arm64: { srcs: ["libf.so"], },
+ arm: { srcs: ["libg.so"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_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 TestPrebuiltLibrarySharedAdditionalAttrs(t *testing.T) {
+ runCcPrebuiltLibrarySharedTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library shared additional attributes",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "testdir/1/include.h": "",
+ "testdir/2/other.h": "",
+ },
+ Blueprint: `
+cc_prebuilt_library_shared {
+ name: "libtest",
+ srcs: ["libf.so"],
+ export_include_dirs: ["testdir/1/"],
+ export_system_include_dirs: ["testdir/2/"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+ "shared_library": `"libf.so"`,
+ "export_includes": `["testdir/1/"]`,
+ "export_system_includes": `["testdir/2/"]`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibrarySharedWithExportIncludesArchVariant(t *testing.T) {
+ runCcPrebuiltLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_prebuilt_library_shared correctly translates export_includes with arch variance",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ Blueprint: `
+cc_prebuilt_library_shared {
+ name: "libtest",
+ srcs: ["libf.so"],
+ arch: {
+ arm: { export_include_dirs: ["testdir/1/"], },
+ arm64: { export_include_dirs: ["testdir/2/"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+ "shared_library": `"libf.so"`,
+ "export_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibrarySharedWithExportSystemIncludesArchVariant(t *testing.T) {
+ runCcPrebuiltLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_prebuilt_library_shared correctly translates export_system_includes with arch variance",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ Blueprint: `
+cc_prebuilt_library_shared {
+ name: "libtest",
+ srcs: ["libf.so"],
+ arch: {
+ arm: { export_system_include_dirs: ["testdir/1/"], },
+ arm64: { export_system_include_dirs: ["testdir/2/"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+ "shared_library": `"libf.so"`,
+ "export_system_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/cc_prebuilt_library_shared_test.go b/bp2build/cc_prebuilt_library_shared_test.go
index 57905e5..58c0a70 100644
--- a/bp2build/cc_prebuilt_library_shared_test.go
+++ b/bp2build/cc_prebuilt_library_shared_test.go
@@ -8,22 +8,22 @@
)
func TestSharedPrebuiltLibrary(t *testing.T) {
- runBp2BuildTestCaseSimple(t,
- bp2buildTestCase{
- description: "prebuilt library shared simple",
- moduleTypeUnderTest: "cc_prebuilt_library_shared",
- moduleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory,
- filesystem: map[string]string{
+ RunBp2BuildTestCaseSimple(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library shared simple",
+ ModuleTypeUnderTest: "cc_prebuilt_library_shared",
+ ModuleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory,
+ Filesystem: map[string]string{
"libf.so": "",
},
- blueprint: `
+ Blueprint: `
cc_prebuilt_library_shared {
name: "libtest",
srcs: ["libf.so"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
"shared_library": `"libf.so"`,
}),
},
@@ -31,16 +31,16 @@
}
func TestSharedPrebuiltLibraryWithArchVariance(t *testing.T) {
- runBp2BuildTestCaseSimple(t,
- bp2buildTestCase{
- description: "prebuilt library shared with arch variance",
- moduleTypeUnderTest: "cc_prebuilt_library_shared",
- moduleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory,
- filesystem: map[string]string{
+ RunBp2BuildTestCaseSimple(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library shared with arch variance",
+ ModuleTypeUnderTest: "cc_prebuilt_library_shared",
+ ModuleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory,
+ Filesystem: map[string]string{
"libf.so": "",
"libg.so": "",
},
- blueprint: `
+ Blueprint: `
cc_prebuilt_library_shared {
name: "libtest",
arch: {
@@ -49,8 +49,8 @@
},
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
"shared_library": `select({
"//build/bazel/platforms/arch:arm": "libg.so",
"//build/bazel/platforms/arch:arm64": "libf.so",
@@ -62,16 +62,16 @@
}
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{
+ 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: `
+ Blueprint: `
cc_prebuilt_library_shared {
name: "libtest",
srcs: ["libf.so"],
@@ -80,6 +80,6 @@
},
bazel_module: { bp2build_available: true},
}`,
- expectedErr: fmt.Errorf("Expected at most one source file"),
+ ExpectedErr: fmt.Errorf("Expected at most one source file"),
})
}
diff --git a/bp2build/cc_prebuilt_library_static_conversion_test.go b/bp2build/cc_prebuilt_library_static_conversion_test.go
new file mode 100644
index 0000000..77562e7
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_static_conversion_test.go
@@ -0,0 +1,199 @@
+// 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 (
+ "testing"
+
+ "android/soong/cc"
+)
+
+func runCcPrebuiltLibraryStaticTestCase(t *testing.T, tc Bp2buildTestCase) {
+ t.Parallel()
+ t.Helper()
+ (&tc).ModuleTypeUnderTest = "cc_prebuilt_library_static"
+ (&tc).ModuleTypeUnderTestFactory = cc.PrebuiltStaticLibraryFactory
+ RunBp2BuildTestCaseSimple(t, tc)
+}
+
+func TestPrebuiltLibraryStaticSimple(t *testing.T) {
+ runCcPrebuiltLibraryStaticTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library static simple",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ },
+ Blueprint: `
+cc_prebuilt_library_static {
+ name: "libtest",
+ srcs: ["libf.so"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
+ "static_library": `"libf.so"`,
+ }),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "alwayslink": "True",
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibraryStaticWithArchVariance(t *testing.T) {
+ runCcPrebuiltLibraryStaticTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library with arch variance",
+ 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("cc_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,
+ })`}),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+ "alwayslink": "True",
+ "static_library": `select({
+ "//build/bazel/platforms/arch:arm": "libg.so",
+ "//build/bazel/platforms/arch:arm64": "libf.so",
+ "//conditions:default": None,
+ })`}),
+ },
+ })
+}
+
+func TestPrebuiltLibraryStaticAdditionalAttrs(t *testing.T) {
+ runCcPrebuiltLibraryStaticTestCase(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library additional attributes",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "testdir/1/include.h": "",
+ "testdir/2/other.h": "",
+ },
+ Blueprint: `
+cc_prebuilt_library_static {
+ name: "libtest",
+ srcs: ["libf.so"],
+ export_include_dirs: ["testdir/1/"],
+ export_system_include_dirs: ["testdir/2/"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "export_includes": `["testdir/1/"]`,
+ "export_system_includes": `["testdir/2/"]`,
+ }),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "export_includes": `["testdir/1/"]`,
+ "export_system_includes": `["testdir/2/"]`,
+ "alwayslink": "True",
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibraryStaticWithExportIncludesArchVariant(t *testing.T) {
+ runCcPrebuiltLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_prebuilt_library_static correctly translates export_includes with arch variance",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ Blueprint: `
+cc_prebuilt_library_static {
+ name: "libtest",
+ srcs: ["libf.so"],
+ arch: {
+ arm: { export_include_dirs: ["testdir/1/"], },
+ arm64: { export_include_dirs: ["testdir/2/"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "export_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+ "alwayslink": "True",
+ "static_library": `"libf.so"`,
+ "export_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltLibraryStaticWithExportSystemIncludesArchVariant(t *testing.T) {
+ runCcPrebuiltLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_prebuilt_library_static correctly translates export_system_includes with arch variance",
+ Filesystem: map[string]string{
+ "libf.so": "",
+ "libg.so": "",
+ },
+ Blueprint: `
+cc_prebuilt_library_static {
+ name: "libtest",
+ srcs: ["libf.so"],
+ arch: {
+ arm: { export_system_include_dirs: ["testdir/1/"], },
+ arm64: { export_system_include_dirs: ["testdir/2/"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "export_system_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+ "alwayslink": "True",
+ "static_library": `"libf.so"`,
+ "export_system_includes": `select({
+ "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+ "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/cc_prebuilt_library_static_test.go b/bp2build/cc_prebuilt_library_static_test.go
index b1a9378..17da813 100644
--- a/bp2build/cc_prebuilt_library_static_test.go
+++ b/bp2build/cc_prebuilt_library_static_test.go
@@ -21,39 +21,43 @@
)
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{
+ RunBp2BuildTestCaseSimple(t,
+ Bp2buildTestCase{
+ Description: "prebuilt library static simple",
+ ModuleTypeUnderTest: "cc_prebuilt_library_static",
+ ModuleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory,
+ Filesystem: map[string]string{
"libf.so": "",
},
- blueprint: `
+ Blueprint: `
cc_prebuilt_library_static {
name: "libtest",
srcs: ["libf.so"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_library_static", "libtest", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
"static_library": `"libf.so"`,
}),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+ "static_library": `"libf.so"`,
+ "alwayslink": "True",
+ }),
},
})
}
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{
+ 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: `
+ Blueprint: `
cc_prebuilt_library_static {
name: "libtest",
arch: {
@@ -62,29 +66,35 @@
},
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_library_static", "libtest", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_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,
- })`,
- }),
+ })`}),
+ MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+ "alwayslink": "True",
+ "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{
+ 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: `
+ Blueprint: `
cc_prebuilt_library_static {
name: "libtest",
srcs: ["libf.so"],
@@ -93,6 +103,55 @@
},
bazel_module: { bp2build_available: true },
}`,
- expectedErr: fmt.Errorf("Expected at most one source file"),
+ 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/cc_prebuilt_object_conversion_test.go b/bp2build/cc_prebuilt_object_conversion_test.go
new file mode 100644
index 0000000..903c816
--- /dev/null
+++ b/bp2build/cc_prebuilt_object_conversion_test.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 bp2build
+
+import (
+ "fmt"
+ "testing"
+
+ "android/soong/cc"
+)
+
+func runCcPrebuiltObjectTestCase(t *testing.T, testCase Bp2buildTestCase) {
+ t.Helper()
+ description := fmt.Sprintf("cc_prebuilt_object: %s", testCase.Description)
+ testCase.ModuleTypeUnderTest = "cc_prebuilt_object"
+ testCase.ModuleTypeUnderTestFactory = cc.PrebuiltObjectFactory
+ testCase.Description = description
+ t.Run(description, func(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCaseSimple(t, testCase)
+ })
+}
+
+func TestPrebuiltObject(t *testing.T) {
+ runCcPrebuiltObjectTestCase(t,
+ Bp2buildTestCase{
+ Description: "simple",
+ Filesystem: map[string]string{
+ "obj.o": "",
+ },
+ Blueprint: `
+cc_prebuilt_object {
+ name: "objtest",
+ srcs: ["obj.o"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_object", "objtest", AttrNameToString{
+ "src": `"obj.o"`,
+ })},
+ })
+}
+
+func TestPrebuiltObjectWithArchVariance(t *testing.T) {
+ runCcPrebuiltObjectTestCase(t,
+ Bp2buildTestCase{
+ Description: "with arch variance",
+ Filesystem: map[string]string{
+ "obja.o": "",
+ "objb.o": "",
+ },
+ Blueprint: `
+cc_prebuilt_object {
+ name: "objtest",
+ arch: {
+ arm64: { srcs: ["obja.o"], },
+ arm: { srcs: ["objb.o"], },
+ },
+ bazel_module: { bp2build_available: true },
+}`, ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_prebuilt_object", "objtest", AttrNameToString{
+ "src": `select({
+ "//build/bazel/platforms/arch:arm": "objb.o",
+ "//build/bazel/platforms/arch:arm64": "obja.o",
+ "//conditions:default": None,
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPrebuiltObjectMultipleSrcsFails(t *testing.T) {
+ runCcPrebuiltObjectTestCase(t,
+ Bp2buildTestCase{
+ Description: "fails because multiple sources",
+ Filesystem: map[string]string{
+ "obja": "",
+ "objb": "",
+ },
+ Blueprint: `
+cc_prebuilt_object {
+ name: "objtest",
+ srcs: ["obja.o", "objb.o"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedErr: fmt.Errorf("Expected at most one source file"),
+ })
+}
+
+// TODO: nosrcs test
diff --git a/bp2build/cc_test_conversion_test.go b/bp2build/cc_test_conversion_test.go
new file mode 100644
index 0000000..20adddb
--- /dev/null
+++ b/bp2build/cc_test_conversion_test.go
@@ -0,0 +1,262 @@
+// 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/android"
+ "android/soong/cc"
+ "android/soong/genrule"
+)
+
+type ccTestBp2buildTestCase struct {
+ description string
+ blueprint string
+ filesystem map[string]string
+ targets []testBazelTarget
+}
+
+func registerCcTestModuleTypes(ctx android.RegistrationContext) {
+ cc.RegisterCCBuildComponents(ctx)
+ ctx.RegisterModuleType("cc_binary", cc.BinaryFactory)
+ ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+ ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+ ctx.RegisterModuleType("cc_test_library", cc.TestLibraryFactory)
+ ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+}
+
+func runCcTestTestCase(t *testing.T, testCase ccTestBp2buildTestCase) {
+ t.Helper()
+ moduleTypeUnderTest := "cc_test"
+ description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description)
+ t.Run(description, func(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(t, registerCcTestModuleTypes, Bp2buildTestCase{
+ ExpectedBazelTargets: generateBazelTargetsForTest(testCase.targets, android.HostAndDeviceSupported),
+ Filesystem: testCase.filesystem,
+ ModuleTypeUnderTest: moduleTypeUnderTest,
+ ModuleTypeUnderTestFactory: cc.TestFactory,
+ Description: description,
+ Blueprint: testCase.blueprint,
+ })
+ })
+}
+
+func TestBasicCcTest(t *testing.T) {
+ runCcTestTestCase(t, ccTestBp2buildTestCase{
+ description: "basic cc_test with commonly used attributes",
+ blueprint: `
+cc_test {
+ name: "mytest",
+ host_supported: true,
+ srcs: ["test.cpp"],
+ target: {
+ android: {
+ srcs: ["android.cpp"],
+ shared_libs: ["foolib"],
+ },
+ linux: {
+ srcs: ["linux.cpp"],
+ },
+ host: {
+ static_libs: ["hostlib"],
+ },
+ },
+ data: [":data_mod", "file.txt"],
+ data_bins: [":cc_bin"],
+ data_libs: [":cc_lib"],
+ cflags: ["-Wall"],
+}
+` + simpleModuleDoNotConvertBp2build("cc_library", "foolib") +
+ simpleModuleDoNotConvertBp2build("cc_library_static", "hostlib") +
+ simpleModuleDoNotConvertBp2build("genrule", "data_mod") +
+ simpleModuleDoNotConvertBp2build("cc_binary", "cc_bin") +
+ simpleModuleDoNotConvertBp2build("cc_test_library", "cc_lib"),
+ targets: []testBazelTarget{
+ {"cc_test", "mytest", AttrNameToString{
+ "copts": `["-Wall"]`,
+ "data": `[
+ ":data_mod",
+ "file.txt",
+ ":cc_bin",
+ ":cc_lib",
+ ]`,
+ "deps": `select({
+ "//build/bazel/platforms/os:darwin": [":hostlib"],
+ "//build/bazel/platforms/os:linux_bionic": [":hostlib"],
+ "//build/bazel/platforms/os:linux_glibc": [":hostlib"],
+ "//build/bazel/platforms/os:linux_musl": [":hostlib"],
+ "//build/bazel/platforms/os:windows": [":hostlib"],
+ "//conditions:default": [],
+ })`,
+ "gtest": "True",
+ "isolated": "True",
+ "local_includes": `["."]`,
+ "dynamic_deps": `select({
+ "//build/bazel/platforms/os:android": [":foolib"],
+ "//conditions:default": [],
+ })`,
+ "srcs": `["test.cpp"] + select({
+ "//build/bazel/platforms/os:android": [
+ "linux.cpp",
+ "android.cpp",
+ ],
+ "//build/bazel/platforms/os:linux_bionic": ["linux.cpp"],
+ "//build/bazel/platforms/os:linux_glibc": ["linux.cpp"],
+ "//build/bazel/platforms/os:linux_musl": ["linux.cpp"],
+ "//conditions:default": [],
+ })`,
+ },
+ },
+ },
+ })
+}
+
+func TestBasicCcTestGtestIsolatedDisabled(t *testing.T) {
+ runCcTestTestCase(t, ccTestBp2buildTestCase{
+ description: "cc test with disabled gtest and isolated props",
+ blueprint: `
+cc_test {
+ name: "mytest",
+ host_supported: true,
+ srcs: ["test.cpp"],
+ gtest: false,
+ isolated: false,
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_test", "mytest", AttrNameToString{
+ "gtest": "False",
+ "isolated": "False",
+ "local_includes": `["."]`,
+ "srcs": `["test.cpp"]`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcTest_TestOptions_Tags(t *testing.T) {
+ runCcTestTestCase(t, ccTestBp2buildTestCase{
+ description: "cc test with test_options.tags converted to tags",
+ blueprint: `
+cc_test {
+ name: "mytest",
+ host_supported: true,
+ srcs: ["test.cpp"],
+ test_options: { tags: ["no-remote"] },
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_test", "mytest", AttrNameToString{
+ "tags": `["no-remote"]`,
+ "local_includes": `["."]`,
+ "srcs": `["test.cpp"]`,
+ "gtest": "True",
+ "isolated": "True",
+ },
+ },
+ },
+ })
+}
+
+func TestCcTest_TestConfig(t *testing.T) {
+ runCcTestTestCase(t, ccTestBp2buildTestCase{
+ description: "cc test that sets a test_config",
+ filesystem: map[string]string{
+ "test_config.xml": "",
+ },
+ blueprint: `
+cc_test {
+ name: "mytest",
+ srcs: ["test.cpp"],
+ test_config: "test_config.xml",
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_test", "mytest", AttrNameToString{
+ "gtest": "True",
+ "isolated": "True",
+ "local_includes": `["."]`,
+ "srcs": `["test.cpp"]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ "test_config": `"test_config.xml"`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcTest_TestConfigAndroidTestXML(t *testing.T) {
+ runCcTestTestCase(t, ccTestBp2buildTestCase{
+ description: "cc test that defaults to test config AndroidTest.xml",
+ filesystem: map[string]string{
+ "AndroidTest.xml": "",
+ },
+ blueprint: `
+cc_test {
+ name: "mytest",
+ srcs: ["test.cpp"],
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_test", "mytest", AttrNameToString{
+ "gtest": "True",
+ "isolated": "True",
+ "local_includes": `["."]`,
+ "srcs": `["test.cpp"]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ "test_config": `"AndroidTest.xml"`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcTest_TestConfigTemplateOptions(t *testing.T) {
+ runCcTestTestCase(t, ccTestBp2buildTestCase{
+ description: "cc test that sets test config template attributes",
+ filesystem: map[string]string{
+ "test_config_template.xml": "",
+ },
+ blueprint: `
+cc_test {
+ name: "mytest",
+ srcs: ["test.cpp"],
+ test_config_template: "test_config_template.xml",
+ auto_gen_config: true,
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_test", "mytest", AttrNameToString{
+ "auto_generate_test_config": "True",
+ "gtest": "True",
+ "isolated": "True",
+ "local_includes": `["."]`,
+ "srcs": `["test.cpp"]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ "template_configs": `[
+ "'<target_preparer class=\"com.android.tradefed.targetprep.RootTargetPreparer\">\\n <option name=\"force-root\" value=\"false\" />\\n </target_preparer>'",
+ "'<option name=\"not-shardable\" value=\"true\" />'",
+ ]`,
+ "template_install_base": `"/data/local/tmp"`,
+ "template_test_config": `"test_config_template.xml"`,
+ },
+ },
+ },
+ })
+}
diff --git a/bp2build/cc_yasm_conversion_test.go b/bp2build/cc_yasm_conversion_test.go
new file mode 100644
index 0000000..55d4feb
--- /dev/null
+++ b/bp2build/cc_yasm_conversion_test.go
@@ -0,0 +1,179 @@
+// 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 (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+)
+
+func runYasmTestCase(t *testing.T, tc Bp2buildTestCase) {
+ t.Helper()
+ RunBp2BuildTestCase(t, registerYasmModuleTypes, tc)
+}
+
+func registerYasmModuleTypes(ctx android.RegistrationContext) {
+ cc.RegisterCCBuildComponents(ctx)
+ ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+ ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
+ ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+}
+
+func TestYasmSimple(t *testing.T) {
+ runYasmTestCase(t, Bp2buildTestCase{
+ Description: "Simple yasm test",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "main.cpp": "",
+ "myfile.asm": "",
+ },
+ Blueprint: `
+cc_library {
+ name: "foo",
+ srcs: ["main.cpp", "myfile.asm"],
+}`,
+ ExpectedBazelTargets: append([]string{
+ MakeBazelTarget("yasm", "foo_yasm", map[string]string{
+ "include_dirs": `["."]`,
+ "srcs": `["myfile.asm"]`,
+ }),
+ }, makeCcLibraryTargets("foo", map[string]string{
+ "local_includes": `["."]`,
+ "srcs": `[
+ "main.cpp",
+ ":foo_yasm",
+ ]`,
+ })...),
+ })
+}
+
+func TestYasmWithIncludeDirs(t *testing.T) {
+ runYasmTestCase(t, Bp2buildTestCase{
+ Description: "Simple yasm test",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "main.cpp": "",
+ "myfile.asm": "",
+ "include1/foo/myinclude.inc": "",
+ "include2/foo/myinclude2.inc": "",
+ },
+ Blueprint: `
+cc_library {
+ name: "foo",
+ local_include_dirs: ["include1/foo"],
+ export_include_dirs: ["include2/foo"],
+ srcs: ["main.cpp", "myfile.asm"],
+}`,
+ ExpectedBazelTargets: append([]string{
+ MakeBazelTarget("yasm", "foo_yasm", map[string]string{
+ "include_dirs": `[
+ "include1/foo",
+ ".",
+ "include2/foo",
+ ]`,
+ "srcs": `["myfile.asm"]`,
+ }),
+ }, makeCcLibraryTargets("foo", map[string]string{
+ "local_includes": `[
+ "include1/foo",
+ ".",
+ ]`,
+ "export_includes": `["include2/foo"]`,
+ "srcs": `[
+ "main.cpp",
+ ":foo_yasm",
+ ]`,
+ })...),
+ })
+}
+
+func TestYasmConditionalBasedOnArch(t *testing.T) {
+ runYasmTestCase(t, Bp2buildTestCase{
+ Description: "Simple yasm test",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "main.cpp": "",
+ "myfile.asm": "",
+ },
+ Blueprint: `
+cc_library {
+ name: "foo",
+ srcs: ["main.cpp"],
+ arch: {
+ x86: {
+ srcs: ["myfile.asm"],
+ },
+ },
+}`,
+ ExpectedBazelTargets: append([]string{
+ MakeBazelTarget("yasm", "foo_yasm", map[string]string{
+ "include_dirs": `["."]`,
+ "srcs": `select({
+ "//build/bazel/platforms/arch:x86": ["myfile.asm"],
+ "//conditions:default": [],
+ })`,
+ }),
+ }, makeCcLibraryTargets("foo", map[string]string{
+ "local_includes": `["."]`,
+ "srcs": `["main.cpp"] + select({
+ "//build/bazel/platforms/arch:x86": [":foo_yasm"],
+ "//conditions:default": [],
+ })`,
+ })...),
+ })
+}
+
+func TestYasmPartiallyConditional(t *testing.T) {
+ runYasmTestCase(t, Bp2buildTestCase{
+ Description: "Simple yasm test",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Filesystem: map[string]string{
+ "main.cpp": "",
+ "myfile.asm": "",
+ "mysecondfile.asm": "",
+ },
+ Blueprint: `
+cc_library {
+ name: "foo",
+ srcs: ["main.cpp", "myfile.asm"],
+ arch: {
+ x86: {
+ srcs: ["mysecondfile.asm"],
+ },
+ },
+}`,
+ ExpectedBazelTargets: append([]string{
+ MakeBazelTarget("yasm", "foo_yasm", map[string]string{
+ "include_dirs": `["."]`,
+ "srcs": `["myfile.asm"] + select({
+ "//build/bazel/platforms/arch:x86": ["mysecondfile.asm"],
+ "//conditions:default": [],
+ })`,
+ }),
+ }, makeCcLibraryTargets("foo", map[string]string{
+ "local_includes": `["."]`,
+ "srcs": `[
+ "main.cpp",
+ ":foo_yasm",
+ ]`,
+ })...),
+ })
+}
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index d37a523..8e17103 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -13,12 +13,40 @@
type selects map[string]reflect.Value
-func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects) {
- value := reflect.ValueOf(list.Value)
- if !list.HasConfigurableValues() {
+func getStringValue(str bazel.StringAttribute) (reflect.Value, []selects) {
+ value := reflect.ValueOf(str.Value)
+
+ if !str.HasConfigurableValues() {
return value, []selects{}
}
+ ret := selects{}
+ for _, axis := range str.SortedConfigurationAxes() {
+ configToStrs := str.ConfigurableValues[axis]
+ for config, strs := range configToStrs {
+ selectKey := axis.SelectKey(config)
+ ret[selectKey] = reflect.ValueOf(strs)
+ }
+ }
+
+ // if there is a select, use the base value as the conditions default value
+ if len(ret) > 0 {
+ if _, ok := ret[bazel.ConditionsDefaultSelectKey]; !ok {
+ ret[bazel.ConditionsDefaultSelectKey] = value
+ value = reflect.Zero(value.Type())
+ }
+ }
+
+ return value, []selects{ret}
+}
+
+func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects, bool) {
+ value := reflect.ValueOf(list.Value)
+ prepend := list.Prepend
+ if !list.HasConfigurableValues() {
+ return value, []selects{}, prepend
+ }
+
var ret []selects
for _, axis := range list.SortedConfigurationAxes() {
configToLists := list.ConfigurableValues[axis]
@@ -32,7 +60,7 @@
}
}
- return value, ret
+ return value, ret, prepend
}
func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) {
@@ -81,8 +109,9 @@
return value, []selects{ret}
}
-func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects) {
+func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects, bool) {
value := reflect.ValueOf(list.Value.Includes)
+ prepend := list.Prepend
var ret []selects
for _, axis := range list.SortedConfigurationAxes() {
configToLabels := list.ConfigurableValues[axis]
@@ -108,7 +137,7 @@
}
}
- return value, ret
+ return value, ret, prepend
}
func labelListSelectValue(selectKey string, list bazel.LabelList, emitEmptyList bool) (bool, reflect.Value) {
@@ -131,17 +160,30 @@
// select statements.
func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
var value reflect.Value
+ // configurableAttrs is the list of individual select statements to be
+ // concatenated together. These select statements should be along different
+ // axes. For example, one element may be
+ // `select({"//color:red": "one", "//color:green": "two"})`, and the second
+ // element may be `select({"//animal:cat": "three", "//animal:dog": "four"}).
+ // These selects should be sorted by axis identifier.
var configurableAttrs []selects
+ var prepend bool
var defaultSelectValue *string
var emitZeroValues bool
// If true, print the default attribute value, even if the attribute is zero.
shouldPrintDefault := false
switch list := v.(type) {
+ case bazel.StringAttribute:
+ if err := list.Collapse(); err != nil {
+ return "", err
+ }
+ value, configurableAttrs = getStringValue(list)
+ defaultSelectValue = &bazelNone
case bazel.StringListAttribute:
- value, configurableAttrs = getStringListValues(list)
+ value, configurableAttrs, prepend = getStringListValues(list)
defaultSelectValue = &emptyBazelList
case bazel.LabelListAttribute:
- value, configurableAttrs = getLabelListValues(list)
+ value, configurableAttrs, prepend = getLabelListValues(list)
emitZeroValues = list.EmitEmptyList
defaultSelectValue = &emptyBazelList
if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) {
@@ -173,22 +215,28 @@
ret += s
}
- // Convenience function to append selects components to an attribute value.
- appendSelects := func(selectsData selects, defaultValue *string, s string) (string, error) {
+ // Convenience function to prepend/append selects components to an attribute value.
+ concatenateSelects := func(selectsData selects, defaultValue *string, s string, prepend bool) (string, error) {
selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent, emitZeroValues)
if err != nil {
return "", err
}
- if s != "" && selectMap != "" {
- s += " + "
+ var left, right string
+ if prepend {
+ left, right = selectMap, s
+ } else {
+ left, right = s, selectMap
}
- s += selectMap
+ if left != "" && right != "" {
+ left += " + "
+ }
+ left += right
- return s, nil
+ return left, nil
}
for _, configurableAttr := range configurableAttrs {
- ret, err = appendSelects(configurableAttr, defaultSelectValue, ret)
+ ret, err = concatenateSelects(configurableAttr, defaultSelectValue, ret, prepend)
if err != nil {
return "", err
}
@@ -208,7 +256,7 @@
}
var selects string
- for _, selectKey := range android.SortedStringKeys(selectMap) {
+ for _, selectKey := range android.SortedKeys(selectMap) {
if selectKey == bazel.ConditionsDefaultSelectKey {
// Handle default condition later.
continue
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 1790dd7..608fcd8 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -1,15 +1,20 @@
package bp2build
import (
+ "android/soong/starlark_fmt"
"encoding/json"
"fmt"
"reflect"
+ "strconv"
"strings"
"android/soong/android"
+ "android/soong/cc"
cc_config "android/soong/cc/config"
java_config "android/soong/java/config"
+ "android/soong/apex"
+
"github.com/google/blueprint/proptools"
)
@@ -19,16 +24,36 @@
Contents string
}
-func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []BazelFile {
+// PRIVATE: Use CreateSoongInjectionDirFiles instead
+func soongInjectionFiles(cfg android.Config, metrics CodegenMetrics) ([]BazelFile, error) {
var files []BazelFile
+ files = append(files, newFile("android", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
+ files = append(files, newFile("android", "constants.bzl", android.BazelCcToolchainVars(cfg)))
+
files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
- files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+ files = append(files, newFile("cc_toolchain", "config_constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+ files = append(files, newFile("cc_toolchain", "sanitizer_constants.bzl", cc.BazelCcSanitizerToolchainVars(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("apex_toolchain", GeneratedBuildFileName, "")) // Creates a //apex_toolchain package.
+ apexToolchainVars, err := apex.BazelApexToolchainVars()
+ if err != nil {
+ return nil, err
+ }
+ files = append(files, newFile("apex_toolchain", "constants.bzl", apexToolchainVars))
+
+ files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.Serialize().ConvertedModules, "\n")))
+
+ convertedModulePathMap, err := json.MarshalIndent(metrics.convertedModulePathMap, "", "\t")
+ if err != nil {
+ panic(err)
+ }
+ files = append(files, newFile("metrics", GeneratedBuildFileName, "")) // Creates a //metrics package.
+ files = append(files, newFile("metrics", "converted_modules_path_map.json", string(convertedModulePathMap)))
+ files = append(files, newFile("metrics", "converted_modules_path_map.bzl", "modules = "+strings.ReplaceAll(string(convertedModulePathMap), "\\", "\\\\")))
files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
@@ -36,20 +61,50 @@
apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
if err != nil {
- panic(err)
+ return nil, err
}
files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
+ // TODO(b/269691302) value of apiLevelsContent is product variable dependent and should be avoided for soong injection
files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
+ files = append(files, newFile("api_levels", "platform_versions.bzl", platformVersionContents(cfg)))
- return files
+ files = append(files, newFile("allowlists", GeneratedBuildFileName, ""))
+ files = append(files, newFile("allowlists", "env.bzl", android.EnvironmentVarsFile(cfg)))
+ // TODO(b/262781701): Create an alternate soong_build entrypoint for writing out these files only when requested
+ files = append(files, newFile("allowlists", "mixed_build_prod_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelProdMode), "\n")+"\n"))
+ files = append(files, newFile("allowlists", "mixed_build_staging_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelStagingMode), "\n")+"\n"))
+
+ return files, nil
}
-func convertedModules(convertedModules []string) string {
- return strings.Join(convertedModules, "\n")
+func platformVersionContents(cfg android.Config) string {
+ // Despite these coming from cfg.productVariables, they are actually hardcoded in global
+ // makefiles, not set in individual product config makesfiles, so they're safe to just export
+ // and load() directly.
+
+ platformVersionActiveCodenames := make([]string, 0, len(cfg.PlatformVersionActiveCodenames()))
+ for _, codename := range cfg.PlatformVersionActiveCodenames() {
+ platformVersionActiveCodenames = append(platformVersionActiveCodenames, fmt.Sprintf("%q", codename))
+ }
+
+ platformSdkVersion := "None"
+ if cfg.RawPlatformSdkVersion() != nil {
+ platformSdkVersion = strconv.Itoa(*cfg.RawPlatformSdkVersion())
+ }
+
+ return fmt.Sprintf(`
+platform_versions = struct(
+ platform_sdk_final = %s,
+ platform_sdk_version = %s,
+ platform_sdk_codename = %q,
+ platform_version_active_codenames = [%s],
+)
+`, starlark_fmt.PrintBool(cfg.PlatformSdkFinal()), platformSdkVersion, cfg.PlatformSdkCodename(), strings.Join(platformVersionActiveCodenames, ", "))
}
func CreateBazelFiles(
+ cfg android.Config,
ruleShims map[string]RuleShim,
buildToTargets map[string]BazelTargets,
mode CodegenMode) []BazelFile {
@@ -81,32 +136,24 @@
func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
files := make([]BazelFile, 0, len(buildToTargets))
- for _, dir := range android.SortedStringKeys(buildToTargets) {
- if mode == Bp2Build && android.ShouldKeepExistingBuildFileForDir(dir) {
- fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
- continue
- }
+ for _, dir := range android.SortedKeys(buildToTargets) {
targets := buildToTargets[dir]
targets.sort()
var content string
- if mode == Bp2Build {
+ if mode == Bp2Build || mode == ApiBp2build {
content = `# READ THIS FIRST:
# This file was automatically generated by bp2build for the Bazel migration project.
# Feel free to edit or test it, but do *not* check it into your version control system.
`
- if targets.hasHandcraftedTargets() {
- // For BUILD files with both handcrafted and generated targets,
- // don't hardcode actual content, like package() declarations.
- // Leave that responsibility to the checked-in BUILD file
- // instead.
- content += `# This file contains generated targets and handcrafted targets that are manually managed in the source tree.`
- } else {
- // For fully-generated BUILD files, hardcode the default visibility.
- content += "package(default_visibility = [\"//visibility:public\"])"
- }
- content += "\n"
content += targets.LoadStatements()
+ content += "\n\n"
+ // Get package rule from the handcrafted BUILD file, otherwise emit the default one.
+ prText := "package(default_visibility = [\"//visibility:public\"])\n"
+ if pr := targets.packageRule(); pr != nil {
+ prText = pr.content
+ }
+ content += prText
} else if mode == QueryView {
content = soongModuleLoad
}
@@ -143,15 +190,17 @@
var (
// Certain module property names are blocklisted/ignored here, for the reasons commented.
ignoredPropNames = map[string]bool{
- "name": true, // redundant, since this is explicitly generated for every target
- "from": true, // reserved keyword
- "in": true, // reserved keyword
- "size": true, // reserved for tests
- "arch": true, // interface prop type is not supported yet.
- "multilib": true, // interface prop type is not supported yet.
- "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.
+ "name": true, // redundant, since this is explicitly generated for every target
+ "from": true, // reserved keyword
+ "in": true, // reserved keyword
+ "size": true, // reserved for tests
+ "arch": true, // interface prop type is not supported yet.
+ "multilib": true, // interface prop type is not supported yet.
+ "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
+ "versions_with_info": true, // TODO(b/245730552) struct properties not fully supported
}
)
@@ -165,7 +214,7 @@
// internal to Soong only, and these fields do not have PkgPath.
return true
}
- // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc
+ // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc.
// but cannot be set in a .bp file
if proptools.HasTag(field, "blueprint", "mutated") {
return true
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index e49d855..2f5dc3c 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -27,7 +27,8 @@
}
func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) {
- files := CreateBazelFiles(map[string]RuleShim{}, map[string]BazelTargets{}, QueryView)
+ files := CreateBazelFiles(android.NullConfig("out", "out/soong"),
+ map[string]RuleShim{}, map[string]BazelTargets{}, QueryView)
expectedFilePaths := []bazelFilepath{
{
dir: "",
@@ -83,18 +84,32 @@
func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
testConfig := android.TestConfig("", make(map[string]string), "", make(map[string][]byte))
- files := CreateSoongInjectionFiles(testConfig, CodegenMetrics{})
-
+ files, err := soongInjectionFiles(testConfig, CreateCodegenMetrics())
+ if err != nil {
+ t.Error(err)
+ }
expectedFilePaths := []bazelFilepath{
{
- dir: "cc_toolchain",
+ dir: "android",
basename: GeneratedBuildFileName,
},
{
- dir: "cc_toolchain",
+ dir: "android",
basename: "constants.bzl",
},
{
+ dir: "cc_toolchain",
+ basename: GeneratedBuildFileName,
+ },
+ {
+ dir: "cc_toolchain",
+ basename: "config_constants.bzl",
+ },
+ {
+ dir: "cc_toolchain",
+ basename: "sanitizer_constants.bzl",
+ },
+ {
dir: "java_toolchain",
basename: GeneratedBuildFileName,
},
@@ -103,10 +118,30 @@
basename: "constants.bzl",
},
{
+ dir: "apex_toolchain",
+ basename: GeneratedBuildFileName,
+ },
+ {
+ dir: "apex_toolchain",
+ basename: "constants.bzl",
+ },
+ {
dir: "metrics",
basename: "converted_modules.txt",
},
{
+ dir: "metrics",
+ basename: "BUILD.bazel",
+ },
+ {
+ dir: "metrics",
+ basename: "converted_modules_path_map.json",
+ },
+ {
+ dir: "metrics",
+ basename: "converted_modules_path_map.bzl",
+ },
+ {
dir: "product_config",
basename: "soong_config_variables.bzl",
},
@@ -126,6 +161,26 @@
dir: "api_levels",
basename: "api_levels.bzl",
},
+ {
+ dir: "api_levels",
+ basename: "platform_versions.bzl",
+ },
+ {
+ dir: "allowlists",
+ basename: GeneratedBuildFileName,
+ },
+ {
+ dir: "allowlists",
+ basename: "env.bzl",
+ },
+ {
+ dir: "allowlists",
+ basename: "mixed_build_prod_allowlist.txt",
+ },
+ {
+ dir: "allowlists",
+ basename: "mixed_build_staging_allowlist.txt",
+ },
}
if len(files) != len(expectedFilePaths) {
diff --git a/bp2build/droidstubs_conversion_test.go b/bp2build/droidstubs_conversion_test.go
new file mode 100644
index 0000000..12c1cfe
--- /dev/null
+++ b/bp2build/droidstubs_conversion_test.go
@@ -0,0 +1,104 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func registerJavaApiModules(ctx android.RegistrationContext) {
+ java.RegisterSdkLibraryBuildComponents(ctx)
+ java.RegisterStubsBuildComponents(ctx)
+}
+
+func TestDroidstubsApiContributions(t *testing.T) {
+ bp := `
+ droidstubs {
+ name: "framework-stubs",
+ check_api: {
+ current: {
+ api_file: "framework.current.txt",
+ },
+ },
+ }
+
+ // Modules without check_api should not generate a Bazel API target
+ droidstubs {
+ name: "framework-docs",
+ }
+
+ // java_sdk_library is a macro that creates droidstubs
+ java_sdk_library {
+ name: "module-stubs",
+ srcs: ["A.java"],
+
+ // These api surfaces are added by default, but add them explicitly to make
+ // this test hermetic
+ public: {
+ enabled: true,
+ },
+ system: {
+ enabled: true,
+ },
+
+ // Disable other api surfaces to keep unit test scope limited
+ module_lib: {
+ enabled: false,
+ },
+ test: {
+ enabled: false,
+ },
+ }
+ `
+ expectedBazelTargets := []string{
+ MakeBazelTargetNoRestrictions(
+ "java_api_contribution",
+ "framework-stubs.contribution",
+ AttrNameToString{
+ "api": `"framework.current.txt"`,
+ "api_surface": `"publicapi"`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ }),
+ MakeBazelTargetNoRestrictions(
+ "java_api_contribution",
+ "module-stubs.stubs.source.contribution",
+ AttrNameToString{
+ "api": `"api/current.txt"`,
+ "api_surface": `"publicapi"`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ }),
+ MakeBazelTargetNoRestrictions(
+ "java_api_contribution",
+ "module-stubs.stubs.source.system.contribution",
+ AttrNameToString{
+ "api": `"api/system-current.txt"`,
+ "api_surface": `"systemapi"`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ }),
+ }
+ RunApiBp2BuildTestCase(t, registerJavaApiModules, Bp2buildTestCase{
+ Blueprint: bp,
+ ExpectedBazelTargets: expectedBazelTargets,
+ Filesystem: map[string]string{
+ "api/current.txt": "",
+ "api/removed.txt": "",
+ "api/system-current.txt": "",
+ "api/system-removed.txt": "",
+ },
+ })
+}
diff --git a/bp2build/filegroup_conversion_test.go b/bp2build/filegroup_conversion_test.go
index b43cf53..273d556 100644
--- a/bp2build/filegroup_conversion_test.go
+++ b/bp2build/filegroup_conversion_test.go
@@ -15,44 +15,194 @@
package bp2build
import (
- "android/soong/android"
"fmt"
-
"testing"
+
+ "android/soong/android"
)
-func runFilegroupTestCase(t *testing.T, tc bp2buildTestCase) {
+func runFilegroupTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "filegroup"
- (&tc).moduleTypeUnderTestFactory = android.FileGroupFactory
- runBp2BuildTestCase(t, registerFilegroupModuleTypes, tc)
+ (&tc).ModuleTypeUnderTest = "filegroup"
+ (&tc).ModuleTypeUnderTestFactory = android.FileGroupFactory
+ RunBp2BuildTestCase(t, registerFilegroupModuleTypes, tc)
}
func registerFilegroupModuleTypes(ctx android.RegistrationContext) {}
func TestFilegroupSameNameAsFile_OneFile(t *testing.T) {
- runFilegroupTestCase(t, bp2buildTestCase{
- description: "filegroup - same name as file, with one file",
- filesystem: map[string]string{},
- blueprint: `
+ runFilegroupTestCase(t, Bp2buildTestCase{
+ Description: "filegroup - same name as file, with one file",
+ Filesystem: map[string]string{},
+ Blueprint: `
filegroup {
name: "foo",
srcs: ["foo"],
}
`,
- expectedBazelTargets: []string{}})
+ ExpectedBazelTargets: []string{}})
}
func TestFilegroupSameNameAsFile_MultipleFiles(t *testing.T) {
- runFilegroupTestCase(t, bp2buildTestCase{
- description: "filegroup - same name as file, with multiple files",
- filesystem: map[string]string{},
- blueprint: `
+ runFilegroupTestCase(t, Bp2buildTestCase{
+ Description: "filegroup - same name as file, with multiple files",
+ Filesystem: map[string]string{},
+ Blueprint: `
filegroup {
name: "foo",
srcs: ["foo", "bar"],
}
`,
- expectedErr: fmt.Errorf("filegroup 'foo' cannot contain a file with the same name"),
+ ExpectedErr: fmt.Errorf("filegroup 'foo' cannot contain a file with the same name"),
})
}
+
+func TestFilegroupWithAidlSrcs(t *testing.T) {
+ testcases := []struct {
+ name string
+ bp string
+ expectedBazelAttrs AttrNameToString
+ }{
+ {
+ name: "filegroup with only aidl srcs",
+ bp: `
+ filegroup {
+ name: "foo",
+ srcs: ["aidl/foo.aidl"],
+ path: "aidl",
+ }`,
+ expectedBazelAttrs: AttrNameToString{
+ "srcs": `["aidl/foo.aidl"]`,
+ "strip_import_prefix": `"aidl"`,
+ "tags": `["apex_available=//apex_available:anyapex"]`,
+ },
+ },
+ {
+ name: "filegroup without path",
+ bp: `
+ filegroup {
+ name: "foo",
+ srcs: ["aidl/foo.aidl"],
+ }`,
+ expectedBazelAttrs: AttrNameToString{
+ "srcs": `["aidl/foo.aidl"]`,
+ "tags": `["apex_available=//apex_available:anyapex"]`,
+ },
+ },
+ }
+
+ for _, test := range testcases {
+ t.Run(test.name, func(t *testing.T) {
+ expectedBazelTargets := []string{
+ MakeBazelTargetNoRestrictions("aidl_library", "foo", test.expectedBazelAttrs),
+ }
+ runFilegroupTestCase(t, Bp2buildTestCase{
+ Description: test.name,
+ Blueprint: test.bp,
+ ExpectedBazelTargets: expectedBazelTargets,
+ })
+ })
+ }
+}
+
+func TestFilegroupWithAidlDeps(t *testing.T) {
+ bp := `
+ filegroup {
+ name: "bar",
+ srcs: ["bar.aidl"],
+ }
+ filegroup {
+ name: "foo",
+ srcs: ["aidl/foo.aidl"],
+ path: "aidl",
+ aidl: {
+ deps: [":bar"],
+ }
+ }`
+
+ t.Run("filegroup with aidl deps", func(t *testing.T) {
+ expectedBazelTargets := []string{
+ MakeBazelTargetNoRestrictions("aidl_library", "bar", AttrNameToString{
+ "srcs": `["bar.aidl"]`,
+ "tags": `["apex_available=//apex_available:anyapex"]`,
+ }),
+ MakeBazelTargetNoRestrictions("aidl_library", "foo", AttrNameToString{
+ "srcs": `["aidl/foo.aidl"]`,
+ "strip_import_prefix": `"aidl"`,
+ "deps": `[":bar"]`,
+ "tags": `["apex_available=//apex_available:anyapex"]`,
+ }),
+ }
+ runFilegroupTestCase(t, Bp2buildTestCase{
+ Description: "filegroup with aidl deps",
+ Blueprint: bp,
+ ExpectedBazelTargets: expectedBazelTargets,
+ })
+ })
+}
+
+func TestFilegroupWithAidlAndNonAidlSrcs(t *testing.T) {
+ runFilegroupTestCase(t, Bp2buildTestCase{
+ Description: "filegroup with aidl and non-aidl srcs",
+ Filesystem: map[string]string{},
+ Blueprint: `
+filegroup {
+ name: "foo",
+ srcs: [
+ "aidl/foo.aidl",
+ "buf.proto",
+ ],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "foo", AttrNameToString{
+ "srcs": `[
+ "aidl/foo.aidl",
+ "buf.proto",
+ ]`}),
+ }})
+}
+
+func TestFilegroupWithProtoSrcs(t *testing.T) {
+ runFilegroupTestCase(t, Bp2buildTestCase{
+ Description: "filegroup with proto and non-proto srcs",
+ Filesystem: map[string]string{},
+ Blueprint: `
+filegroup {
+ name: "foo",
+ srcs: ["proto/foo.proto"],
+ path: "proto",
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("proto_library", "foo_bp2build_converted", AttrNameToString{
+ "srcs": `["proto/foo.proto"]`,
+ "strip_import_prefix": `"proto"`,
+ "tags": `[
+ "apex_available=//apex_available:anyapex",
+ "manual",
+ ]`,
+ }),
+ MakeBazelTargetNoRestrictions("filegroup", "foo", AttrNameToString{
+ "srcs": `["proto/foo.proto"]`}),
+ }})
+}
+
+func TestFilegroupWithProtoAndNonProtoSrcs(t *testing.T) {
+ runFilegroupTestCase(t, Bp2buildTestCase{
+ Description: "filegroup with proto and non-proto srcs",
+ Filesystem: map[string]string{},
+ Blueprint: `
+filegroup {
+ name: "foo",
+ srcs: [
+ "foo.proto",
+ "buf.cpp",
+ ],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("filegroup", "foo", AttrNameToString{
+ "srcs": `[
+ "foo.proto",
+ "buf.cpp",
+ ]`}),
+ }})
+}
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index 9244b99..3490881 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -15,23 +15,24 @@
package bp2build
import (
+ "fmt"
+ "testing"
+
"android/soong/android"
"android/soong/cc"
"android/soong/genrule"
"android/soong/java"
- "fmt"
- "testing"
)
func registerGenruleModuleTypes(ctx android.RegistrationContext) {
ctx.RegisterModuleType("genrule_defaults", func() android.Module { return genrule.DefaultsFactory() })
}
-func runGenruleTestCase(t *testing.T, tc bp2buildTestCase) {
+func runGenruleTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "genrule"
- (&tc).moduleTypeUnderTestFactory = genrule.GenRuleFactory
- runBp2BuildTestCase(t, registerGenruleModuleTypes, tc)
+ (&tc).ModuleTypeUnderTest = "genrule"
+ (&tc).ModuleTypeUnderTestFactory = genrule.GenRuleFactory
+ RunBp2BuildTestCase(t, registerGenruleModuleTypes, tc)
}
func otherGenruleBp(genruleTarget string) map[string]string {
@@ -56,26 +57,30 @@
moduleType string
factory android.ModuleFactory
genDir string
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
factory: genrule.GenRuleFactory,
- genDir: "$(GENDIR)",
+ genDir: "$(RULEDIR)",
},
{
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,31 +102,24 @@
}`
for _, tc := range testCases {
- moduleAttrs := attrNameToString{
+ moduleAttrs := AttrNameToString{
"cmd": fmt.Sprintf(`"$(location :foo.tool) --genDir=%s arg $(SRCS) $(OUTS)"`, tc.genDir),
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
"tools": `[":foo.tool"]`,
}
- if tc.moduleType == "java_genrule_host" {
- moduleAttrs["target_compatible_with"] = `select({
- "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
- "//conditions:default": [],
- })`
- }
-
expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", moduleAttrs),
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
}
t.Run(tc.moduleType, func(t *testing.T) {
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
- bp2buildTestCase{
- moduleTypeUnderTest: tc.moduleType,
- moduleTypeUnderTestFactory: tc.factory,
- blueprint: fmt.Sprintf(bp, tc.moduleType, tc.moduleType),
- expectedBazelTargets: expectedBazelTargets,
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+ Bp2buildTestCase{
+ ModuleTypeUnderTest: tc.moduleType,
+ ModuleTypeUnderTestFactory: tc.factory,
+ Blueprint: fmt.Sprintf(bp, tc.moduleType, tc.moduleType),
+ ExpectedBazelTargets: expectedBazelTargets,
})
})
}
@@ -131,6 +129,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -139,14 +138,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,
},
}
@@ -168,13 +170,13 @@
}`
for _, tc := range testCases {
- fooAttrs := attrNameToString{
+ fooAttrs := AttrNameToString{
"cmd": `"$(locations :foo.tools) -s $(OUTS) $(SRCS)"`,
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
"tools": `[":foo.tools"]`,
}
- fooToolsAttrs := attrNameToString{
+ fooToolsAttrs := AttrNameToString{
"cmd": `"cp $(SRCS) $(OUTS)"`,
"outs": `[
"foo_tool.out",
@@ -183,27 +185,18 @@
"srcs": `["foo_tool.in"]`,
}
- if tc.moduleType == "java_genrule_host" {
- compatibilityAttrs := `select({
- "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
- "//conditions:default": [],
- })`
- fooAttrs["target_compatible_with"] = compatibilityAttrs
- fooToolsAttrs["target_compatible_with"] = compatibilityAttrs
- }
-
expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", fooAttrs),
- makeBazelTarget("genrule", "foo.tools", fooToolsAttrs),
+ 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{
- moduleTypeUnderTest: tc.moduleType,
- moduleTypeUnderTestFactory: tc.factory,
- blueprint: fmt.Sprintf(bp, tc.moduleType, tc.moduleType),
- expectedBazelTargets: expectedBazelTargets,
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+ Bp2buildTestCase{
+ ModuleTypeUnderTest: tc.moduleType,
+ ModuleTypeUnderTestFactory: tc.factory,
+ Blueprint: fmt.Sprintf(bp, tc.moduleType, tc.moduleType),
+ ExpectedBazelTargets: expectedBazelTargets,
})
})
}
@@ -213,6 +206,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -221,14 +215,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,
},
}
@@ -242,32 +239,25 @@
}`
for _, tc := range testCases {
- moduleAttrs := attrNameToString{
+ moduleAttrs := AttrNameToString{
"cmd": `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
"tools": `["//other:foo.tool"]`,
}
- if tc.moduleType == "java_genrule_host" {
- moduleAttrs["target_compatible_with"] = `select({
- "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
- "//conditions:default": [],
- })`
- }
-
expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", moduleAttrs),
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
}
t.Run(tc.moduleType, func(t *testing.T) {
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
- bp2buildTestCase{
- moduleTypeUnderTest: tc.moduleType,
- moduleTypeUnderTestFactory: tc.factory,
- blueprint: fmt.Sprintf(bp, tc.moduleType),
- expectedBazelTargets: expectedBazelTargets,
- filesystem: otherGenruleBp(tc.moduleType),
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+ Bp2buildTestCase{
+ ModuleTypeUnderTest: tc.moduleType,
+ ModuleTypeUnderTestFactory: tc.factory,
+ Blueprint: fmt.Sprintf(bp, tc.moduleType),
+ ExpectedBazelTargets: expectedBazelTargets,
+ Filesystem: otherGenruleBp(tc.moduleType),
})
})
}
@@ -277,6 +267,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -285,14 +276,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,
},
}
@@ -306,32 +300,25 @@
}`
for _, tc := range testCases {
- moduleAttrs := attrNameToString{
+ moduleAttrs := AttrNameToString{
"cmd": `"$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)"`,
"outs": `["foo.out"]`,
"srcs": `["//other:other.tool"]`,
"tools": `["//other:foo.tool"]`,
}
- if tc.moduleType == "java_genrule_host" {
- moduleAttrs["target_compatible_with"] = `select({
- "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
- "//conditions:default": [],
- })`
- }
-
expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", moduleAttrs),
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
}
t.Run(tc.moduleType, func(t *testing.T) {
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
- bp2buildTestCase{
- moduleTypeUnderTest: tc.moduleType,
- moduleTypeUnderTestFactory: tc.factory,
- blueprint: fmt.Sprintf(bp, tc.moduleType),
- expectedBazelTargets: expectedBazelTargets,
- filesystem: otherGenruleBp(tc.moduleType),
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+ Bp2buildTestCase{
+ ModuleTypeUnderTest: tc.moduleType,
+ ModuleTypeUnderTestFactory: tc.factory,
+ Blueprint: fmt.Sprintf(bp, tc.moduleType),
+ ExpectedBazelTargets: expectedBazelTargets,
+ Filesystem: otherGenruleBp(tc.moduleType),
})
})
}
@@ -341,6 +328,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -349,14 +337,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,
},
}
@@ -370,7 +361,7 @@
}`
for _, tc := range testCases {
- moduleAttrs := attrNameToString{
+ moduleAttrs := AttrNameToString{
"cmd": `"$(location //other:foo.tool) -s $(OUTS) $(SRCS)"`,
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
@@ -380,25 +371,18 @@
]`,
}
- if tc.moduleType == "java_genrule_host" {
- moduleAttrs["target_compatible_with"] = `select({
- "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
- "//conditions:default": [],
- })`
- }
-
expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", moduleAttrs),
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
}
t.Run(tc.moduleType, func(t *testing.T) {
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
- bp2buildTestCase{
- moduleTypeUnderTest: tc.moduleType,
- moduleTypeUnderTestFactory: tc.factory,
- blueprint: fmt.Sprintf(bp, tc.moduleType),
- expectedBazelTargets: expectedBazelTargets,
- filesystem: otherGenruleBp(tc.moduleType),
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+ Bp2buildTestCase{
+ ModuleTypeUnderTest: tc.moduleType,
+ ModuleTypeUnderTestFactory: tc.factory,
+ Blueprint: fmt.Sprintf(bp, tc.moduleType),
+ ExpectedBazelTargets: expectedBazelTargets,
+ Filesystem: otherGenruleBp(tc.moduleType),
})
})
}
@@ -408,6 +392,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -416,14 +401,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,
},
}
@@ -437,7 +425,7 @@
}`
for _, tc := range testCases {
- moduleAttrs := attrNameToString{
+ moduleAttrs := AttrNameToString{
"cmd": `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
@@ -447,25 +435,18 @@
]`,
}
- if tc.moduleType == "java_genrule_host" {
- moduleAttrs["target_compatible_with"] = `select({
- "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
- "//conditions:default": [],
- })`
- }
-
expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", moduleAttrs),
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
}
t.Run(tc.moduleType, func(t *testing.T) {
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
- bp2buildTestCase{
- moduleTypeUnderTest: tc.moduleType,
- moduleTypeUnderTestFactory: tc.factory,
- blueprint: fmt.Sprintf(bp, tc.moduleType),
- expectedBazelTargets: expectedBazelTargets,
- filesystem: otherGenruleBp(tc.moduleType),
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+ Bp2buildTestCase{
+ ModuleTypeUnderTest: tc.moduleType,
+ ModuleTypeUnderTestFactory: tc.factory,
+ Blueprint: fmt.Sprintf(bp, tc.moduleType),
+ ExpectedBazelTargets: expectedBazelTargets,
+ Filesystem: otherGenruleBp(tc.moduleType),
})
})
}
@@ -475,6 +456,7 @@
testCases := []struct {
moduleType string
factory android.ModuleFactory
+ hod android.HostOrDeviceSupported
}{
{
moduleType: "genrule",
@@ -483,14 +465,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,
},
}
@@ -503,40 +488,33 @@
}`
for _, tc := range testCases {
- moduleAttrs := attrNameToString{
+ moduleAttrs := AttrNameToString{
"cmd": `"cp $(SRCS) $(OUTS)"`,
"outs": `["foo.out"]`,
"srcs": `["foo.in"]`,
}
- if tc.moduleType == "java_genrule_host" {
- moduleAttrs["target_compatible_with"] = `select({
- "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
- "//conditions:default": [],
- })`
- }
-
expectedBazelTargets := []string{
- makeBazelTarget("genrule", "foo", moduleAttrs),
+ makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
}
t.Run(tc.moduleType, func(t *testing.T) {
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
- bp2buildTestCase{
- moduleTypeUnderTest: tc.moduleType,
- moduleTypeUnderTestFactory: tc.factory,
- blueprint: fmt.Sprintf(bp, tc.moduleType),
- expectedBazelTargets: expectedBazelTargets,
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+ Bp2buildTestCase{
+ ModuleTypeUnderTest: tc.moduleType,
+ ModuleTypeUnderTestFactory: tc.factory,
+ Blueprint: fmt.Sprintf(bp, tc.moduleType),
+ ExpectedBazelTargets: expectedBazelTargets,
})
})
}
}
func TestGenruleBp2BuildInlinesDefaults(t *testing.T) {
- testCases := []bp2buildTestCase{
+ testCases := []Bp2buildTestCase{
{
- description: "genrule applies properties from a genrule_defaults dependency if not specified",
- blueprint: `genrule_defaults {
+ Description: "genrule applies properties from a genrule_defaults dependency if not specified",
+ Blueprint: `genrule_defaults {
name: "gen_defaults",
cmd: "do-something $(in) $(out)",
}
@@ -548,8 +526,8 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("genrule", "gen", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("genrule", "gen", AttrNameToString{
"cmd": `"do-something $(SRCS) $(OUTS)"`,
"outs": `["out"]`,
"srcs": `["in1"]`,
@@ -557,8 +535,8 @@
},
},
{
- description: "genrule does merges properties from a genrule_defaults dependency, latest-first",
- blueprint: `genrule_defaults {
+ Description: "genrule does merges properties from a genrule_defaults dependency, latest-first",
+ Blueprint: `genrule_defaults {
name: "gen_defaults",
out: ["out-from-defaults"],
srcs: ["in-from-defaults"],
@@ -573,8 +551,8 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("genrule", "gen", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("genrule", "gen", AttrNameToString{
"cmd": `"do-something $(SRCS) $(OUTS)"`,
"outs": `[
"out-from-defaults",
@@ -588,8 +566,8 @@
},
},
{
- description: "genrule applies properties from list of genrule_defaults",
- blueprint: `genrule_defaults {
+ Description: "genrule applies properties from list of genrule_defaults",
+ Blueprint: `genrule_defaults {
name: "gen_defaults1",
cmd: "cp $(in) $(out)",
}
@@ -606,8 +584,8 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("genrule", "gen", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("genrule", "gen", AttrNameToString{
"cmd": `"cp $(SRCS) $(OUTS)"`,
"outs": `["out"]`,
"srcs": `["in1"]`,
@@ -615,8 +593,8 @@
},
},
{
- description: "genrule applies properties from genrule_defaults transitively",
- blueprint: `genrule_defaults {
+ Description: "genrule applies properties from genrule_defaults transitively",
+ Blueprint: `genrule_defaults {
name: "gen_defaults1",
defaults: ["gen_defaults2"],
cmd: "cmd1 $(in) $(out)", // overrides gen_defaults2's cmd property value.
@@ -643,8 +621,8 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("genrule", "gen", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("genrule", "gen", AttrNameToString{
"cmd": `"cmd1 $(SRCS) $(OUTS)"`,
"outs": `[
"out-from-3",
@@ -661,8 +639,55 @@
}
for _, testCase := range testCases {
- t.Run(testCase.description, func(t *testing.T) {
+ t.Run(testCase.Description, func(t *testing.T) {
runGenruleTestCase(t, testCase)
})
}
}
+
+func TestCcGenruleArchAndExcludeSrcs(t *testing.T) {
+ name := "cc_genrule with arch"
+ bp := `
+ cc_genrule {
+ name: "foo",
+ srcs: [
+ "foo1.in",
+ "foo2.in",
+ ],
+ exclude_srcs: ["foo2.in"],
+ arch: {
+ arm: {
+ srcs: [
+ "foo1_arch.in",
+ "foo2_arch.in",
+ ],
+ exclude_srcs: ["foo2_arch.in"],
+ },
+ },
+ cmd: "cat $(in) > $(out)",
+ bazel_module: { bp2build_available: true },
+ }`
+
+ expectedBazelAttrs := AttrNameToString{
+ "srcs": `["foo1.in"] + select({
+ "//build/bazel/platforms/arch:arm": ["foo1_arch.in"],
+ "//conditions:default": [],
+ })`,
+ "cmd": `"cat $(SRCS) > $(OUTS)"`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ }
+
+ expectedBazelTargets := []string{
+ MakeBazelTargetNoRestrictions("genrule", "foo", expectedBazelAttrs),
+ }
+
+ t.Run(name, func(t *testing.T) {
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+ Bp2buildTestCase{
+ ModuleTypeUnderTest: "cc_genrule",
+ ModuleTypeUnderTestFactory: cc.GenRuleFactory,
+ Blueprint: bp,
+ ExpectedBazelTargets: expectedBazelTargets,
+ })
+ })
+}
diff --git a/bp2build/gensrcs_conversion_test.go b/bp2build/gensrcs_conversion_test.go
new file mode 100644
index 0000000..4845973
--- /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{
+ MakeBazelTargetNoRestrictions("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 4fc07e0..c821f59 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -22,17 +22,18 @@
"android/soong/java"
)
-func runJavaBinaryHostTestCase(t *testing.T, tc bp2buildTestCase) {
+func runJavaBinaryHostTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "java_binary_host"
- (&tc).moduleTypeUnderTestFactory = java.BinaryHostFactory
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+ (&tc).ModuleTypeUnderTest = "java_binary_host"
+ (&tc).ModuleTypeUnderTestFactory = java.BinaryHostFactory
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("cc_library_host_shared", cc.LibraryHostSharedFactory)
ctx.RegisterModuleType("java_library", java.LibraryFactory)
+ ctx.RegisterModuleType("java_import_host", java.ImportFactory)
}, tc)
}
-var fs = map[string]string{
+var testFs = map[string]string{
"test.mf": "Main-Class: com.android.test.MainClass",
"other/Android.bp": `cc_library_host_shared {
name: "jni-lib-1",
@@ -41,10 +42,10 @@
}
func TestJavaBinaryHost(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 {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with srcs, exclude_srcs, jni_libs, javacflags, and manifest.",
+ Filesystem: testFs,
+ Blueprint: `java_binary_host {
name: "java-binary-host-1",
srcs: ["a.java", "b.java"],
exclude_srcs: ["b.java"],
@@ -52,28 +53,37 @@
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{
- "srcs": `["a.java"]`,
- "main_class": `"com.android.test.MainClass"`,
- "deps": `["//other:jni-lib-1"]`,
- "jvm_flags": `["-Djava.library.path=$${RUNPATH}other"]`,
- "javacopts": `["-Xdoclint:all/protected"]`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-binary-host-1_lib", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "deps": `["//other:jni-lib-1"]`,
+ "java_version": `"8"`,
+ "javacopts": `["-Xdoclint:all/protected"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
"//conditions:default": [],
})`,
}),
+ MakeBazelTarget("java_binary", "java-binary-host-1", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "jvm_flags": `["-Djava.library.path=$${RUNPATH}other"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ "runtime_deps": `[":java-binary-host-1_lib"]`,
+ }),
},
})
}
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 {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with srcs, exclude_srcs, jni_libs, javacflags, and manifest.",
+ Filesystem: testFs,
+ Blueprint: `java_binary_host {
name: "java-binary-host-1",
static_libs: ["java-dep-1"],
manifest: "test.mf",
@@ -86,8 +96,8 @@
bazel_module: { bp2build_available: false },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_binary", "java-binary-host-1", attrNameToString{
+ 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({
@@ -98,3 +108,226 @@
},
})
}
+
+func TestJavaBinaryHostLibs(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with srcs, libs.",
+ Filesystem: testFs,
+ Blueprint: `java_binary_host {
+ name: "java-binary-host-libs",
+ libs: ["java-lib-dep-1"],
+ manifest: "test.mf",
+ srcs: ["a.java"],
+}
+
+java_import_host{
+ name: "java-lib-dep-1",
+ jars: ["foo.jar"],
+ bazel_module: { bp2build_available: false },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-binary-host-libs_lib", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "deps": `[":java-lib-dep-1-neverlink"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host-libs", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ "runtime_deps": `[":java-binary-host-libs_lib"]`,
+ }),
+ },
+ })
+}
+
+func TestJavaBinaryHostKotlinSrcs(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with srcs, libs.",
+ Filesystem: testFs,
+ Blueprint: `java_binary_host {
+ name: "java-binary-host",
+ manifest: "test.mf",
+ srcs: ["a.java", "b.kt"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-binary-host_lib"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestJavaBinaryHostKotlinCommonSrcs(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with common_srcs",
+ Filesystem: testFs,
+ Blueprint: `java_binary_host {
+ name: "java-binary-host",
+ manifest: "test.mf",
+ srcs: ["a.java"],
+ common_srcs: ["b.kt"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "common_srcs": `["b.kt"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-binary-host_lib"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestJavaBinaryHostKotlinWithResourceDir(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with srcs, libs, resource dir .",
+ Filesystem: map[string]string{
+ "test.mf": "Main-Class: com.android.test.MainClass",
+ "res/a.res": "",
+ "res/dir1/b.res": "",
+ },
+ Blueprint: `java_binary_host {
+ name: "java-binary-host",
+ manifest: "test.mf",
+ srcs: ["a.java", "b.kt"],
+ java_resource_dirs: ["res"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "resources": `[
+ "res/a.res",
+ "res/dir1/b.res",
+ ]`,
+ "resource_strip_prefix": `"res"`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-binary-host_lib"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestJavaBinaryHostKotlinWithResources(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with srcs, libs, resources.",
+ Filesystem: map[string]string{
+ "test.mf": "Main-Class: com.android.test.MainClass",
+ "res/a.res": "",
+ "res/b.res": "",
+ },
+ Blueprint: `java_binary_host {
+ name: "java-binary-host",
+ manifest: "test.mf",
+ srcs: ["a.java", "b.kt"],
+ java_resources: ["res/a.res", "res/b.res"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "resources": `[
+ "res/a.res",
+ "res/b.res",
+ ]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-binary-host_lib"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestJavaBinaryHostKotlinCflags(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with kotlincflags",
+ Filesystem: testFs,
+ Blueprint: `java_binary_host {
+ name: "java-binary-host",
+ manifest: "test.mf",
+ srcs: ["a.kt"],
+ kotlincflags: ["-flag1", "-flag2"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+ "srcs": `["a.kt"]`,
+ "kotlincflags": `[
+ "-flag1",
+ "-flag2",
+ ]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-binary-host_lib"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/java_host_for_device_conversion_test.go b/bp2build/java_host_for_device_conversion_test.go
new file mode 100644
index 0000000..448cba4
--- /dev/null
+++ b/bp2build/java_host_for_device_conversion_test.go
@@ -0,0 +1,65 @@
+// Copyright 2023 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 runJavaHostForDeviceTestCaseWithRegistrationCtxFunc(t *testing.T, tc Bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) {
+ t.Helper()
+ (&tc).ModuleTypeUnderTest = "java_host_for_device"
+ (&tc).ModuleTypeUnderTestFactory = java.HostForDeviceFactory
+ RunBp2BuildTestCase(t, registrationCtxFunc, tc)
+}
+
+func runJavaHostForDeviceTestCase(t *testing.T, tc Bp2buildTestCase) {
+ t.Helper()
+ runJavaHostForDeviceTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("java_library", java.LibraryFactory)
+ })
+}
+
+func TestJavaHostForDevice(t *testing.T) {
+ runJavaHostForDeviceTestCase(t, Bp2buildTestCase{
+ Description: "java_host_for_device test",
+ Blueprint: `java_host_for_device {
+ name: "java-lib-1",
+ libs: ["java-lib-2"],
+ bazel_module: { bp2build_available: true },
+}
+
+java_library {
+ name: "java-lib-2",
+ srcs: ["b.java"],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_host_for_device", "java-lib-1", AttrNameToString{
+ "exports": `[":java-lib-2"]`,
+ }),
+ MakeNeverlinkDuplicateTargetWithAttrs("java_library", "java-lib-1", AttrNameToString{
+ "sdk_version": `"none"`,
+ }),
+ MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{
+ "srcs": `["b.java"]`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-2"),
+ },
+ })
+}
diff --git a/bp2build/java_import_conversion_test.go b/bp2build/java_import_conversion_test.go
index 0b3191c..5661620 100644
--- a/bp2build/java_import_conversion_test.go
+++ b/bp2build/java_import_conversion_test.go
@@ -21,45 +21,50 @@
"testing"
)
-func runJavaImportTestCase(t *testing.T, tc bp2buildTestCase) {
+func runJavaImportTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, registerJavaImportModuleTypes, tc)
+ RunBp2BuildTestCase(t, registerJavaImportModuleTypes, tc)
}
func registerJavaImportModuleTypes(ctx android.RegistrationContext) {
}
func TestJavaImportMinimal(t *testing.T) {
- runJavaImportTestCase(t, bp2buildTestCase{
- description: "Java import - simple example",
- moduleTypeUnderTest: "java_import",
- moduleTypeUnderTestFactory: java.ImportFactory,
- filesystem: map[string]string{
+ runJavaImportTestCase(t, Bp2buildTestCase{
+ Description: "Java import - simple example",
+ ModuleTypeUnderTest: "java_import",
+ ModuleTypeUnderTestFactory: java.ImportFactory,
+ Filesystem: map[string]string{
"import.jar": "",
},
- blueprint: `
+ Blueprint: `
java_import {
name: "example_import",
jars: ["import.jar"],
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_import", "example_import", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_import", "example_import", AttrNameToString{
"jars": `["import.jar"]`,
}),
+ MakeBazelTarget("java_library", "example_import-neverlink", AttrNameToString{
+ "exports": `[":example_import"]`,
+ "neverlink": `True`,
+ "sdk_version": `"none"`,
+ }),
}})
}
func TestJavaImportArchVariant(t *testing.T) {
- runJavaImportTestCase(t, bp2buildTestCase{
- description: "Java import - simple example",
- moduleTypeUnderTest: "java_import",
- moduleTypeUnderTestFactory: java.ImportFactory,
- filesystem: map[string]string{
+ runJavaImportTestCase(t, Bp2buildTestCase{
+ Description: "Java import - simple example",
+ ModuleTypeUnderTest: "java_import",
+ ModuleTypeUnderTestFactory: java.ImportFactory,
+ Filesystem: map[string]string{
"import.jar": "",
},
- blueprint: `
+ Blueprint: `
java_import {
name: "example_import",
target: {
@@ -73,13 +78,45 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_import", "example_import", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_import", "example_import", AttrNameToString{
"jars": `select({
"//build/bazel/platforms/os:android": ["android.jar"],
- "//build/bazel/platforms/os:linux": ["linux.jar"],
+ "//build/bazel/platforms/os:linux_glibc": ["linux.jar"],
"//conditions:default": [],
})`,
}),
+ MakeBazelTarget("java_library", "example_import-neverlink", AttrNameToString{
+ "exports": `[":example_import"]`,
+ "neverlink": `True`,
+ "sdk_version": `"none"`,
+ }),
+ }})
+}
+
+func TestJavaImportHost(t *testing.T) {
+ runJavaImportTestCase(t, Bp2buildTestCase{
+ Description: "Java import host- simple example",
+ ModuleTypeUnderTest: "java_import_host",
+ ModuleTypeUnderTestFactory: java.ImportFactory,
+ Filesystem: map[string]string{
+ "import.jar": "",
+ },
+ Blueprint: `
+java_import_host {
+ name: "example_import",
+ jars: ["import.jar"],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_import", "example_import", AttrNameToString{
+ "jars": `["import.jar"]`,
+ }),
+ MakeBazelTarget("java_library", "example_import-neverlink", AttrNameToString{
+ "exports": `[":example_import"]`,
+ "neverlink": `True`,
+ "sdk_version": `"none"`,
+ }),
}})
}
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index ccc52ef..24b763b 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -22,22 +22,22 @@
"android/soong/java"
)
-func runJavaLibraryTestCaseWithRegistrationCtxFunc(t *testing.T, tc bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) {
+func runJavaLibraryTestCaseWithRegistrationCtxFunc(t *testing.T, tc Bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) {
t.Helper()
- (&tc).moduleTypeUnderTest = "java_library"
- (&tc).moduleTypeUnderTestFactory = java.LibraryFactory
- runBp2BuildTestCase(t, registrationCtxFunc, tc)
+ (&tc).ModuleTypeUnderTest = "java_library"
+ (&tc).ModuleTypeUnderTestFactory = java.LibraryFactory
+ RunBp2BuildTestCase(t, registrationCtxFunc, tc)
}
-func runJavaLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+func runJavaLibraryTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
runJavaLibraryTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) {})
}
func TestJavaLibrary(t *testing.T) {
- runJavaLibraryTestCase(t, bp2buildTestCase{
- description: "java_library with srcs, exclude_srcs and libs",
- blueprint: `java_library {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Description: "java_library with srcs, exclude_srcs and libs",
+ Blueprint: `java_library {
name: "java-lib-1",
srcs: ["a.java", "b.java"],
exclude_srcs: ["b.java"],
@@ -50,21 +50,23 @@
srcs: ["b.java"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
"srcs": `["a.java"]`,
- "deps": `[":java-lib-2"]`,
+ "deps": `[":java-lib-2-neverlink"]`,
}),
- makeBazelTarget("java_library", "java-lib-2", attrNameToString{
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+ MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{
"srcs": `["b.java"]`,
}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-2"),
},
})
}
func TestJavaLibraryConvertsStaticLibsToDepsAndExports(t *testing.T) {
- runJavaLibraryTestCase(t, bp2buildTestCase{
- blueprint: `java_library {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Blueprint: `java_library {
name: "java-lib-1",
srcs: ["a.java"],
libs: ["java-lib-2"],
@@ -83,22 +85,23 @@
srcs: ["c.java"],
bazel_module: { bp2build_available: false },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
"srcs": `["a.java"]`,
"deps": `[
- ":java-lib-2",
+ ":java-lib-2-neverlink",
":java-lib-3",
]`,
"exports": `[":java-lib-3"]`,
}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
},
})
}
func TestJavaLibraryConvertsStaticLibsToExportsIfNoSrcs(t *testing.T) {
- runJavaLibraryTestCase(t, bp2buildTestCase{
- blueprint: `java_library {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Blueprint: `java_library {
name: "java-lib-1",
static_libs: ["java-lib-2"],
bazel_module: { bp2build_available: true },
@@ -109,18 +112,19 @@
srcs: ["a.java"],
bazel_module: { bp2build_available: false },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
"exports": `[":java-lib-2"]`,
}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
},
})
}
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 {
+ 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 },
@@ -131,13 +135,13 @@
srcs: ["a.java"],
bazel_module: { bp2build_available: false },
}`,
- expectedBazelTargets: []string{},
+ ExpectedBazelTargets: []string{},
})
}
func TestJavaLibraryPlugins(t *testing.T) {
- runJavaLibraryTestCaseWithRegistrationCtxFunc(t, bp2buildTestCase{
- blueprint: `java_library {
+ runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
+ Blueprint: `java_library {
name: "java-lib-1",
plugins: ["java-plugin-1"],
bazel_module: { bp2build_available: true },
@@ -148,19 +152,40 @@
srcs: ["a.java"],
bazel_module: { bp2build_available: false },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
"plugins": `[":java-plugin-1"]`,
}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-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"]`,
+ "java_version": `"11"`,
+ }),
+ MakeNeverlinkDuplicateTargetWithAttrs(
+ "java_library",
+ "java-lib-1",
+ AttrNameToString{"java_version": `"11"`}),
+ },
+ })
+}
+
func TestJavaLibraryErrorproneJavacflagsEnabledManually(t *testing.T) {
- runJavaLibraryTestCase(t, bp2buildTestCase{
- blueprint: `java_library {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Blueprint: `java_library {
name: "java-lib-1",
srcs: ["a.java"],
javacflags: ["-Xsuper-fast"],
@@ -169,21 +194,22 @@
javacflags: ["-Xep:SpeedLimit:OFF"],
},
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
"javacopts": `[
"-Xsuper-fast",
"-Xep:SpeedLimit:OFF",
]`,
"srcs": `["a.java"]`,
}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
},
})
}
func TestJavaLibraryErrorproneJavacflagsErrorproneDisabledByDefault(t *testing.T) {
- runJavaLibraryTestCase(t, bp2buildTestCase{
- blueprint: `java_library {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Blueprint: `java_library {
name: "java-lib-1",
srcs: ["a.java"],
javacflags: ["-Xsuper-fast"],
@@ -191,18 +217,19 @@
javacflags: ["-Xep:SpeedLimit:OFF"],
},
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
"javacopts": `["-Xsuper-fast"]`,
"srcs": `["a.java"]`,
}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
},
})
}
func TestJavaLibraryErrorproneJavacflagsErrorproneDisabledManually(t *testing.T) {
- runJavaLibraryTestCase(t, bp2buildTestCase{
- blueprint: `java_library {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Blueprint: `java_library {
name: "java-lib-1",
srcs: ["a.java"],
javacflags: ["-Xsuper-fast"],
@@ -211,21 +238,22 @@
javacflags: ["-Xep:SpeedLimit:OFF"],
},
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
"javacopts": `["-Xsuper-fast"]`,
"srcs": `["a.java"]`,
}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
},
})
}
func TestJavaLibraryLogTags(t *testing.T) {
- runJavaLibraryTestCase(t, bp2buildTestCase{
- description: "Java library - logtags creates separate dependency",
- moduleTypeUnderTest: "java_library",
- moduleTypeUnderTestFactory: java.LibraryFactory,
- blueprint: `java_library {
+ 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",
@@ -235,19 +263,562 @@
],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("event_log_tags", "example_lib_logtags", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("event_log_tags", "example_lib_logtags", AttrNameToString{
"srcs": `[
"a.logtag",
"b.logtag",
]`,
}),
- makeBazelTarget("java_library", "example_lib", attrNameToString{
+ MakeBazelTarget("java_library", "example_lib", AttrNameToString{
"srcs": `[
"a.java",
"b.java",
":example_lib_logtags",
]`,
}),
+ MakeNeverlinkDuplicateTarget("java_library", "example_lib"),
}})
}
+
+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",
+ ]`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+ },
+ })
+}
+
+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",
+ ]`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+ },
+ })
+}
+
+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"]`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+ },
+ })
+}
+
+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",
+ ]`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+ },
+ })
+}
+
+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{},
+ })
+}
+
+func TestJavaLibraryAidl(t *testing.T) {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Description: "Java library - aidl creates separate dependency",
+ ModuleTypeUnderTest: "java_library",
+ ModuleTypeUnderTestFactory: java.LibraryFactory,
+ Blueprint: `java_library {
+ name: "example_lib",
+ srcs: [
+ "a.java",
+ "b.java",
+ "a.aidl",
+ "b.aidl",
+ ],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("aidl_library", "example_lib_aidl_library", AttrNameToString{
+ "srcs": `[
+ "a.aidl",
+ "b.aidl",
+ ]`,
+ }),
+ MakeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{
+ "deps": `[":example_lib_aidl_library"]`,
+ }),
+ MakeBazelTarget("java_library", "example_lib", AttrNameToString{
+ "deps": `[":example_lib_java_aidl_library"]`,
+ "exports": `[":example_lib_java_aidl_library"]`,
+ "srcs": `[
+ "a.java",
+ "b.java",
+ ]`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "example_lib"),
+ }})
+}
+
+func TestJavaLibraryAidlSrcsNoFileGroup(t *testing.T) {
+ runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
+ Description: "Java library - aidl filegroup is parsed",
+ ModuleTypeUnderTest: "java_library",
+ ModuleTypeUnderTestFactory: java.LibraryFactory,
+ Blueprint: `
+java_library {
+ name: "example_lib",
+ srcs: [
+ "a.java",
+ "b.aidl",
+ ],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("aidl_library", "example_lib_aidl_library", AttrNameToString{
+ "srcs": `["b.aidl"]`,
+ }),
+ MakeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{
+ "deps": `[":example_lib_aidl_library"]`,
+ }),
+ MakeBazelTarget("java_library", "example_lib", AttrNameToString{
+ "deps": `[":example_lib_java_aidl_library"]`,
+ "exports": `[":example_lib_java_aidl_library"]`,
+ "srcs": `["a.java"]`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "example_lib"),
+ },
+ }, func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ })
+}
+
+func TestJavaLibraryAidlFilegroup(t *testing.T) {
+ runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
+ Description: "Java library - aidl filegroup is parsed",
+ ModuleTypeUnderTest: "java_library",
+ ModuleTypeUnderTestFactory: java.LibraryFactory,
+ Blueprint: `
+filegroup {
+ name: "random_other_files",
+ srcs: [
+ "a.java",
+ "b.java",
+ ],
+}
+filegroup {
+ name: "aidl_files",
+ srcs: [
+ "a.aidl",
+ "b.aidl",
+ ],
+}
+java_library {
+ name: "example_lib",
+ srcs: [
+ "a.java",
+ "b.java",
+ ":aidl_files",
+ ":random_other_files",
+ ],
+ bazel_module: { bp2build_available: true },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("aidl_library", "aidl_files", AttrNameToString{
+ "srcs": `[
+ "a.aidl",
+ "b.aidl",
+ ]`,
+ "tags": `["apex_available=//apex_available:anyapex"]`,
+ }),
+ MakeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{
+ "deps": `[":aidl_files"]`,
+ }),
+ MakeBazelTarget("java_library", "example_lib", AttrNameToString{
+ "deps": `[":example_lib_java_aidl_library"]`,
+ "exports": `[":example_lib_java_aidl_library"]`,
+ "srcs": `[
+ "a.java",
+ "b.java",
+ ":random_other_files",
+ ]`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "example_lib"),
+ MakeBazelTargetNoRestrictions("filegroup", "random_other_files", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.java",
+ ]`,
+ }),
+ },
+ }, func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ })
+}
+
+func TestJavaLibraryAidlNonAdjacentAidlFilegroup(t *testing.T) {
+ runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
+ Description: "java_library with non adjacent aidl filegroup",
+ ModuleTypeUnderTest: "java_library",
+ ModuleTypeUnderTestFactory: java.LibraryFactory,
+ Filesystem: map[string]string{
+ "path/to/A/Android.bp": `
+filegroup {
+ name: "A_aidl",
+ srcs: ["aidl/A.aidl"],
+ path: "aidl",
+}`,
+ },
+ Blueprint: `
+java_library {
+ name: "foo",
+ srcs: [
+ ":A_aidl",
+ ],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_aidl_library", "foo_java_aidl_library", AttrNameToString{
+ "deps": `["//path/to/A:A_aidl"]`,
+ }),
+ MakeBazelTarget("java_library", "foo", AttrNameToString{
+ "exports": `[":foo_java_aidl_library"]`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "foo"),
+ },
+ }, func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ })
+}
+
+func TestConvertArmNeonVariant(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+ Description: "Android Library - simple arch feature",
+ ModuleTypeUnderTest: "android_library",
+ ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+ Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+ name: "TestLib",
+ manifest: "manifest/AndroidManifest.xml",
+ srcs: ["lib.java"],
+ arch: {
+ arm: {
+ neon: {
+ srcs: ["arm_neon.java"],
+ },
+ },
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget(
+ "android_library",
+ "TestLib",
+ AttrNameToString{
+ "srcs": `["lib.java"] + select({
+ "//build/bazel/platforms/arch/variants:arm-neon": ["arm_neon.java"],
+ "//conditions:default": [],
+ })`,
+ "manifest": `"manifest/AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ }),
+ MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+ }})
+}
+
+func TestConvertMultipleArchFeatures(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+ Description: "Android Library - multiple arch features",
+ ModuleTypeUnderTest: "android_library",
+ ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+ Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+ name: "TestLib",
+ manifest: "manifest/AndroidManifest.xml",
+ srcs: ["lib.java"],
+ arch: {
+ x86: {
+ ssse3: {
+ srcs: ["ssse3.java"],
+ },
+ sse4_1: {
+ srcs: ["sse4_1.java"],
+ },
+ },
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget(
+ "android_library",
+ "TestLib",
+ AttrNameToString{
+ "srcs": `["lib.java"] + select({
+ "//build/bazel/platforms/arch/variants:x86-sse4_1": ["sse4_1.java"],
+ "//build/bazel/platforms/arch/variants:x86-sse4_1-ssse3": [
+ "sse4_1.java",
+ "ssse3.java",
+ ],
+ "//build/bazel/platforms/arch/variants:x86-ssse3": ["ssse3.java"],
+ "//conditions:default": [],
+ })`,
+ "manifest": `"manifest/AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ }),
+ MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+ }})
+}
+
+func TestConvertExcludeSrcsArchFeature(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+ Description: "Android Library - exclude_srcs with arch feature",
+ ModuleTypeUnderTest: "android_library",
+ ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+ Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+ name: "TestLib",
+ manifest: "manifest/AndroidManifest.xml",
+ srcs: ["lib.java"],
+ arch: {
+ arm: {
+ srcs: ["arm_non_neon.java"],
+ neon: {
+ exclude_srcs: ["arm_non_neon.java"],
+ },
+ },
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget(
+ "android_library",
+ "TestLib",
+ AttrNameToString{
+ "srcs": `["lib.java"] + select({
+ "//build/bazel/platforms/arch/variants:arm-neon": [],
+ "//build/bazel/platforms/arch:arm": ["arm_non_neon.java"],
+ "//conditions:default": [],
+ })`,
+ "manifest": `"manifest/AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ }),
+ MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+ }})
+}
+
+func TestJavaLibraryKotlinSrcs(t *testing.T) {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Description: "java_library with kotlin srcs",
+ Blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java", "b.java", "c.kt"],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-lib-1", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.java",
+ "c.kt",
+ ]`,
+ }),
+ MakeNeverlinkDuplicateTarget("kt_jvm_library", "java-lib-1"),
+ },
+ })
+}
+
+func TestJavaLibraryKotlincflags(t *testing.T) {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Description: "java_library with kotlincfalgs",
+ Blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: [ "a.kt"],
+ kotlincflags: ["-flag1", "-flag2"],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-lib-1", AttrNameToString{
+ "srcs": `["a.kt"]`,
+ "kotlincflags": `[
+ "-flag1",
+ "-flag2",
+ ]`,
+ }),
+ MakeNeverlinkDuplicateTarget("kt_jvm_library", "java-lib-1"),
+ },
+ })
+}
+
+func TestJavaLibraryKotlinCommonSrcs(t *testing.T) {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Description: "java_library with kotlin common_srcs",
+ Blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java", "b.java"],
+ common_srcs: ["c.kt"],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-lib-1", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.java",
+ ]`,
+ "common_srcs": `["c.kt"]`,
+ }),
+ MakeNeverlinkDuplicateTarget("kt_jvm_library", "java-lib-1"),
+ },
+ })
+}
+
+func TestJavaLibraryArchVariantDeps(t *testing.T) {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Description: "java_library with arch variant libs",
+ Blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java"],
+ libs: ["java-lib-2"],
+ target: {
+ android: {
+ libs: ["java-lib-3"],
+ static_libs: ["java-lib-4"],
+ },
+ },
+ bazel_module: { bp2build_available: true },
+}
+
+ java_library{
+ name: "java-lib-2",
+}
+
+ java_library{
+ name: "java-lib-3",
+}
+
+ java_library{
+ name: "java-lib-4",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "exports": `select({
+ "//build/bazel/platforms/os:android": [":java-lib-4"],
+ "//conditions:default": [],
+ })`,
+ "deps": `[":java-lib-2-neverlink"] + select({
+ "//build/bazel/platforms/os:android": [
+ ":java-lib-3-neverlink",
+ ":java-lib-4",
+ ],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+ MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-2"),
+ MakeBazelTarget("java_library", "java-lib-3", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-3"),
+ MakeBazelTarget("java_library", "java-lib-4", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-4"),
+ },
+ })
+}
+
+func TestJavaLibraryArchVariantSrcsWithExcludes(t *testing.T) {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Description: "java_library with arch variant libs",
+ Blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java", "b.java"],
+ target: {
+ android: {
+ exclude_srcs: ["a.java"],
+ },
+ },
+ bazel_module: { bp2build_available: true },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+ "srcs": `["b.java"] + select({
+ "//build/bazel/platforms/os:android": [],
+ "//conditions:default": ["a.java"],
+ })`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+ },
+ })
+}
diff --git a/bp2build/java_library_host_conversion_test.go b/bp2build/java_library_host_conversion_test.go
index 73abdd2..9e47b09 100644
--- a/bp2build/java_library_host_conversion_test.go
+++ b/bp2build/java_library_host_conversion_test.go
@@ -21,17 +21,17 @@
"android/soong/java"
)
-func runJavaLibraryHostTestCase(t *testing.T, tc bp2buildTestCase) {
+func runJavaLibraryHostTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "java_library_host"
- (&tc).moduleTypeUnderTestFactory = java.LibraryHostFactory
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+ (&tc).ModuleTypeUnderTest = "java_library_host"
+ (&tc).ModuleTypeUnderTestFactory = java.LibraryHostFactory
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
}
func TestJavaLibraryHost(t *testing.T) {
- runJavaLibraryHostTestCase(t, bp2buildTestCase{
- description: "java_library_host with srcs, exclude_srcs and libs",
- blueprint: `java_library_host {
+ runJavaLibraryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_library_host with srcs, exclude_srcs and libs",
+ Blueprint: `java_library_host {
name: "java-lib-host-1",
srcs: ["a.java", "b.java"],
exclude_srcs: ["b.java"],
@@ -43,23 +43,42 @@
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{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-host-1", AttrNameToString{
"srcs": `["a.java"]`,
- "deps": `[":java-lib-host-2"]`,
+ "deps": `[":java-lib-host-2-neverlink"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
"//conditions:default": [],
})`,
}),
- makeBazelTarget("java_library", "java-lib-host-2", attrNameToString{
- "srcs": `["c.java"]`,
+ MakeBazelTarget("java_library", "java-lib-host-1-neverlink", AttrNameToString{
+ "exports": `[":java-lib-host-1"]`,
+ "neverlink": `True`,
"target_compatible_with": `select({
"//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
"//conditions:default": [],
})`,
}),
+ MakeBazelTarget("java_library", "java-lib-host-2", AttrNameToString{
+ "java_version": `"9"`,
+ "srcs": `["c.java"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_library", "java-lib-host-2-neverlink", AttrNameToString{
+ "exports": `[":java-lib-host-2"]`,
+ "neverlink": `True`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ "java_version": `"9"`,
+ }),
},
})
}
diff --git a/bp2build/java_plugin_conversion_test.go b/bp2build/java_plugin_conversion_test.go
index c2a2182..f2b6f20 100644
--- a/bp2build/java_plugin_conversion_test.go
+++ b/bp2build/java_plugin_conversion_test.go
@@ -21,24 +21,25 @@
"android/soong/java"
)
-func runJavaPluginTestCase(t *testing.T, tc bp2buildTestCase) {
+func runJavaPluginTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "java_plugin"
- (&tc).moduleTypeUnderTestFactory = java.PluginFactory
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+ (&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 {
+ 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 {
@@ -52,29 +53,30 @@
srcs: ["c.java"],
bazel_module: { bp2build_available: false },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_plugin", "java-plug-1", attrNameToString{
+ 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-1-neverlink",
":java-lib-2",
]`,
"srcs": `[
"a.java",
"b.java",
]`,
+ "java_version": `"7"`,
}),
},
})
}
func TestJavaPluginNoSrcs(t *testing.T) {
- runJavaPluginTestCase(t, bp2buildTestCase{
- description: "java_plugin without srcs converts (static) libs to deps",
- blueprint: `java_plugin {
+ 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"],
@@ -92,14 +94,14 @@
srcs: ["c.java"],
bazel_module: { bp2build_available: false },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("java_plugin", "java-plug-1", attrNameToString{
+ 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-1-neverlink",
":java-lib-2",
]`,
}),
diff --git a/bp2build/java_proto_conversion_test.go b/bp2build/java_proto_conversion_test.go
index 67f8044..f546cf4 100644
--- a/bp2build/java_proto_conversion_test.go
+++ b/bp2build/java_proto_conversion_test.go
@@ -22,11 +22,11 @@
"android/soong/java"
)
-func runJavaProtoTestCase(t *testing.T, tc bp2buildTestCase) {
+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)
+ (&tc).ModuleTypeUnderTest = "java_library_static"
+ (&tc).ModuleTypeUnderTestFactory = java.LibraryFactory
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
}
func TestJavaProto(t *testing.T) {
@@ -70,53 +70,61 @@
srcs: ["a.proto"],
}`
- protoLibrary := makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{
+ 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{
+ runJavaProtoTestCase(t, Bp2buildTestCase{
+ Description: fmt.Sprintf("java_proto %s", tc.protoType),
+ Blueprint: fmt.Sprintf(bp, tc.protoType),
+ ExpectedBazelTargets: []string{
protoLibrary,
- makeBazelTarget(
+ MakeBazelTarget(
tc.javaLibraryType,
javaLibraryName,
- attrNameToString{
+ AttrNameToString{
"deps": `[":java-protos_proto"]`,
}),
- makeBazelTarget("java_library", "java-protos", attrNameToString{
+ MakeBazelTarget("java_library", "java-protos", AttrNameToString{
"exports": fmt.Sprintf(`[":%s"]`, javaLibraryName),
}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-protos"),
},
})
}
}
func TestJavaProtoDefault(t *testing.T) {
- runJavaProtoTestCase(t, bp2buildTestCase{
- description: "java_library proto default",
- blueprint: `java_library_static {
+ 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{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("proto_library", "java-protos_proto", AttrNameToString{
"srcs": `["a.proto"]`,
}),
- makeBazelTarget(
+ MakeBazelTarget(
"java_lite_proto_library",
"java-protos_java_proto_lite",
- attrNameToString{
- "deps": `[":java-protos_proto"]`,
+ AttrNameToString{
+ "deps": `[":java-protos_proto"]`,
+ "java_version": `"7"`,
}),
- makeBazelTarget("java_library", "java-protos", attrNameToString{
- "exports": `[":java-protos_java_proto_lite"]`,
+ MakeBazelTarget("java_library", "java-protos", AttrNameToString{
+ "exports": `[":java-protos_java_proto_lite"]`,
+ "java_version": `"7"`,
}),
+ MakeNeverlinkDuplicateTargetWithAttrs(
+ "java_library",
+ "java-protos",
+ AttrNameToString{"java_version": `"7"`}),
},
})
}
diff --git a/bp2build/java_sdk_library_conversion_test.go b/bp2build/java_sdk_library_conversion_test.go
new file mode 100644
index 0000000..9ce7446
--- /dev/null
+++ b/bp2build/java_sdk_library_conversion_test.go
@@ -0,0 +1,148 @@
+// Copyright 2023 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 runJavaSdkLibraryTestCaseWithRegistrationCtxFunc(t *testing.T, tc Bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) {
+ t.Helper()
+ (&tc).ModuleTypeUnderTest = "java_sdk_library"
+ (&tc).ModuleTypeUnderTestFactory = java.SdkLibraryFactory
+ RunBp2BuildTestCase(t, registrationCtxFunc, tc)
+}
+
+func runJavaSdkLibraryTestCase(t *testing.T, tc Bp2buildTestCase) {
+ t.Helper()
+ runJavaSdkLibraryTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) {})
+}
+
+func TestJavaSdkLibraryApiSurfaceGeneral(t *testing.T) {
+ runJavaSdkLibraryTestCase(t, Bp2buildTestCase{
+ Description: "limited java_sdk_library for api surfaces, general conversion",
+ Filesystem: map[string]string{
+ "build/soong/scripts/gen-java-current-api-files.sh": "",
+ "api/current.txt": "",
+ "api/system-current.txt": "",
+ "api/test-current.txt": "",
+ "api/module-lib-current.txt": "",
+ "api/system-server-current.txt": "",
+ "api/removed.txt": "",
+ "api/system-removed.txt": "",
+ "api/test-removed.txt": "",
+ "api/module-lib-removed.txt": "",
+ "api/system-server-removed.txt": "",
+ },
+ Blueprint: `java_sdk_library {
+ name: "java-sdk-lib",
+ srcs: ["a.java"],
+ public: {enabled: true},
+ system: {enabled: true},
+ test: {enabled: true},
+ module_lib: {enabled: true},
+ system_server: {enabled: true},
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_sdk_library", "java-sdk-lib", AttrNameToString{
+ "public": `"api/current.txt"`,
+ "system": `"api/system-current.txt"`,
+ "test": `"api/test-current.txt"`,
+ "module_lib": `"api/module-lib-current.txt"`,
+ "system_server": `"api/system-server-current.txt"`,
+ }),
+ },
+ })
+}
+
+func TestJavaSdkLibraryApiSurfacePublicDefault(t *testing.T) {
+ runJavaSdkLibraryTestCase(t, Bp2buildTestCase{
+ Description: "limited java_sdk_library for api surfaces, public prop uses default value",
+ Filesystem: map[string]string{
+ "build/soong/scripts/gen-java-current-api-files.sh": "",
+ "api/current.txt": "",
+ "api/system-current.txt": "",
+ "api/test-current.txt": "",
+ "api/module-lib-current.txt": "",
+ "api/system-server-current.txt": "",
+ "api/removed.txt": "",
+ "api/system-removed.txt": "",
+ "api/test-removed.txt": "",
+ "api/module-lib-removed.txt": "",
+ "api/system-server-removed.txt": "",
+ },
+ Blueprint: `java_sdk_library {
+ name: "java-sdk-lib",
+ srcs: ["a.java"],
+ system: {enabled: false},
+ test: {enabled: false},
+ module_lib: {enabled: false},
+ system_server: {enabled: false},
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_sdk_library", "java-sdk-lib", AttrNameToString{
+ "public": `"api/current.txt"`,
+ }),
+ },
+ })
+}
+
+func TestJavaSdkLibraryApiSurfacePublicNotEnabled(t *testing.T) {
+ runJavaSdkLibraryTestCase(t, Bp2buildTestCase{
+ Description: "limited java_sdk_library for api surfaces, public enable is false",
+ Filesystem: map[string]string{
+ "build/soong/scripts/gen-java-current-api-files.sh": "",
+ "api/current.txt": "",
+ "api/removed.txt": "",
+ },
+ Blueprint: `java_sdk_library {
+ name: "java-sdk-lib",
+ srcs: ["a.java"],
+ public: {enabled: false},
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_sdk_library", "java-sdk-lib", AttrNameToString{}),
+ },
+ })
+}
+
+func TestJavaSdkLibraryApiSurfaceNoScopeIsSet(t *testing.T) {
+ runJavaSdkLibraryTestCase(t, Bp2buildTestCase{
+ Description: "limited java_sdk_library for api surfaces, none of the api scopes is set",
+ Filesystem: map[string]string{
+ "build/soong/scripts/gen-java-current-api-files.sh": "",
+ "api/current.txt": "",
+ "api/system-current.txt": "",
+ "api/test-current.txt": "",
+ "api/removed.txt": "",
+ "api/system-removed.txt": "",
+ "api/test-removed.txt": "",
+ },
+ Blueprint: `java_sdk_library {
+ name: "java-sdk-lib",
+ srcs: ["a.java"],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_sdk_library", "java-sdk-lib", AttrNameToString{
+ "public": `"api/current.txt"`,
+ "system": `"api/system-current.txt"`,
+ "test": `"api/test-current.txt"`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/license_conversion_test.go b/bp2build/license_conversion_test.go
new file mode 100644
index 0000000..ea6b27a
--- /dev/null
+++ b/bp2build/license_conversion_test.go
@@ -0,0 +1,81 @@
+// 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 (
+ "android/soong/android"
+ "testing"
+)
+
+func registerLicenseModuleTypes(_ android.RegistrationContext) {}
+
+func TestLicenseBp2Build(t *testing.T) {
+ tests := []struct {
+ description string
+ module string
+ expected ExpectedRuleTarget
+ }{
+ {
+ description: "license kind and text notice",
+ module: `
+license {
+ name: "my_license",
+ license_kinds: [ "SPDX-license-identifier-Apache-2.0"],
+ license_text: [ "NOTICE"],
+}`,
+ expected: ExpectedRuleTarget{
+ "android_license",
+ "my_license",
+ AttrNameToString{
+ "license_kinds": `["SPDX-license-identifier-Apache-2.0"]`,
+ "license_text": `"NOTICE"`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ },
+ {
+ description: "visibility, package_name, copyright_notice",
+ module: `
+license {
+ name: "my_license",
+ package_name: "my_package",
+ visibility: [":__subpackages__"],
+ copyright_notice: "Copyright © 2022",
+}`,
+ expected: ExpectedRuleTarget{
+ "android_license",
+ "my_license",
+ AttrNameToString{
+ "copyright_notice": `"Copyright © 2022"`,
+ "package_name": `"my_package"`,
+ "visibility": `[":__subpackages__"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ },
+ }
+
+ for _, test := range tests {
+ RunBp2BuildTestCase(t,
+ registerLicenseModuleTypes,
+ Bp2buildTestCase{
+ Description: test.description,
+ ModuleTypeUnderTest: "license",
+ ModuleTypeUnderTestFactory: android.LicenseFactory,
+ Blueprint: test.module,
+ ExpectedBazelTargets: []string{test.expected.String()},
+ })
+ }
+}
diff --git a/bp2build/license_kind_conversion_test.go b/bp2build/license_kind_conversion_test.go
new file mode 100644
index 0000000..eda116c
--- /dev/null
+++ b/bp2build/license_kind_conversion_test.go
@@ -0,0 +1,69 @@
+// 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 (
+ "android/soong/android"
+ "testing"
+)
+
+func registerLicenseKindModuleTypes(_ android.RegistrationContext) {}
+
+func TestLicenseKindBp2Build(t *testing.T) {
+ tests := []struct {
+ description string
+ module string
+ expected ExpectedRuleTarget
+ }{
+ {
+ description: "license_kind",
+ module: `
+license_kind {
+ name: "my_license",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/0BSD",
+ visibility: ["//visibility:public"],
+}`,
+ expected: ExpectedRuleTarget{
+ "license_kind",
+ "my_license",
+ AttrNameToString{
+ "conditions": `[
+ "by_exception_only",
+ "not_allowed",
+ ]`,
+ "url": `"https://spdx.org/licenses/0BSD"`,
+ "visibility": `["//visibility:public"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ },
+ }
+
+ for _, test := range tests {
+ RunBp2BuildTestCase(t,
+ registerLicenseKindModuleTypes,
+ Bp2buildTestCase{
+ Description: test.description,
+ ModuleTypeUnderTest: "license_kind",
+ ModuleTypeUnderTestFactory: android.LicenseKindFactory,
+ Blueprint: test.module,
+ ExpectedBazelTargets: []string{test.expected.String()},
+ })
+ }
+}
diff --git a/bp2build/linker_config_conversion_test.go b/bp2build/linker_config_conversion_test.go
new file mode 100644
index 0000000..5e7bcd4
--- /dev/null
+++ b/bp2build/linker_config_conversion_test.go
@@ -0,0 +1,59 @@
+// 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/linkerconfig"
+)
+
+func runLinkerConfigTestCase(t *testing.T, tc Bp2buildTestCase) {
+ t.Helper()
+ (&tc).ModuleTypeUnderTest = "linker_config"
+ (&tc).ModuleTypeUnderTestFactory = linkerconfig.LinkerConfigFactory
+ RunBp2BuildTestCaseSimple(t, tc)
+}
+
+func TestLinkerConfigConvertsSrc(t *testing.T) {
+ runLinkerConfigTestCase(t,
+ Bp2buildTestCase{
+ Blueprint: `
+linker_config {
+ name: "foo",
+ src: "a.json",
+}
+`,
+ ExpectedBazelTargets: []string{MakeBazelTarget("linker_config", "foo", AttrNameToString{
+ "src": `"a.json"`,
+ })},
+ })
+
+}
+
+func TestLinkerConfigNoSrc(t *testing.T) {
+ runLinkerConfigTestCase(t,
+ Bp2buildTestCase{
+ Blueprint: `
+linker_config {
+ name: "foo",
+}
+`,
+ ExpectedBazelTargets: []string{},
+ ExpectedErr: fmt.Errorf("Android.bp:2:1: module \"foo\": src: empty src is not supported"),
+ })
+
+}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 04fac44..a020650 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -9,24 +9,16 @@
"android/soong/android"
"android/soong/shared"
"android/soong/ui/metrics/bp2build_metrics_proto"
+ "google.golang.org/protobuf/proto"
+
"github.com/google/blueprint"
)
-// Simple metrics struct to collect information about a Blueprint to BUILD
+// CodegenMetrics represents information about the Blueprint-to-BUILD
// conversion process.
+// Use CreateCodegenMetrics() to get a properly initialized instance
type CodegenMetrics struct {
- // Total number of Soong modules converted to generated targets
- generatedModuleCount uint64
-
- // Total number of Soong modules converted to handcrafted targets
- handCraftedModuleCount uint64
-
- // Total number of unconverted Soong modules
- unconvertedModuleCount uint64
-
- // Counts of generated Bazel targets per Bazel rule class
- ruleClassCount map[string]uint64
-
+ serialized *bp2build_metrics_proto.Bp2BuildMetrics
// List of modules with unconverted deps
// NOTE: NOT in the .proto
moduleWithUnconvertedDepsMsgs []string
@@ -35,37 +27,32 @@
// NOTE: NOT in the .proto
moduleWithMissingDepsMsgs []string
- // List of converted modules
- convertedModules []string
+ // Map of converted modules and paths to call
+ // NOTE: NOT in the .proto
+ convertedModulePathMap map[string]string
+}
- // Counts of converted modules by module type.
- convertedModuleTypeCount map[string]uint64
-
- // Counts of total modules by module type.
- totalModuleTypeCount map[string]uint64
-
- Events []*bp2build_metrics_proto.Event
+func CreateCodegenMetrics() CodegenMetrics {
+ return CodegenMetrics{
+ serialized: &bp2build_metrics_proto.Bp2BuildMetrics{
+ RuleClassCount: make(map[string]uint64),
+ ConvertedModuleTypeCount: make(map[string]uint64),
+ TotalModuleTypeCount: make(map[string]uint64),
+ },
+ convertedModulePathMap: make(map[string]string),
+ }
}
// Serialize returns the protoized version of CodegenMetrics: bp2build_metrics_proto.Bp2BuildMetrics
-func (metrics *CodegenMetrics) Serialize() bp2build_metrics_proto.Bp2BuildMetrics {
- return bp2build_metrics_proto.Bp2BuildMetrics{
- GeneratedModuleCount: metrics.generatedModuleCount,
- HandCraftedModuleCount: metrics.handCraftedModuleCount,
- UnconvertedModuleCount: metrics.unconvertedModuleCount,
- RuleClassCount: metrics.ruleClassCount,
- ConvertedModules: metrics.convertedModules,
- ConvertedModuleTypeCount: metrics.convertedModuleTypeCount,
- TotalModuleTypeCount: metrics.totalModuleTypeCount,
- Events: metrics.Events,
- }
+func (metrics *CodegenMetrics) Serialize() *bp2build_metrics_proto.Bp2BuildMetrics {
+ return metrics.serialized
}
// Print the codegen metrics to stdout.
func (metrics *CodegenMetrics) Print() {
generatedTargetCount := uint64(0)
- for _, ruleClass := range android.SortedStringKeys(metrics.ruleClassCount) {
- count := metrics.ruleClassCount[ruleClass]
+ for _, ruleClass := range android.SortedKeys(metrics.serialized.RuleClassCount) {
+ count := metrics.serialized.RuleClassCount[ruleClass]
fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count)
generatedTargetCount += count
}
@@ -76,9 +63,9 @@
%d converted modules have missing deps:
%s
`,
- metrics.generatedModuleCount,
+ metrics.serialized.GeneratedModuleCount,
generatedTargetCount,
- metrics.handCraftedModuleCount,
+ metrics.serialized.HandCraftedModuleCount,
metrics.TotalModuleCount(),
len(metrics.moduleWithUnconvertedDepsMsgs),
strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"),
@@ -115,31 +102,82 @@
fail(err, "Error outputting %s", metricsFile)
}
if _, err := os.Stat(metricsFile); err != nil {
- fail(err, "MISSING BP2BUILD METRICS OUTPUT: Failed to `stat` %s", metricsFile)
+ if os.IsNotExist(err) {
+ fail(err, "MISSING BP2BUILD METRICS OUTPUT: %s", metricsFile)
+ } else {
+ fail(err, "FAILED TO `stat` BP2BUILD METRICS OUTPUT: %s", metricsFile)
+ }
+ }
+}
+
+// ReadCodegenMetrics loads CodegenMetrics from `dir`
+// returns a nil pointer if the file doesn't exist
+func ReadCodegenMetrics(dir string) *CodegenMetrics {
+ metricsFile := filepath.Join(dir, bp2buildMetricsFilename)
+ if _, err := os.Stat(metricsFile); err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ } else {
+ fail(err, "FAILED TO `stat` BP2BUILD METRICS OUTPUT: %s", metricsFile)
+ panic("unreachable after fail")
+ }
+ }
+ if buf, err := os.ReadFile(metricsFile); err != nil {
+ fail(err, "FAILED TO READ BP2BUILD METRICS OUTPUT: %s", metricsFile)
+ panic("unreachable after fail")
} else {
- fmt.Printf("\nWrote bp2build metrics to: %s\n", metricsFile)
+ bp2BuildMetrics := bp2build_metrics_proto.Bp2BuildMetrics{
+ RuleClassCount: make(map[string]uint64),
+ ConvertedModuleTypeCount: make(map[string]uint64),
+ TotalModuleTypeCount: make(map[string]uint64),
+ }
+ if err := proto.Unmarshal(buf, &bp2BuildMetrics); err != nil {
+ fail(err, "FAILED TO PARSE BP2BUILD METRICS OUTPUT: %s", metricsFile)
+ }
+ return &CodegenMetrics{
+ serialized: &bp2BuildMetrics,
+ convertedModulePathMap: make(map[string]string),
+ }
}
}
func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) {
- metrics.ruleClassCount[ruleClass] += 1
+ metrics.serialized.RuleClassCount[ruleClass] += 1
+}
+
+func (metrics *CodegenMetrics) AddEvent(event *bp2build_metrics_proto.Event) {
+ metrics.serialized.Events = append(metrics.serialized.Events, event)
}
func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) {
- metrics.unconvertedModuleCount += 1
- metrics.totalModuleTypeCount[moduleType] += 1
+ metrics.serialized.UnconvertedModuleCount += 1
+ metrics.serialized.TotalModuleTypeCount[moduleType] += 1
+}
+
+func (metrics *CodegenMetrics) SetSymlinkCount(n uint64) {
+ if m := metrics.serialized.WorkspaceSymlinkCount; m != 0 {
+ fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceSymlinkCount of %d", m)
+ }
+ metrics.serialized.WorkspaceSymlinkCount = n
+}
+
+func (metrics *CodegenMetrics) SetMkDirCount(n uint64) {
+ if m := metrics.serialized.WorkspaceMkDirCount; m != 0 {
+ fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceDirCount of %d", m)
+ }
+ metrics.serialized.WorkspaceMkDirCount = n
}
func (metrics *CodegenMetrics) TotalModuleCount() uint64 {
- return metrics.handCraftedModuleCount +
- metrics.generatedModuleCount +
- metrics.unconvertedModuleCount
+ return metrics.serialized.HandCraftedModuleCount +
+ metrics.serialized.GeneratedModuleCount +
+ metrics.serialized.UnconvertedModuleCount
}
// Dump serializes the metrics to the given filename
func (metrics *CodegenMetrics) dump(filename string) (err error) {
ser := metrics.Serialize()
- return shared.Save(&ser, filename)
+ return shared.Save(ser, filename)
}
type ConversionType int
@@ -149,16 +187,21 @@
Handcrafted
)
-func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, conversionType ConversionType) {
+func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string, conversionType ConversionType) {
+ //a package module has empty name
+ if moduleType == "package" {
+ return
+ }
// Undo prebuilt_ module name prefix modifications
moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
- metrics.convertedModules = append(metrics.convertedModules, moduleName)
- metrics.convertedModuleTypeCount[moduleType] += 1
- metrics.totalModuleTypeCount[moduleType] += 1
+ metrics.serialized.ConvertedModules = append(metrics.serialized.ConvertedModules, moduleName)
+ metrics.convertedModulePathMap[moduleName] = "//" + dir
+ metrics.serialized.ConvertedModuleTypeCount[moduleType] += 1
+ metrics.serialized.TotalModuleTypeCount[moduleType] += 1
if conversionType == Handcrafted {
- metrics.handCraftedModuleCount += 1
+ metrics.serialized.HandCraftedModuleCount += 1
} else if conversionType == Generated {
- metrics.generatedModuleCount += 1
+ metrics.serialized.GeneratedModuleCount += 1
}
}
diff --git a/bp2build/ndk_headers_conversion_test.go b/bp2build/ndk_headers_conversion_test.go
new file mode 100644
index 0000000..9d0f1f2
--- /dev/null
+++ b/bp2build/ndk_headers_conversion_test.go
@@ -0,0 +1,164 @@
+// 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 TestNdkHeaderFilepaths(t *testing.T) {
+ bpTemplate := `
+ ndk_headers {
+ name: "foo",
+ srcs: %v,
+ exclude_srcs: %v,
+ }
+ `
+ testCases := []struct {
+ desc string
+ srcs string
+ excludeSrcs string
+ expectedHdrs string
+ }{
+ {
+ desc: "Single header file",
+ srcs: `["foo.h"]`,
+ excludeSrcs: `[]`,
+ expectedHdrs: `["foo.h"]`,
+ },
+ {
+ desc: "Multiple header files",
+ srcs: `["foo.h", "foo_other.h"]`,
+ excludeSrcs: `[]`,
+ expectedHdrs: `[
+ "foo.h",
+ "foo_other.h",
+ ]`,
+ },
+ {
+ desc: "Multiple header files with excludes",
+ srcs: `["foo.h", "foo_other.h"]`,
+ excludeSrcs: `["foo_other.h"]`,
+ expectedHdrs: `["foo.h"]`,
+ },
+ {
+ desc: "Multiple header files via Soong-supported globs",
+ srcs: `["*.h"]`,
+ excludeSrcs: `[]`,
+ expectedHdrs: `[
+ "foo.h",
+ "foo_other.h",
+ ]`,
+ },
+ }
+ for _, testCase := range testCases {
+ fs := map[string]string{
+ "foo.h": "",
+ "foo_other.h": "",
+ }
+ expectedApiContributionTargetName := "foo.contribution"
+ expectedBazelTarget := MakeBazelTargetNoRestrictions(
+ "cc_api_headers",
+ expectedApiContributionTargetName,
+ AttrNameToString{
+ "hdrs": testCase.expectedHdrs,
+ },
+ )
+ RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+ Description: testCase.desc,
+ Blueprint: fmt.Sprintf(bpTemplate, testCase.srcs, testCase.excludeSrcs),
+ ExpectedBazelTargets: []string{expectedBazelTarget},
+ Filesystem: fs,
+ })
+ }
+}
+
+func TestNdkHeaderIncludeDir(t *testing.T) {
+ bpTemplate := `
+ ndk_headers {
+ name: "foo",
+ from: %v,
+ to: "this/value/is/ignored",
+ }
+ `
+ testCases := []struct {
+ desc string
+ from string
+ expectedIncludeDir string
+ }{
+ {
+ desc: "Empty `from` value",
+ from: `""`,
+ expectedIncludeDir: `""`,
+ },
+ {
+ desc: "Non-Empty `from` value",
+ from: `"include"`,
+ expectedIncludeDir: `"include"`,
+ },
+ }
+ for _, testCase := range testCases {
+ expectedApiContributionTargetName := "foo.contribution"
+ expectedBazelTarget := MakeBazelTargetNoRestrictions(
+ "cc_api_headers",
+ expectedApiContributionTargetName,
+ AttrNameToString{
+ "include_dir": testCase.expectedIncludeDir,
+ },
+ )
+ RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+ Description: testCase.desc,
+ Blueprint: fmt.Sprintf(bpTemplate, testCase.from),
+ ExpectedBazelTargets: []string{expectedBazelTarget},
+ })
+ }
+}
+
+func TestVersionedNdkHeaderFilepaths(t *testing.T) {
+ bp := `
+ versioned_ndk_headers {
+ name: "common_libc",
+ from: "include"
+ }
+ `
+ fs := map[string]string{
+ "include/math.h": "",
+ "include/stdio.h": "",
+ "include/arm/arm.h": "",
+ "include/x86/x86.h": "",
+ }
+ expectedApiContributionTargetName := "common_libc.contribution"
+ expectedBazelTarget := MakeBazelTargetNoRestrictions(
+ "cc_api_headers",
+ expectedApiContributionTargetName,
+ AttrNameToString{
+ "include_dir": `"include"`,
+ "hdrs": `[
+ "include/math.h",
+ "include/stdio.h",
+ "include/arm/arm.h",
+ "include/x86/x86.h",
+ ]`,
+ },
+ )
+ RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+ Blueprint: bp,
+ Filesystem: fs,
+ ExpectedBazelTargets: []string{expectedBazelTarget},
+ })
+}
diff --git a/bp2build/ndk_library_conversion_test.go b/bp2build/ndk_library_conversion_test.go
new file mode 100644
index 0000000..819ab25
--- /dev/null
+++ b/bp2build/ndk_library_conversion_test.go
@@ -0,0 +1,77 @@
+// 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 (
+ "testing"
+
+ "android/soong/cc"
+)
+
+func TestNdkLibraryContributionSymbolFile(t *testing.T) {
+ bp := `
+ ndk_library {
+ name: "libfoo",
+ symbol_file: "libfoo.map.txt",
+ }
+ `
+ expectedBazelTarget := MakeBazelTargetNoRestrictions(
+ "cc_api_contribution",
+ "libfoo.ndk.contribution",
+ AttrNameToString{
+ "api": `"libfoo.map.txt"`,
+ "api_surfaces": `["publicapi"]`,
+ "library_name": `"libfoo"`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ },
+ )
+ RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{expectedBazelTarget},
+ })
+}
+
+func TestNdkLibraryContributionHeaders(t *testing.T) {
+ bp := `
+ ndk_library {
+ name: "libfoo",
+ symbol_file: "libfoo.map.txt",
+ export_header_libs: ["libfoo_headers"],
+ }
+ `
+ fs := map[string]string{
+ "header_directory/Android.bp": `
+ ndk_headers {
+ name: "libfoo_headers",
+ }
+ `,
+ }
+ expectedBazelTarget := MakeBazelTargetNoRestrictions(
+ "cc_api_contribution",
+ "libfoo.ndk.contribution",
+ AttrNameToString{
+ "api": `"libfoo.map.txt"`,
+ "api_surfaces": `["publicapi"]`,
+ "library_name": `"libfoo"`,
+ "hdrs": `["//header_directory:libfoo_headers.contribution"]`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ },
+ )
+ RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+ Blueprint: bp,
+ Filesystem: fs,
+ ExpectedBazelTargets: []string{expectedBazelTarget},
+ })
+}
diff --git a/bp2build/package_conversion_test.go b/bp2build/package_conversion_test.go
new file mode 100644
index 0000000..ce848e4
--- /dev/null
+++ b/bp2build/package_conversion_test.go
@@ -0,0 +1,139 @@
+// 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 (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/genrule"
+)
+
+func registerDependentModules(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("license", android.LicenseFactory)
+ ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+}
+
+func TestPackage(t *testing.T) {
+ tests := []struct {
+ description string
+ modules string
+ fs map[string]string
+ expected []ExpectedRuleTarget
+ }{
+ {
+ description: "with default applicable licenses",
+ modules: `
+license {
+ name: "my_license",
+ visibility: [":__subpackages__"],
+ license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+ license_text: ["NOTICE"],
+}
+
+package {
+ default_applicable_licenses: ["my_license"],
+}
+`,
+ expected: []ExpectedRuleTarget{
+ {
+ "package",
+ "",
+ AttrNameToString{
+ "default_package_metadata": `[":my_license"]`,
+ "default_visibility": `["//visibility:public"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ {
+ "android_license",
+ "my_license",
+ AttrNameToString{
+ "license_kinds": `["SPDX-license-identifier-Apache-2.0"]`,
+ "license_text": `"NOTICE"`,
+ "visibility": `[":__subpackages__"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ },
+ },
+ {
+ description: "package has METADATA file",
+ fs: map[string]string{
+ "METADATA": ``,
+ },
+ modules: `
+license {
+ name: "my_license",
+ visibility: [":__subpackages__"],
+ license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+ license_text: ["NOTICE"],
+}
+
+package {
+ default_applicable_licenses: ["my_license"],
+}
+`,
+ expected: []ExpectedRuleTarget{
+ {
+ "package",
+ "",
+ AttrNameToString{
+ "default_package_metadata": `[
+ ":my_license",
+ ":default_metadata_file",
+ ]`,
+ "default_visibility": `["//visibility:public"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ {
+ "android_license",
+ "my_license",
+ AttrNameToString{
+ "license_kinds": `["SPDX-license-identifier-Apache-2.0"]`,
+ "license_text": `"NOTICE"`,
+ "visibility": `[":__subpackages__"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ {
+ "filegroup",
+ "default_metadata_file",
+ AttrNameToString{
+ "applicable_licenses": `[]`,
+ "srcs": `["METADATA"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ },
+ },
+ }
+ for _, test := range tests {
+ expected := make([]string, 0, len(test.expected))
+ for _, e := range test.expected {
+ expected = append(expected, e.String())
+ }
+ RunBp2BuildTestCase(t, registerDependentModules,
+ Bp2buildTestCase{
+ Description: test.description,
+ ModuleTypeUnderTest: "package",
+ ModuleTypeUnderTestFactory: android.PackageFactory,
+ Blueprint: test.modules,
+ ExpectedBazelTargets: expected,
+ Filesystem: test.fs,
+ })
+ }
+}
diff --git a/bp2build/performance_test.go b/bp2build/performance_test.go
index c4bbae2..5f80b83 100644
--- a/bp2build/performance_test.go
+++ b/bp2build/performance_test.go
@@ -22,11 +22,12 @@
// run for longer, set -benchtime to a larger value.
import (
- "android/soong/android"
"fmt"
"math"
"strings"
"testing"
+
+ "android/soong/android"
)
const (
@@ -105,7 +106,7 @@
ctx := android.NewTestContext(config)
registerCustomModuleForBp2buildConversion(ctx)
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
return testConfig{
config,
ctx,
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
index 2e4b221..aa0a5b7 100644
--- a/bp2build/prebuilt_etc_conversion_test.go
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -15,27 +15,28 @@
package bp2build
import (
+ "fmt"
+ "testing"
+
"android/soong/android"
"android/soong/etc"
-
- "testing"
)
-func runPrebuiltEtcTestCase(t *testing.T, tc bp2buildTestCase) {
+func runPrebuiltEtcTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "prebuilt_etc"
- (&tc).moduleTypeUnderTestFactory = etc.PrebuiltEtcFactory
- runBp2BuildTestCase(t, registerPrebuiltEtcModuleTypes, tc)
+ (&tc).ModuleTypeUnderTest = "prebuilt_etc"
+ (&tc).ModuleTypeUnderTestFactory = etc.PrebuiltEtcFactory
+ RunBp2BuildTestCase(t, registerPrebuiltEtcModuleTypes, tc)
}
func registerPrebuiltEtcModuleTypes(ctx android.RegistrationContext) {
}
func TestPrebuiltEtcSimple(t *testing.T) {
- runPrebuiltEtcTestCase(t, bp2buildTestCase{
- description: "prebuilt_etc - simple example",
- filesystem: map[string]string{},
- blueprint: `
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt_etc - simple example",
+ Filesystem: map[string]string{},
+ Blueprint: `
prebuilt_etc {
name: "apex_tz_version",
src: "version/tz_version",
@@ -44,8 +45,8 @@
installable: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
"filename": `"tz_version"`,
"installable": `False`,
"src": `"version/tz_version"`,
@@ -54,10 +55,10 @@
}
func TestPrebuiltEtcArchVariant(t *testing.T) {
- runPrebuiltEtcTestCase(t, bp2buildTestCase{
- description: "prebuilt_etc - arch variant",
- filesystem: map[string]string{},
- blueprint: `
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt_etc - arch variant",
+ Filesystem: map[string]string{},
+ Blueprint: `
prebuilt_etc {
name: "apex_tz_version",
src: "version/tz_version",
@@ -74,8 +75,8 @@
}
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
"filename": `"tz_version"`,
"installable": `False`,
"src": `select({
@@ -88,10 +89,10 @@
}
func TestPrebuiltEtcArchAndTargetVariant(t *testing.T) {
- runPrebuiltEtcTestCase(t, bp2buildTestCase{
- description: "prebuilt_etc - arch variant",
- filesystem: map[string]string{},
- blueprint: `
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt_etc - arch variant",
+ Filesystem: map[string]string{},
+ Blueprint: `
prebuilt_etc {
name: "apex_tz_version",
src: "version/tz_version",
@@ -113,8 +114,8 @@
},
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
"filename": `"tz_version"`,
"installable": `False`,
"src": `select({
@@ -128,22 +129,48 @@
"dir": `"etc/tz"`,
})}})
}
+func TestPrebuiltEtcProductVariables(t *testing.T) {
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt etc - product variables",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc {
+ name: "apex_tz_version",
+ src: "version/tz_version",
+ filename: "tz_version",
+ product_variables: {
+ native_coverage: {
+ src: "src1",
+ },
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
+ "filename": `"tz_version"`,
+ "src": `select({
+ "//build/bazel/product_variables:native_coverage": "src1",
+ "//conditions:default": "version/tz_version",
+ })`,
+ "dir": `"etc"`,
+ })}})
+}
-func runPrebuiltUsrShareTestCase(t *testing.T, tc bp2buildTestCase) {
+func runPrebuiltUsrShareTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- (&tc).moduleTypeUnderTest = "prebuilt_usr_share"
- (&tc).moduleTypeUnderTestFactory = etc.PrebuiltUserShareFactory
- runBp2BuildTestCase(t, registerPrebuiltEtcModuleTypes, tc)
+ (&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: `
+ 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",
@@ -152,8 +179,8 @@
installable: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
"filename": `"tz_version"`,
"installable": `False`,
"src": `"version/tz_version"`,
@@ -162,10 +189,10 @@
}
func TestPrebuiltEtcNoSubdir(t *testing.T) {
- runPrebuiltEtcTestCase(t, bp2buildTestCase{
- description: "prebuilt_etc - no subdir",
- filesystem: map[string]string{},
- blueprint: `
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt_etc - no subdir",
+ Filesystem: map[string]string{},
+ Blueprint: `
prebuilt_etc {
name: "apex_tz_version",
src: "version/tz_version",
@@ -173,11 +200,149 @@
installable: false,
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("prebuilt_file", "apex_tz_version", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
"filename": `"tz_version"`,
"installable": `False`,
"src": `"version/tz_version"`,
"dir": `"etc"`,
})}})
}
+
+func TestFilenameAsProperty(t *testing.T) {
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt_etc - filename is specified as a property ",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc {
+ name: "foo",
+ src: "fooSrc",
+ filename: "fooFileName",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+ "filename": `"fooFileName"`,
+ "src": `"fooSrc"`,
+ "dir": `"etc"`,
+ })}})
+}
+
+func TestFileNameFromSrc(t *testing.T) {
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt_etc - filename_from_src is true ",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc {
+ name: "foo",
+ filename_from_src: true,
+ src: "fooSrc",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+ "filename": `"fooSrc"`,
+ "src": `"fooSrc"`,
+ "dir": `"etc"`,
+ })}})
+}
+
+func TestFileNameFromSrcMultipleSrcs(t *testing.T) {
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt_etc - filename_from_src is true but there are multiple srcs",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc {
+ name: "foo",
+ filename_from_src: true,
+ arch: {
+ arm: {
+ src: "barSrc",
+ },
+ arm64: {
+ src: "bazSrc",
+ },
+ }
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+ "filename_from_src": `True`,
+ "dir": `"etc"`,
+ "src": `select({
+ "//build/bazel/platforms/arch:arm": "barSrc",
+ "//build/bazel/platforms/arch:arm64": "bazSrc",
+ "//conditions:default": None,
+ })`,
+ })}})
+}
+
+func TestFilenameFromModuleName(t *testing.T) {
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt_etc - neither filename nor filename_from_src are specified ",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc {
+ name: "foo",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+ "filename": `"foo"`,
+ "dir": `"etc"`,
+ })}})
+}
+
+func TestPrebuiltEtcProductVariableArchSrcs(t *testing.T) {
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "prebuilt etc- SRcs from arch variant product variables",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc {
+ name: "foo",
+ filename: "fooFilename",
+ arch: {
+ arm: {
+ src: "armSrc",
+ product_variables: {
+ native_coverage: {
+ src: "nativeCoverageArmSrc",
+ },
+ },
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+ "filename": `"fooFilename"`,
+ "dir": `"etc"`,
+ "src": `select({
+ "//build/bazel/platforms/arch:arm": "armSrc",
+ "//build/bazel/product_variables:native_coverage-arm": "nativeCoverageArmSrc",
+ "//conditions:default": None,
+ })`,
+ })}})
+}
+
+func TestPrebuiltEtcProductVariableError(t *testing.T) {
+ runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+ Description: "",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc {
+ name: "foo",
+ filename: "fooFilename",
+ arch: {
+ arm: {
+ src: "armSrc",
+ },
+ },
+ product_variables: {
+ native_coverage: {
+ src: "nativeCoverageArmSrc",
+ },
+ },
+}`,
+ ExpectedErr: fmt.Errorf("label attribute could not be collapsed"),
+ })
+}
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index dfa11d1..1b538d0 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -4,30 +4,33 @@
"testing"
"android/soong/android"
+ "android/soong/genrule"
"android/soong/python"
)
-func runBp2BuildTestCaseWithPythonLibraries(t *testing.T, tc bp2buildTestCase) {
+func runBp2BuildTestCaseWithPythonLibraries(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
ctx.RegisterModuleType("python_library_host", python.PythonLibraryHostFactory)
+ ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+ ctx.RegisterModuleType("python_defaults", python.DefaultsFactory)
}, tc)
}
func TestPythonBinaryHostSimple(t *testing.T) {
- runBp2BuildTestCaseWithPythonLibraries(t, bp2buildTestCase{
- description: "simple python_binary_host converts to a native py_binary",
- moduleTypeUnderTest: "python_binary_host",
- moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
- filesystem: map[string]string{
+ runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+ Description: "simple python_binary_host converts to a native py_binary",
+ ModuleTypeUnderTest: "python_binary_host",
+ ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ Filesystem: map[string]string{
"a.py": "",
"b/c.py": "",
"b/d.py": "",
"b/e.py": "",
"files/data.txt": "",
},
- blueprint: `python_binary_host {
+ Blueprint: `python_binary_host {
name: "foo",
main: "a.py",
srcs: ["**/*.py"],
@@ -41,11 +44,12 @@
srcs: ["b/e.py"],
bazel_module: { bp2build_available: false },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("py_binary", "foo", attrNameToString{
- "data": `["files/data.txt"]`,
- "deps": `[":bar"]`,
- "main": `"a.py"`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_binary", "foo", AttrNameToString{
+ "data": `["files/data.txt"]`,
+ "deps": `[":bar"]`,
+ "main": `"a.py"`,
+ "imports": `["."]`,
"srcs": `[
"a.py",
"b/c.py",
@@ -61,11 +65,11 @@
}
func TestPythonBinaryHostPy2(t *testing.T) {
- runBp2BuildTestCaseSimple(t, bp2buildTestCase{
- description: "py2 python_binary_host",
- moduleTypeUnderTest: "python_binary_host",
- moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
- blueprint: `python_binary_host {
+ RunBp2BuildTestCaseSimple(t, Bp2buildTestCase{
+ Description: "py2 python_binary_host",
+ ModuleTypeUnderTest: "python_binary_host",
+ ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ Blueprint: `python_binary_host {
name: "foo",
srcs: ["a.py"],
version: {
@@ -80,9 +84,10 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{
- makeBazelTarget("py_binary", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_binary", "foo", AttrNameToString{
"python_version": `"PY2"`,
+ "imports": `["."]`,
"srcs": `["a.py"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
@@ -94,11 +99,11 @@
}
func TestPythonBinaryHostPy3(t *testing.T) {
- runBp2BuildTestCaseSimple(t, bp2buildTestCase{
- description: "py3 python_binary_host",
- moduleTypeUnderTest: "python_binary_host",
- moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
- blueprint: `python_binary_host {
+ RunBp2BuildTestCaseSimple(t, Bp2buildTestCase{
+ Description: "py3 python_binary_host",
+ ModuleTypeUnderTest: "python_binary_host",
+ ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ Blueprint: `python_binary_host {
name: "foo",
srcs: ["a.py"],
version: {
@@ -113,10 +118,11 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{
+ ExpectedBazelTargets: []string{
// python_version is PY3 by default.
- makeBazelTarget("py_binary", "foo", attrNameToString{
- "srcs": `["a.py"]`,
+ MakeBazelTarget("py_binary", "foo", AttrNameToString{
+ "imports": `["."]`,
+ "srcs": `["a.py"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
"//conditions:default": [],
@@ -127,15 +133,15 @@
}
func TestPythonBinaryHostArchVariance(t *testing.T) {
- runBp2BuildTestCaseSimple(t, bp2buildTestCase{
- description: "test arch variants",
- moduleTypeUnderTest: "python_binary_host",
- moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
- filesystem: map[string]string{
+ RunBp2BuildTestCaseSimple(t, Bp2buildTestCase{
+ Description: "test arch variants",
+ ModuleTypeUnderTest: "python_binary_host",
+ ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ Filesystem: map[string]string{
"dir/arm.py": "",
"dir/x86.py": "",
},
- blueprint: `python_binary_host {
+ Blueprint: `python_binary_host {
name: "foo-arm",
arch: {
arm: {
@@ -146,8 +152,9 @@
},
},
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("py_binary", "foo-arm", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_binary", "foo-arm", AttrNameToString{
+ "imports": `["."]`,
"srcs": `select({
"//build/bazel/platforms/arch:arm": ["arm.py"],
"//build/bazel/platforms/arch:x86": ["x86.py"],
@@ -161,3 +168,152 @@
},
})
}
+
+func TestPythonBinaryMainIsNotSpecified(t *testing.T) {
+ runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+ Description: "python_binary_host main label in same package",
+ ModuleTypeUnderTest: "python_binary_host",
+ ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ Blueprint: `python_binary_host {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_binary", "foo", AttrNameToString{
+ "imports": `["."]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPythonBinaryMainIsLabel(t *testing.T) {
+ runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+ Description: "python_binary_host main label in same package",
+ ModuleTypeUnderTest: "python_binary_host",
+ ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ Blueprint: `python_binary_host {
+ name: "foo",
+ main: ":a",
+ bazel_module: { bp2build_available: true },
+}
+
+genrule {
+ name: "a",
+ bazel_module: { bp2build_available: false },
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_binary", "foo", AttrNameToString{
+ "main": `":a"`,
+ "imports": `["."]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPythonBinaryMainIsSubpackageFile(t *testing.T) {
+ runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+ Description: "python_binary_host main is subpackage file",
+ ModuleTypeUnderTest: "python_binary_host",
+ ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ Filesystem: map[string]string{
+ "a/Android.bp": "",
+ "a/b.py": "",
+ },
+ Blueprint: `python_binary_host {
+ name: "foo",
+ main: "a/b.py",
+ bazel_module: { bp2build_available: true },
+}
+
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_binary", "foo", AttrNameToString{
+ "main": `"//a:b.py"`,
+ "imports": `["."]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPythonBinaryMainIsSubDirFile(t *testing.T) {
+ runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+ Description: "python_binary_host main is file in sub directory that is not Bazel package",
+ ModuleTypeUnderTest: "python_binary_host",
+ ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ Filesystem: map[string]string{
+ "a/b.py": "",
+ },
+ Blueprint: `python_binary_host {
+ name: "foo",
+ main: "a/b.py",
+ bazel_module: { bp2build_available: true },
+}
+
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_binary", "foo", AttrNameToString{
+ "main": `"a/b.py"`,
+ "imports": `["."]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestPythonBinaryDuplicatesInRequired(t *testing.T) {
+ runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+ Description: "python_binary_host duplicates in required attribute of the module and its defaults",
+ ModuleTypeUnderTest: "python_binary_host",
+ ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ Blueprint: `python_binary_host {
+ name: "foo",
+ main: "a.py",
+ defaults: ["d"],
+ required: [
+ "r1",
+ ],
+ bazel_module: { bp2build_available: true },
+}
+
+python_defaults {
+ name: "d",
+ required: [
+ "r1",
+ "r2",
+ ],
+}` + simpleModuleDoNotConvertBp2build("genrule", "r1") +
+ simpleModuleDoNotConvertBp2build("genrule", "r2"),
+
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_binary", "foo", AttrNameToString{
+ "main": `"a.py"`,
+ "imports": `["."]`,
+ "data": `[
+ ":r1",
+ ":r2",
+ ]`,
+ "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 356d52e..a53371d 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"
@@ -16,9 +17,11 @@
filesystem map[string]string
blueprint string
expectedBazelTargets []testBazelTarget
+ dir string
+ expectedError error
}
-func convertPythonLibTestCaseToBp2build_Host(tc pythonLibBp2BuildTestCase) bp2buildTestCase {
+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"],
@@ -29,38 +32,56 @@
return convertPythonLibTestCaseToBp2build(tc)
}
-func convertPythonLibTestCaseToBp2build(tc pythonLibBp2BuildTestCase) bp2buildTestCase {
+func convertPythonLibTestCaseToBp2build(tc pythonLibBp2BuildTestCase) Bp2buildTestCase {
var bp2BuildTargets []string
for _, t := range tc.expectedBazelTargets {
- bp2BuildTargets = append(bp2BuildTargets, makeBazelTarget(t.typ, t.name, t.attrs))
+ bp2BuildTargets = append(bp2BuildTargets, MakeBazelTarget(t.typ, t.name, t.attrs))
}
- return bp2buildTestCase{
- description: tc.description,
- filesystem: tc.filesystem,
- blueprint: tc.blueprint,
- expectedBazelTargets: bp2BuildTargets,
+ // 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 := convertPythonLibTestCaseToBp2build(tc)
- testCase.description = fmt.Sprintf(testCase.description, "python_library")
- testCase.blueprint = fmt.Sprintf(testCase.blueprint, "python_library")
- testCase.moduleTypeUnderTest = "python_library"
- testCase.moduleTypeUnderTestFactory = python.PythonLibraryFactory
+ 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)
+ RunBp2BuildTestCaseSimple(t, testCase)
}
func runPythonLibraryHostTestCase(t *testing.T, tc pythonLibBp2BuildTestCase) {
t.Helper()
testCase := convertPythonLibTestCaseToBp2build_Host(tc)
- testCase.description = fmt.Sprintf(testCase.description, "python_library_host")
- testCase.blueprint = fmt.Sprintf(testCase.blueprint, "python_library_host")
- testCase.moduleTypeUnderTest = "python_library_host"
- testCase.moduleTypeUnderTestFactory = python.PythonLibraryHostFactory
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+ 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) {
ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
},
testCase)
@@ -100,7 +121,7 @@
{
typ: "py_library",
name: "foo",
- attrs: attrNameToString{
+ attrs: AttrNameToString{
"data": `["files/data.txt"]`,
"deps": `[":bar"]`,
"srcs": `[
@@ -109,6 +130,7 @@
"b/d.py",
]`,
"srcs_version": `"PY3"`,
+ "imports": `["."]`,
},
},
},
@@ -133,9 +155,10 @@
{
typ: "py_library",
name: "foo",
- attrs: attrNameToString{
+ attrs: AttrNameToString{
"srcs": `["a.py"]`,
"srcs_version": `"PY2"`,
+ "imports": `["."]`,
},
},
},
@@ -160,9 +183,10 @@
{
typ: "py_library",
name: "foo",
- attrs: attrNameToString{
+ attrs: AttrNameToString{
"srcs": `["a.py"]`,
"srcs_version": `"PY3"`,
+ "imports": `["."]`,
},
},
},
@@ -188,12 +212,55 @@
// srcs_version is PY2ANDPY3 by default.
typ: "py_library",
name: "foo",
- attrs: attrNameToString{
- "srcs": `["a.py"]`,
+ 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 {
@@ -225,13 +292,57 @@
{
typ: "py_library",
name: "foo",
- attrs: attrNameToString{
+ attrs: AttrNameToString{
"srcs": `select({
"//build/bazel/platforms/arch:arm": ["arm.py"],
"//build/bazel/platforms/arch:x86": ["x86.py"],
"//conditions:default": [],
})`,
"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/python_test_conversion_test.go b/bp2build/python_test_conversion_test.go
new file mode 100644
index 0000000..4ff1fa1
--- /dev/null
+++ b/bp2build/python_test_conversion_test.go
@@ -0,0 +1,66 @@
+// Copyright 2023 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/python"
+ "testing"
+)
+
+func TestPythonTestHostSimple(t *testing.T) {
+ runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+ Description: "simple python_test_host converts to a native py_test",
+ ModuleTypeUnderTest: "python_test_host",
+ ModuleTypeUnderTestFactory: python.PythonTestHostFactory,
+ Filesystem: map[string]string{
+ "a.py": "",
+ "b/c.py": "",
+ "b/d.py": "",
+ "b/e.py": "",
+ "files/data.txt": "",
+ },
+ Blueprint: `python_test_host {
+ name: "foo",
+ main: "a.py",
+ srcs: ["**/*.py"],
+ exclude_srcs: ["b/e.py"],
+ data: ["files/data.txt",],
+ libs: ["bar"],
+ bazel_module: { bp2build_available: true },
+}
+ python_library_host {
+ name: "bar",
+ srcs: ["b/e.py"],
+ bazel_module: { bp2build_available: false },
+ }`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("py_test", "foo", AttrNameToString{
+ "data": `["files/data.txt"]`,
+ "deps": `[":bar"]`,
+ "main": `"a.py"`,
+ "imports": `["."]`,
+ "srcs": `[
+ "a.py",
+ "b/c.py",
+ "b/d.py",
+ ]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go
index ac89087..92b3a65 100644
--- a/bp2build/sh_conversion_test.go
+++ b/bp2build/sh_conversion_test.go
@@ -48,25 +48,25 @@
}
}
-func runShBinaryTestCase(t *testing.T, tc bp2buildTestCase) {
+func runShBinaryTestCase(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
}
func TestShBinarySimple(t *testing.T) {
- runShBinaryTestCase(t, bp2buildTestCase{
- description: "sh_binary test",
- moduleTypeUnderTest: "sh_binary",
- moduleTypeUnderTestFactory: sh.ShBinaryFactory,
- blueprint: `sh_binary {
+ runShBinaryTestCase(t, Bp2buildTestCase{
+ Description: "sh_binary test",
+ ModuleTypeUnderTest: "sh_binary",
+ ModuleTypeUnderTestFactory: sh.ShBinaryFactory,
+ Blueprint: `sh_binary {
name: "foo",
src: "foo.sh",
filename: "foo.exe",
sub_dir: "sub",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("sh_binary", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("sh_binary", "foo", AttrNameToString{
"srcs": `["foo.sh"]`,
"filename": `"foo.exe"`,
"sub_dir": `"sub"`,
@@ -75,17 +75,17 @@
}
func TestShBinaryDefaults(t *testing.T) {
- runShBinaryTestCase(t, bp2buildTestCase{
- description: "sh_binary test",
- moduleTypeUnderTest: "sh_binary",
- moduleTypeUnderTestFactory: sh.ShBinaryFactory,
- blueprint: `sh_binary {
+ runShBinaryTestCase(t, Bp2buildTestCase{
+ Description: "sh_binary test",
+ ModuleTypeUnderTest: "sh_binary",
+ ModuleTypeUnderTestFactory: sh.ShBinaryFactory,
+ Blueprint: `sh_binary {
name: "foo",
src: "foo.sh",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{
- makeBazelTarget("sh_binary", "foo", attrNameToString{
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("sh_binary", "foo", AttrNameToString{
"srcs": `["foo.sh"]`,
})},
})
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index b1e1fb2..ba42f34 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -17,23 +17,45 @@
import (
"android/soong/android"
"android/soong/cc"
+ "fmt"
"testing"
)
-func runSoongConfigModuleTypeTest(t *testing.T, tc bp2buildTestCase) {
+func runSoongConfigModuleTypeTest(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, registerSoongConfigModuleTypes, tc)
+ RunBp2BuildTestCase(t, registerSoongConfigModuleTypes, tc)
}
func registerSoongConfigModuleTypes(ctx android.RegistrationContext) {
cc.RegisterCCBuildComponents(ctx)
- ctx.RegisterModuleType("soong_config_module_type_import", android.SoongConfigModuleTypeImportFactory)
- ctx.RegisterModuleType("soong_config_module_type", android.SoongConfigModuleTypeFactory)
- ctx.RegisterModuleType("soong_config_string_variable", android.SoongConfigStringVariableDummyFactory)
- ctx.RegisterModuleType("soong_config_bool_variable", android.SoongConfigBoolVariableDummyFactory)
+ android.RegisterSoongConfigModuleBuildComponents(ctx)
ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+ ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
+}
+
+func TestErrorInBpFileDoesNotPanic(t *testing.T) {
+ bp := `
+soong_config_module_type {
+ name: "library_linking_strategy_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ variables: ["library_linking_strategy"],
+ properties: [
+ "shared_libs",
+ "static_libs",
+ ],
+}
+`
+
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ ExpectedErr: fmt.Errorf(`unknown variable "library_linking_strategy" in module type "library_linking_strategy_cc_defaults`),
+ })
}
func TestSoongConfigModuleType(t *testing.T) {
@@ -49,6 +71,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
feature1: {
conditions_default: {
@@ -60,12 +83,12 @@
}
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - soong_config_module_type is supported in bp2build",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- blueprint: bp,
- expectedBazelTargets: []string{`cc_library_static(
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - soong_config_module_type is supported in bp2build",
+ ModuleTypeUnderTest: "cc_library_static",
+ ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{`cc_library_static(
name = "foo",
copts = select({
"//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
@@ -94,6 +117,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
feature1: {
conditions_default: {
@@ -105,15 +129,15 @@
}
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - soong_config_module_type_import is supported in bp2build",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- filesystem: map[string]string{
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - soong_config_module_type_import is supported in bp2build",
+ ModuleTypeUnderTest: "cc_library_static",
+ ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ Filesystem: map[string]string{
"foo/bar/SoongConfig.bp": configBp,
},
- blueprint: bp,
- expectedBazelTargets: []string{`cc_library_static(
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{`cc_library_static(
name = "foo",
copts = select({
"//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
@@ -141,6 +165,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
board: {
soc_a: {
@@ -158,16 +183,17 @@
}
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - generates selects for string vars",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- blueprint: bp,
- expectedBazelTargets: []string{`cc_library_static(
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for string vars",
+ ModuleTypeUnderTest: "cc_library_static",
+ ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{`cc_library_static(
name = "foo",
copts = select({
"//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
"//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
+ "//build/bazel/product_variables:acme__board__soc_c": [],
"//conditions:default": ["-DSOC_DEFAULT"],
}),
local_includes = ["."],
@@ -186,7 +212,7 @@
soong_config_string_variable {
name: "board",
- values: ["soc_a", "soc_b", "soc_c"],
+ values: ["soc_a", "soc_b", "soc_c", "soc_d"],
}
soong_config_module_type {
@@ -200,6 +226,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
feature1: {
conditions_default: {
@@ -228,16 +255,17 @@
},
}`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - generates selects for multiple variable types",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- blueprint: bp,
- expectedBazelTargets: []string{`cc_library_static(
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for multiple variable types",
+ ModuleTypeUnderTest: "cc_library_static",
+ ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{`cc_library_static(
name = "foo",
copts = select({
"//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
"//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
+ "//build/bazel/product_variables:acme__board__soc_c": [],
"//conditions:default": ["-DSOC_DEFAULT"],
}) + select({
"//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
@@ -254,7 +282,7 @@
bp := `
soong_config_string_variable {
name: "board",
- values: ["soc_a", "soc_b", "soc_c"],
+ values: ["soc_a", "soc_b", "soc_c", "soc_d"],
}
soong_config_module_type {
@@ -268,6 +296,7 @@
custom_cc_library_static {
name: "foo",
bazel_module: { bp2build_available: true },
+ host_supported: true,
soong_config_variables: {
board: {
soc_a: {
@@ -293,24 +322,26 @@
cc_library_static { name: "soc_default_static_dep", bazel_module: { bp2build_available: false } }
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - generates selects for label list attributes",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- blueprint: bp,
- filesystem: map[string]string{
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for label list attributes",
+ ModuleTypeUnderTest: "cc_library_static",
+ ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{
"foo/bar/Android.bp": otherDeps,
},
- expectedBazelTargets: []string{`cc_library_static(
+ ExpectedBazelTargets: []string{`cc_library_static(
name = "foo",
copts = select({
"//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
"//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
+ "//build/bazel/product_variables:acme__board__soc_c": [],
"//conditions:default": ["-DSOC_DEFAULT"],
}),
implementation_deps = select({
"//build/bazel/product_variables:acme__board__soc_a": ["//foo/bar:soc_a_dep"],
"//build/bazel/product_variables:acme__board__soc_b": ["//foo/bar:soc_b_dep"],
+ "//build/bazel/product_variables:acme__board__soc_c": [],
"//conditions:default": ["//foo/bar:soc_default_static_dep"],
}),
local_includes = ["."],
@@ -356,15 +387,16 @@
name: "lib",
defaults: ["foo_defaults_2"],
bazel_module: { bp2build_available: true },
+ host_supported: true,
}
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - defaults with a single namespace",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- blueprint: bp,
- expectedBazelTargets: []string{`cc_library_static(
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - defaults with a single namespace",
+ ModuleTypeUnderTest: "cc_library_static",
+ ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{`cc_library_static(
name = "lib",
copts = select({
"//build/bazel/product_variables:vendor_foo__feature": [
@@ -429,21 +461,23 @@
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,
}
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - multiple defaults with a single namespace",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- blueprint: bp,
- expectedBazelTargets: []string{`cc_library_static(
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - multiple defaults with a single namespace",
+ ModuleTypeUnderTest: "cc_library_static",
+ ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{`cc_library_static(
name = "lib",
asflags = select({
"//build/bazel/product_variables:acme__feature": ["-asflag_bar"],
@@ -550,15 +584,16 @@
name: "lib",
defaults: ["foo_defaults", "qux_defaults"],
bazel_module: { bp2build_available: true },
+ host_supported: true,
}
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - defaults with multiple namespaces",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- blueprint: bp,
- expectedBazelTargets: []string{`cc_library_static(
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - defaults with multiple namespaces",
+ ModuleTypeUnderTest: "cc_library_static",
+ ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ Blueprint: bp,
+ ExpectedBazelTargets: []string{`cc_library_static(
name = "lib",
copts = select({
"//build/bazel/product_variables:vendor_bar__feature": ["-DVENDOR_BAR_FEATURE"],
@@ -574,6 +609,234 @@
)`}})
}
+func TestSoongConfigModuleType_Defaults_UseBaselineValueForStringProp(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "library_linking_strategy",
+ values: [
+ "prefer_static",
+ ],
+}
+
+soong_config_module_type {
+ name: "library_linking_strategy_custom",
+ module_type: "custom",
+ config_namespace: "ANDROID",
+ variables: ["library_linking_strategy"],
+ properties: [
+ "string_literal_prop",
+ ],
+}
+
+library_linking_strategy_custom {
+ name: "foo",
+ string_literal_prop: "29",
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ string_literal_prop: "30",
+ },
+ },
+ },
+}`
+
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{},
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "foo", AttrNameToString{
+ "string_literal_prop": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "29",
+ "//conditions:default": "30",
+ })`,
+ }),
+ },
+ })
+}
+
+func TestSoongConfigModuleType_UnsetConditions(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "library_linking_strategy",
+ values: [
+ "prefer_static",
+ ],
+}
+
+soong_config_module_type {
+ name: "library_linking_strategy_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ variables: ["library_linking_strategy"],
+ properties: [
+ "shared_libs",
+ "static_libs",
+ ],
+}
+
+library_linking_strategy_cc_defaults {
+ name: "library_linking_strategy_lib_a_defaults",
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_a",
+ ],
+ },
+ },
+ },
+}
+
+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: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_b",
+ ],
+ },
+ },
+ },
+}
+
+cc_binary {
+ name: "library_linking_strategy_sample_binary",
+ srcs: ["library_linking_strategy.cc"],
+ defaults: ["library_linking_strategy_merged_defaults"],
+ include_build_directory: false,
+}`
+
+ otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_default", bazel_module: { bp2build_available: false } }
+`
+
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{
+ "foo/bar/Android.bp": otherDeps,
+ },
+ ExpectedBazelTargets: []string{`cc_binary(
+ name = "library_linking_strategy_sample_binary",
+ dynamic_deps = select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": [
+ "//foo/bar:lib_b",
+ "//foo/bar:lib_a",
+ ],
+ }),
+ srcs = ["library_linking_strategy.cc"],
+)`}})
+}
+
+func TestSoongConfigModuleType_UnsetConditionsExcludeLibs(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "library_linking_strategy",
+ values: [
+ "prefer_static",
+ ],
+}
+
+soong_config_module_type {
+ name: "library_linking_strategy_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ variables: ["library_linking_strategy"],
+ properties: ["shared_libs"],
+}
+
+library_linking_strategy_cc_defaults {
+ name: "library_linking_strategy_lib_a_defaults",
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_a",
+ ],
+ },
+ },
+ },
+}
+
+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: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_b",
+ "lib_c",
+ ],
+ },
+ },
+ },
+ exclude_shared_libs: ["lib_a"],
+}
+
+cc_binary {
+ name: "library_linking_strategy_sample_binary",
+ defaults: ["library_linking_strategy_merged_defaults"],
+ include_build_directory: false,
+}
+
+cc_binary {
+ name: "library_linking_strategy_sample_binary_with_excludes",
+ defaults: ["library_linking_strategy_merged_defaults"],
+ exclude_shared_libs: ["lib_c"],
+ include_build_directory: false,
+}`
+
+ otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_c", bazel_module: { bp2build_available: false } }
+`
+
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{
+ "foo/bar/Android.bp": otherDeps,
+ },
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary", AttrNameToString{
+ "dynamic_deps": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": [
+ "//foo/bar:lib_b",
+ "//foo/bar:lib_c",
+ ],
+ })`,
+ }),
+ MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary_with_excludes", AttrNameToString{
+ "dynamic_deps": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": ["//foo/bar:lib_b"],
+ })`,
+ }),
+ }})
+}
+
func TestSoongConfigModuleType_Defaults(t *testing.T) {
bp := `
soong_config_string_variable {
@@ -615,6 +878,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: {
@@ -643,15 +907,15 @@
cc_library { name: "lib_default", bazel_module: { bp2build_available: false } }
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - generates selects for library_linking_strategy",
- moduleTypeUnderTest: "cc_binary",
- moduleTypeUnderTestFactory: cc.BinaryFactory,
- blueprint: bp,
- filesystem: map[string]string{
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{
"foo/bar/Android.bp": otherDeps,
},
- expectedBazelTargets: []string{`cc_binary(
+ ExpectedBazelTargets: []string{`cc_binary(
name = "library_linking_strategy_sample_binary",
deps = select({
"//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [
@@ -714,6 +978,7 @@
cc_binary {
name: "library_linking_strategy_sample_binary",
+ host_supported: true,
srcs: ["library_linking_strategy.cc"],
defaults: ["library_linking_strategy_sample_defaults"],
}`
@@ -723,15 +988,15 @@
cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - generates selects for library_linking_strategy",
- moduleTypeUnderTest: "cc_binary",
- moduleTypeUnderTestFactory: cc.BinaryFactory,
- blueprint: bp,
- filesystem: map[string]string{
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{
"foo/bar/Android.bp": otherDeps,
},
- expectedBazelTargets: []string{`cc_binary(
+ ExpectedBazelTargets: []string{`cc_binary(
name = "library_linking_strategy_sample_binary",
deps = select({
"//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [
@@ -800,6 +1065,7 @@
cc_binary {
name: "alphabet_binary",
+ host_supported: true,
srcs: ["main.cc"],
defaults: ["alphabet_sample_cc_defaults"],
}`
@@ -810,15 +1076,15 @@
cc_library { name: "lib_default", bazel_module: { bp2build_available: false } }
`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - generates selects for library_linking_strategy",
- moduleTypeUnderTest: "cc_binary",
- moduleTypeUnderTestFactory: cc.BinaryFactory,
- blueprint: bp,
- filesystem: map[string]string{
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{
"foo/bar/Android.bp": otherDeps,
},
- expectedBazelTargets: []string{`cc_binary(
+ ExpectedBazelTargets: []string{`cc_binary(
name = "alphabet_binary",
deps = select({
"//build/bazel/product_variables:android__alphabet__a": [],
@@ -861,6 +1127,7 @@
cc_binary {
name: "alphabet_binary",
srcs: ["main.cc"],
+ host_supported: true,
defaults: ["alphabet_sample_cc_defaults"],
enabled: false,
arch: {
@@ -875,13 +1142,13 @@
},
}`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - generates selects for library_linking_strategy",
- moduleTypeUnderTest: "cc_binary",
- moduleTypeUnderTestFactory: cc.BinaryFactory,
- blueprint: bp,
- filesystem: map[string]string{},
- expectedBazelTargets: []string{`cc_binary(
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{},
+ ExpectedBazelTargets: []string{`cc_binary(
name = "alphabet_binary",
local_includes = ["."],
srcs = ["main.cc"],
@@ -928,13 +1195,13 @@
enabled: false,
}`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - generates selects for library_linking_strategy",
- moduleTypeUnderTest: "cc_binary",
- moduleTypeUnderTestFactory: cc.BinaryFactory,
- blueprint: bp,
- filesystem: map[string]string{},
- expectedBazelTargets: []string{`cc_binary(
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{},
+ ExpectedBazelTargets: []string{`cc_binary(
name = "alphabet_binary",
local_includes = ["."],
srcs = ["main.cc"],
@@ -958,6 +1225,7 @@
alphabet_cc_defaults {
name: "alphabet_sample_cc_defaults",
+ host_supported: true,
soong_config_variables: {
special_build: {
enabled: true,
@@ -971,13 +1239,13 @@
defaults: ["alphabet_sample_cc_defaults"],
}`
- runSoongConfigModuleTypeTest(t, bp2buildTestCase{
- description: "soong config variables - generates selects for library_linking_strategy",
- moduleTypeUnderTest: "cc_binary",
- moduleTypeUnderTestFactory: cc.BinaryFactory,
- blueprint: bp,
- filesystem: map[string]string{},
- expectedBazelTargets: []string{`cc_binary(
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{},
+ ExpectedBazelTargets: []string{`cc_binary(
name = "alphabet_binary",
local_includes = ["."],
srcs = ["main.cc"],
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 15a6335..5c33308 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -1,3 +1,17 @@
+// 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 (
@@ -5,8 +19,16 @@
"io/ioutil"
"os"
"path/filepath"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
"android/soong/shared"
+
+ "github.com/google/blueprint/pathtools"
)
// A tree structure that describes what to do at each directory in the created
@@ -15,14 +37,33 @@
// or a directory. If excluded is true, then that file/directory should be
// excluded from symlinking. Otherwise, the node is not excluded, but one of its
// descendants is (otherwise the node in question would not exist)
-type node struct {
+
+// This is a version int written to a file called symlink_forest_version at the root of the
+// symlink forest. If the version here does not match the version in the file, then we'll
+// clean the whole symlink forest and recreate it. This number can be bumped whenever there's
+// an incompatible change to the forest layout or a bug in incrementality that needs to be fixed
+// on machines that may still have the bug present in their forest.
+const symlinkForestVersion = 1
+
+type instructionsNode struct {
name string
excluded bool // If false, this is just an intermediate node
- children map[string]*node
+ children map[string]*instructionsNode
}
-// Ensures that the a node for the given path exists in the tree and returns it.
-func ensureNodeExists(root *node, path string) *node {
+type symlinkForestContext struct {
+ verbose bool
+ topdir string // $TOPDIR
+
+ // State
+ wg sync.WaitGroup
+ depCh chan string
+ mkdirCount atomic.Uint64
+ symlinkCount atomic.Uint64
+}
+
+// Ensures that the node for the given path exists in the tree and returns it.
+func ensureNodeExists(root *instructionsNode, path string) *instructionsNode {
if path == "" {
return root
}
@@ -40,15 +81,14 @@
if child, ok := dn.children[base]; ok {
return child
} else {
- dn.children[base] = &node{base, false, make(map[string]*node)}
+ dn.children[base] = &instructionsNode{base, false, make(map[string]*instructionsNode)}
return dn.children[base]
}
}
-// Turns a list of paths to be excluded into a tree made of "node" objects where
-// the specified paths are marked as excluded.
-func treeFromExcludePathList(paths []string) *node {
- result := &node{"", false, make(map[string]*node)}
+// Turns a list of paths to be excluded into a tree
+func instructionsFromExcludePathList(paths []string) *instructionsNode {
+ result := &instructionsNode{"", false, make(map[string]*instructionsNode)}
for _, p := range paths {
ensureNodeExists(result, p).excluded = true
@@ -57,6 +97,73 @@
return result
}
+func mergeBuildFiles(output string, srcBuildFile string, generatedBuildFile string, verbose bool) error {
+
+ srcBuildFileContent, err := os.ReadFile(srcBuildFile)
+ if err != nil {
+ return err
+ }
+
+ generatedBuildFileContent, err := os.ReadFile(generatedBuildFile)
+ if err != nil {
+ return err
+ }
+
+ // There can't be a package() call in both the source and generated BUILD files.
+ // bp2build will generate a package() call for licensing information, but if
+ // there's no licensing information, it will still generate a package() call
+ // that just sets default_visibility=public. If the handcrafted build file
+ // also has a package() call, we'll allow it to override the bp2build
+ // generated one if it doesn't have any licensing information. If the bp2build
+ // one has licensing information and the handcrafted one exists, we'll leave
+ // them both in for bazel to throw an error.
+ packageRegex := regexp.MustCompile(`(?m)^package\s*\(`)
+ packageDefaultVisibilityRegex := regexp.MustCompile(`(?m)^package\s*\(\s*default_visibility\s*=\s*\[\s*"//visibility:public",?\s*]\s*\)`)
+ if packageRegex.Find(srcBuildFileContent) != nil {
+ if verbose && packageDefaultVisibilityRegex.Find(generatedBuildFileContent) != nil {
+ fmt.Fprintf(os.Stderr, "Both '%s' and '%s' have a package() target, removing the first one\n",
+ generatedBuildFile, srcBuildFile)
+ }
+ generatedBuildFileContent = packageDefaultVisibilityRegex.ReplaceAll(generatedBuildFileContent, []byte{})
+ }
+
+ newContents := generatedBuildFileContent
+ if newContents[len(newContents)-1] != '\n' {
+ newContents = append(newContents, '\n')
+ }
+ newContents = append(newContents, srcBuildFileContent...)
+
+ // Say you run bp2build 4 times:
+ // - The first time there's only an Android.bp file. bp2build will convert it to a build file
+ // under out/soong/bp2build, then symlink from the forest to that generated file
+ // - Then you add a handcrafted BUILD file in the same directory. bp2build will merge this with
+ // the generated one, and write the result to the output file in the forest. But the output
+ // file was a symlink to out/soong/bp2build from the previous step! So we erroneously update
+ // the file in out/soong/bp2build instead. So far this doesn't cause any problems...
+ // - You run a 3rd bp2build with no relevant changes. Everything continues to work.
+ // - You then add a comment to the handcrafted BUILD file. This causes a merge with the
+ // generated file again. But since we wrote to the generated file in step 2, the generated
+ // file has an old copy of the handcrafted file in it! This probably causes duplicate bazel
+ // targets.
+ // To solve this, if we see that the output file is a symlink from a previous build, remove it.
+ stat, err := os.Lstat(output)
+ if err != nil && !os.IsNotExist(err) {
+ return err
+ } else if err == nil {
+ if stat.Mode()&os.ModeSymlink == os.ModeSymlink {
+ if verbose {
+ fmt.Fprintf(os.Stderr, "Removing symlink so that we can replace it with a merged file: %s\n", output)
+ }
+ err = os.Remove(output)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return pathtools.WriteFileIfChanged(output, newContents, 0666)
+}
+
// Calls readdir() and returns it as a map from the basename of the files in dir
// to os.FileInfo.
func readdirToMap(dir string) map[string]os.FileInfo {
@@ -82,118 +189,313 @@
}
// Creates a symbolic link at dst pointing to src
-func symlinkIntoForest(topdir, dst, src string) {
- err := os.Symlink(shared.JoinPath(topdir, src), shared.JoinPath(topdir, dst))
- if err != nil {
+func symlinkIntoForest(topdir, dst, src string) uint64 {
+ srcPath := shared.JoinPath(topdir, src)
+ dstPath := shared.JoinPath(topdir, dst)
+
+ // Check if a symlink already exists.
+ if dstInfo, err := os.Lstat(dstPath); err != nil {
+ if !os.IsNotExist(err) {
+ fmt.Fprintf(os.Stderr, "Failed to lstat '%s': %s", dst, err)
+ os.Exit(1)
+ }
+ } else {
+ if dstInfo.Mode()&os.ModeSymlink != 0 {
+ // Assume that the link's target is correct, i.e. no manual tampering.
+ // E.g. OUT_DIR could have been previously used with a different source tree check-out!
+ return 0
+ } else {
+ if err := os.RemoveAll(dstPath); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to remove '%s': %s", dst, err)
+ os.Exit(1)
+ }
+ }
+ }
+
+ // Create symlink.
+ if err := os.Symlink(srcPath, dstPath); err != nil {
fmt.Fprintf(os.Stderr, "Cannot create symlink at '%s' pointing to '%s': %s", dst, src, err)
os.Exit(1)
}
+ return 1
+}
+
+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
+}
+
+// maybeCleanSymlinkForest will remove the whole symlink forest directory if the version recorded
+// in the symlink_forest_version file is not equal to symlinkForestVersion.
+func maybeCleanSymlinkForest(topdir, forest string, verbose bool) error {
+ versionFilePath := shared.JoinPath(topdir, forest, "symlink_forest_version")
+ versionFileContents, err := os.ReadFile(versionFilePath)
+ if err != nil && !os.IsNotExist(err) {
+ return err
+ }
+ versionFileString := strings.TrimSpace(string(versionFileContents))
+ symlinkForestVersionString := strconv.Itoa(symlinkForestVersion)
+ if err != nil || versionFileString != symlinkForestVersionString {
+ if verbose {
+ fmt.Fprintf(os.Stderr, "Old symlink_forest_version was %q, current is %q. Cleaning symlink forest before recreating...\n", versionFileString, symlinkForestVersionString)
+ }
+ err = os.RemoveAll(shared.JoinPath(topdir, forest))
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// maybeWriteVersionFile will write the symlink_forest_version file containing symlinkForestVersion
+// if it doesn't exist already. If it exists we know it must contain symlinkForestVersion because
+// we checked for that already in maybeCleanSymlinkForest
+func maybeWriteVersionFile(topdir, forest string) error {
+ versionFilePath := shared.JoinPath(topdir, forest, "symlink_forest_version")
+ _, err := os.Stat(versionFilePath)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return err
+ }
+ err = os.WriteFile(versionFilePath, []byte(strconv.Itoa(symlinkForestVersion)+"\n"), 0666)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
}
// 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
-// into acc.
-func plantSymlinkForestRecursive(topdir string, forestDir string, buildFilesDir string, srcDir string, exclude *node, acc *[]string, okay *bool) {
- if exclude != nil && exclude.excluded {
- // This directory is not needed, bail out
- return
- }
+// instructions. Collects every directory encountered during the traversal of
+// srcDir .
+func plantSymlinkForestRecursive(context *symlinkForestContext, instructions *instructionsNode, forestDir string, buildFilesDir string, srcDir string) {
+ defer context.wg.Done()
- *acc = append(*acc, srcDir)
- srcDirMap := readdirToMap(shared.JoinPath(topdir, srcDir))
- buildFilesMap := readdirToMap(shared.JoinPath(topdir, buildFilesDir))
-
- allEntries := make(map[string]bool)
- for n, _ := range srcDirMap {
- allEntries[n] = true
- }
-
- for n, _ := range buildFilesMap {
- allEntries[n] = true
- }
-
- err := os.MkdirAll(shared.JoinPath(topdir, forestDir), 0777)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Cannot mkdir '%s': %s\n", forestDir, err)
+ if instructions != nil && instructions.excluded {
+ // Excluded paths are skipped at the level of the non-excluded parent.
+ fmt.Fprintf(os.Stderr, "may not specify a root-level exclude directory '%s'", srcDir)
os.Exit(1)
}
- for f, _ := range allEntries {
+ // We don't add buildFilesDir here because the bp2build files marker files is
+ // already a dependency which covers it. If we ever wanted to turn this into
+ // a generic symlink forest creation tool, we'd need to add it, too.
+ context.depCh <- srcDir
+
+ srcDirMap := readdirToMap(shared.JoinPath(context.topdir, srcDir))
+ buildFilesMap := readdirToMap(shared.JoinPath(context.topdir, buildFilesDir))
+
+ renamingBuildFile := false
+ if _, ok := srcDirMap["BUILD"]; ok {
+ if _, ok := srcDirMap["BUILD.bazel"]; !ok {
+ if _, ok := buildFilesMap["BUILD.bazel"]; ok {
+ renamingBuildFile = true
+ srcDirMap["BUILD.bazel"] = srcDirMap["BUILD"]
+ delete(srcDirMap, "BUILD")
+ if instructions != nil {
+ if _, ok := instructions.children["BUILD"]; ok {
+ instructions.children["BUILD.bazel"] = instructions.children["BUILD"]
+ delete(instructions.children, "BUILD")
+ }
+ }
+ }
+ }
+ }
+
+ allEntries := make([]string, 0, len(srcDirMap)+len(buildFilesMap))
+ for n := range srcDirMap {
+ allEntries = append(allEntries, n)
+ }
+ for n := range buildFilesMap {
+ if _, ok := srcDirMap[n]; !ok {
+ allEntries = append(allEntries, n)
+ }
+ }
+ // Tests read the error messages generated, so ensure their order is deterministic
+ sort.Strings(allEntries)
+
+ fullForestPath := shared.JoinPath(context.topdir, forestDir)
+ createForestDir := false
+ if fi, err := os.Lstat(fullForestPath); err != nil {
+ if os.IsNotExist(err) {
+ createForestDir = true
+ } else {
+ fmt.Fprintf(os.Stderr, "Could not read info for '%s': %s\n", forestDir, err)
+ }
+ } else if fi.Mode()&os.ModeDir == 0 {
+ if err := os.RemoveAll(fullForestPath); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to remove '%s': %s", forestDir, err)
+ os.Exit(1)
+ }
+ createForestDir = true
+ }
+ if createForestDir {
+ if err := os.MkdirAll(fullForestPath, 0777); err != nil {
+ fmt.Fprintf(os.Stderr, "Could not mkdir '%s': %s\n", forestDir, err)
+ os.Exit(1)
+ }
+ context.mkdirCount.Add(1)
+ }
+
+ // Start with a list of items that already exist in the forest, and remove
+ // each element as it is processed in allEntries. Any remaining items in
+ // forestMapForDeletion must be removed. (This handles files which were
+ // removed since the previous forest generation).
+ forestMapForDeletion := readdirToMap(shared.JoinPath(context.topdir, forestDir))
+
+ for _, f := range allEntries {
if f[0] == '.' {
continue // Ignore dotfiles
}
+ delete(forestMapForDeletion, f)
+ // todo add deletionCount metric
// The full paths of children in the input trees and in the output tree
forestChild := shared.JoinPath(forestDir, f)
srcChild := shared.JoinPath(srcDir, f)
+ if f == "BUILD.bazel" && renamingBuildFile {
+ srcChild = shared.JoinPath(srcDir, "BUILD")
+ }
buildFilesChild := shared.JoinPath(buildFilesDir, f)
- // Descend in the exclusion tree, if there are any excludes left
- var excludeChild *node
- if exclude == nil {
- excludeChild = nil
- } else {
- excludeChild = exclude.children[f]
+ // Descend in the instruction tree if it exists
+ var instructionsChild *instructionsNode
+ if instructions != nil {
+ instructionsChild = instructions.children[f]
}
srcChildEntry, sExists := srcDirMap[f]
buildFilesChildEntry, bExists := buildFilesMap[f]
- excluded := excludeChild != nil && excludeChild.excluded
- if excluded {
+ if instructionsChild != nil && instructionsChild.excluded {
+ if bExists {
+ context.symlinkCount.Add(symlinkIntoForest(context.topdir, forestChild, buildFilesChild))
+ }
continue
}
+ sDir := sExists && isDir(shared.JoinPath(context.topdir, srcChild), srcChildEntry)
+ bDir := bExists && isDir(shared.JoinPath(context.topdir, buildFilesChild), buildFilesChildEntry)
+
if !sExists {
- if buildFilesChildEntry.IsDir() && excludeChild != nil {
+ if bDir && instructionsChild != 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)
+ context.wg.Add(1)
+ go plantSymlinkForestRecursive(context, instructionsChild, forestChild, buildFilesChild, srcChild)
} else {
// Not in the source tree, symlink BUILD file
- symlinkIntoForest(topdir, forestChild, buildFilesChild)
+ context.symlinkCount.Add(symlinkIntoForest(context.topdir, forestChild, buildFilesChild))
}
} else if !bExists {
- if srcChildEntry.IsDir() && excludeChild != nil {
+ if sDir && instructionsChild != 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)
+ context.wg.Add(1)
+ go plantSymlinkForestRecursive(context, instructionsChild, forestChild, buildFilesChild, srcChild)
} else {
// Not in the build file tree, symlink source tree, carry on
- symlinkIntoForest(topdir, forestChild, srcChild)
+ context.symlinkCount.Add(symlinkIntoForest(context.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() {
- // 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",
- buildFilesChild, srcChild, forestChild)
- symlinkIntoForest(topdir, forestChild, buildFilesChild)
+ context.wg.Add(1)
+ go plantSymlinkForestRecursive(context, instructionsChild, forestChild, buildFilesChild, srcChild)
+ } else if !sDir && !bDir {
+ // Neither is a directory. Merge them.
+ srcBuildFile := shared.JoinPath(context.topdir, srcChild)
+ generatedBuildFile := shared.JoinPath(context.topdir, buildFilesChild)
+ // The Android.bp file that codegen used to produce `buildFilesChild` is
+ // already a dependency, we can ignore `buildFilesChild`.
+ context.depCh <- srcChild
+ if err := mergeBuildFiles(shared.JoinPath(context.topdir, forestChild), srcBuildFile, generatedBuildFile, context.verbose); err != nil {
+ fmt.Fprintf(os.Stderr, "Error merging %s and %s: %s",
+ srcBuildFile, generatedBuildFile, err)
+ os.Exit(1)
+ }
} else {
// Both exist and one is a file. This is an error.
fmt.Fprintf(os.Stderr,
"Conflict in workspace symlink tree creation: both '%s' and '%s' exist and exactly one is a directory\n",
srcChild, buildFilesChild)
- *okay = false
+ os.Exit(1)
+ }
+ }
+
+ // Remove all files in the forest that exist in neither the source
+ // tree nor the build files tree. (This handles files which were removed
+ // since the previous forest generation).
+ for f := range forestMapForDeletion {
+ var instructionsChild *instructionsNode
+ if instructions != nil {
+ instructionsChild = instructions.children[f]
+ }
+
+ if instructionsChild != nil && instructionsChild.excluded {
+ // This directory may be excluded because bazel writes to it under the
+ // forest root. Thus this path is intentionally left alone.
+ continue
+ }
+ forestChild := shared.JoinPath(context.topdir, forestDir, f)
+ if err := os.RemoveAll(forestChild); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to remove '%s/%s': %s", forestDir, f, err)
+ os.Exit(1)
}
}
}
-// Creates a symlink forest by merging the directory tree at "buildFiles" and
+// PlantSymlinkForest Creates a symlink forest by merging the directory tree at "buildFiles" and
// "srcDir" while excluding paths listed in "exclude". Returns the set of paths
// under srcDir on which readdir() had to be called to produce the symlink
// forest.
-func PlantSymlinkForest(topdir string, forest string, buildFiles string, srcDir string, exclude []string) []string {
- deps := make([]string, 0)
- os.RemoveAll(shared.JoinPath(topdir, forest))
- excludeTree := treeFromExcludePathList(exclude)
- okay := true
- plantSymlinkForestRecursive(topdir, forest, buildFiles, srcDir, excludeTree, &deps, &okay)
- if !okay {
+func PlantSymlinkForest(verbose bool, topdir string, forest string, buildFiles string, exclude []string) (deps []string, mkdirCount, symlinkCount uint64) {
+ context := &symlinkForestContext{
+ verbose: verbose,
+ topdir: topdir,
+ depCh: make(chan string),
+ mkdirCount: atomic.Uint64{},
+ symlinkCount: atomic.Uint64{},
+ }
+
+ err := maybeCleanSymlinkForest(topdir, forest, verbose)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
- return deps
+
+ instructions := instructionsFromExcludePathList(exclude)
+ go func() {
+ context.wg.Add(1)
+ plantSymlinkForestRecursive(context, instructions, forest, buildFiles, ".")
+ context.wg.Wait()
+ close(context.depCh)
+ }()
+
+ for dep := range context.depCh {
+ deps = append(deps, dep)
+ }
+
+ err = maybeWriteVersionFile(topdir, forest)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ return deps, context.mkdirCount.Load(), context.symlinkCount.Load()
}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 029ba49..6e919db 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -21,22 +21,18 @@
import (
"fmt"
+ "sort"
"strings"
"testing"
+ "github.com/google/blueprint/proptools"
+
"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.NewBp2BuildAllowlist().SetDefaultConfig(
- allowlists.Bp2BuildConfig{
- android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
- },
- )
-
buildDir string
)
@@ -53,16 +49,16 @@
return false
}
-func errored(t *testing.T, tc bp2buildTestCase, errs []error) bool {
+func errored(t *testing.T, tc Bp2buildTestCase, errs []error) bool {
t.Helper()
- if tc.expectedErr != nil {
+ if tc.ExpectedErr != nil {
// Rely on checkErrors, as this test case is expected to have an error.
return false
}
if len(errs) > 0 {
for _, err := range errs {
- t.Errorf("%s: %s", tc.description, err)
+ t.Errorf("%s: %s", tc.Description, err)
}
return true
}
@@ -71,84 +67,215 @@
return false
}
-func runBp2BuildTestCaseSimple(t *testing.T, tc bp2buildTestCase) {
+func RunBp2BuildTestCaseSimple(t *testing.T, tc Bp2buildTestCase) {
t.Helper()
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
}
-type bp2buildTestCase struct {
- description string
- moduleTypeUnderTest string
- moduleTypeUnderTestFactory android.ModuleFactory
- blueprint string
- expectedBazelTargets []string
- filesystem map[string]string
- dir string
+type Bp2buildTestCase struct {
+ Description string
+ ModuleTypeUnderTest string
+ ModuleTypeUnderTestFactory android.ModuleFactory
+ Blueprint string
+ ExpectedBazelTargets []string
+ Filesystem map[string]string
+ Dir string
// An error with a string contained within the string of the expected error
- expectedErr error
- unconvertedDepsMode unconvertedDepsMode
+ ExpectedErr error
+ UnconvertedDepsMode unconvertedDepsMode
+
+ // For every directory listed here, the BUILD file for that directory will
+ // be merged with the generated BUILD file. This allows custom BUILD targets
+ // to be used in tests, or use BUILD files to draw package boundaries.
+ KeepBuildFileForDirs []string
}
-func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
+func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
+ t.Helper()
+ bp2buildSetup := android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerModuleTypes),
+ SetBp2BuildTestRunner,
+ )
+ runBp2BuildTestCaseWithSetup(t, bp2buildSetup, tc)
+}
+
+func RunApiBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
+ t.Helper()
+ apiBp2BuildSetup := android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerModuleTypes),
+ SetApiBp2BuildTestRunner,
+ )
+ runBp2BuildTestCaseWithSetup(t, apiBp2BuildSetup, tc)
+}
+
+func runBp2BuildTestCaseWithSetup(t *testing.T, extraPreparer android.FixturePreparer, tc Bp2buildTestCase) {
t.Helper()
dir := "."
filesystem := make(map[string][]byte)
- toParse := []string{
- "Android.bp",
- }
- for f, content := range tc.filesystem {
- if strings.HasSuffix(f, "Android.bp") {
- toParse = append(toParse, f)
- }
+ for f, content := range tc.Filesystem {
filesystem[f] = []byte(content)
}
- config := android.TestConfig(buildDir, nil, tc.blueprint, filesystem)
- ctx := android.NewTestContext(config)
- registerModuleTypes(ctx)
- ctx.RegisterModuleType(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestFactory)
- ctx.RegisterBp2BuildConfig(bp2buildConfig)
- ctx.RegisterForBazelConversion()
-
- _, parseErrs := ctx.ParseFileList(dir, toParse)
- if errored(t, tc, parseErrs) {
- return
- }
- _, resolveDepsErrs := ctx.ResolveDependencies(config)
- if errored(t, tc, resolveDepsErrs) {
- return
+ preparers := []android.FixturePreparer{
+ extraPreparer,
+ android.FixtureMergeMockFs(filesystem),
+ android.FixtureWithRootAndroidBp(tc.Blueprint),
+ android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory)
+ }),
+ android.FixtureModifyContext(func(ctx *android.TestContext) {
+ // A default configuration for tests to not have to specify bp2build_available on top level
+ // targets.
+ bp2buildConfig := android.NewBp2BuildAllowlist().SetDefaultConfig(
+ allowlists.Bp2BuildConfig{
+ android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
+ },
+ )
+ for _, f := range tc.KeepBuildFileForDirs {
+ bp2buildConfig.SetKeepExistingBuildFile(map[string]bool{
+ f: /*recursive=*/ false,
+ })
+ }
+ ctx.RegisterBp2BuildConfig(bp2buildConfig)
+ }),
+ android.FixtureModifyEnv(func(env map[string]string) {
+ if tc.UnconvertedDepsMode == errorModulesUnconvertedDeps {
+ env["BP2BUILD_ERROR_UNCONVERTED"] = "true"
+ }
+ }),
}
- errs := append(parseErrs, resolveDepsErrs...)
- if tc.expectedErr != nil && checkError(t, errs, tc.expectedErr) {
+ preparer := android.GroupFixturePreparers(preparers...)
+ if tc.ExpectedErr != nil {
+ pattern := "\\Q" + tc.ExpectedErr.Error() + "\\E"
+ preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(pattern))
+ }
+ result := preparer.RunTestWithCustomResult(t).(*BazelTestResult)
+ if len(result.Errs) > 0 {
return
}
checkDir := dir
- if tc.dir != "" {
- checkDir = tc.dir
+ if tc.Dir != "" {
+ checkDir = tc.Dir
}
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
- codegenCtx.unconvertedDepMode = tc.unconvertedDepsMode
- bazelTargets, errs := generateBazelTargetsForDir(codegenCtx, checkDir)
- if tc.expectedErr != nil {
- if checkError(t, errs, tc.expectedErr) {
- return
+ expectedTargets := map[string][]string{
+ checkDir: tc.ExpectedBazelTargets,
+ }
+
+ result.CompareAllBazelTargets(t, tc.Description, expectedTargets, true)
+}
+
+// SetBp2BuildTestRunner customizes the test fixture mechanism to run tests in Bp2Build mode.
+var SetBp2BuildTestRunner = android.FixtureSetTestRunner(&bazelTestRunner{Bp2Build})
+
+// SetApiBp2BuildTestRunner customizes the test fixture mechanism to run tests in ApiBp2build mode.
+var SetApiBp2BuildTestRunner = android.FixtureSetTestRunner(&bazelTestRunner{ApiBp2build})
+
+// bazelTestRunner customizes the test fixture mechanism to run tests of the bp2build and
+// apiBp2build build modes.
+type bazelTestRunner struct {
+ mode CodegenMode
+}
+
+func (b *bazelTestRunner) FinalPreparer(result *android.TestResult) android.CustomTestResult {
+ ctx := result.TestContext
+ switch b.mode {
+ case Bp2Build:
+ ctx.RegisterForBazelConversion()
+ case ApiBp2build:
+ ctx.RegisterForApiBazelConversion()
+ default:
+ panic(fmt.Errorf("unknown build mode: %d", b.mode))
+ }
+
+ return &BazelTestResult{TestResult: result}
+}
+
+func (b *bazelTestRunner) PostParseProcessor(result android.CustomTestResult) {
+ bazelResult := result.(*BazelTestResult)
+ ctx := bazelResult.TestContext
+ config := bazelResult.Config
+ _, errs := ctx.ResolveDependencies(config)
+ if bazelResult.CollateErrs(errs) {
+ return
+ }
+
+ codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
+ res, errs := GenerateBazelTargets(codegenCtx, false)
+ if bazelResult.CollateErrs(errs) {
+ return
+ }
+
+ // Store additional data for access by tests.
+ bazelResult.conversionResults = res
+}
+
+// BazelTestResult is a wrapper around android.TestResult to provide type safe access to the bazel
+// specific data stored by the bazelTestRunner.
+type BazelTestResult struct {
+ *android.TestResult
+
+ // The result returned by the GenerateBazelTargets function.
+ conversionResults
+}
+
+// CompareAllBazelTargets compares the BazelTargets produced by the test for all the directories
+// with the supplied set of expected targets.
+//
+// If ignoreUnexpected=false then this enforces an exact match where every BazelTarget produced must
+// have a corresponding expected BazelTarget.
+//
+// If ignoreUnexpected=true then it will ignore directories for which there are no expected targets.
+func (b BazelTestResult) CompareAllBazelTargets(t *testing.T, description string, expectedTargets map[string][]string, ignoreUnexpected bool) {
+ t.Helper()
+ actualTargets := b.buildFileToTargets
+
+ // Generate the sorted set of directories to check.
+ dirsToCheck := android.SortedKeys(expectedTargets)
+ if !ignoreUnexpected {
+ // This needs to perform an exact match so add the directories in which targets were
+ // produced to the list of directories to check.
+ dirsToCheck = append(dirsToCheck, android.SortedKeys(actualTargets)...)
+ dirsToCheck = android.SortedUniqueStrings(dirsToCheck)
+ }
+
+ for _, dir := range dirsToCheck {
+ expected := expectedTargets[dir]
+ actual := actualTargets[dir]
+
+ if expected == nil {
+ if actual != nil {
+ t.Errorf("did not expect any bazel modules in %q but found %d", dir, len(actual))
+ }
+ } else if actual == nil {
+ expectedCount := len(expected)
+ if expectedCount > 0 {
+ t.Errorf("expected %d bazel modules in %q but did not find any", expectedCount, dir)
+ }
} else {
- t.Errorf("Expected error: %q, got: %q", tc.expectedErr, errs)
+ b.CompareBazelTargets(t, description, expected, actual)
}
- } else {
- android.FailIfErrored(t, errs)
}
- if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount {
- t.Errorf("%s: Expected %d bazel target (%s), got `%d`` (%s)",
- tc.description, expectedCount, tc.expectedBazelTargets, actualCount, bazelTargets)
+}
+
+func (b BazelTestResult) CompareBazelTargets(t *testing.T, description string, expectedContents []string, actualTargets BazelTargets) {
+ t.Helper()
+ if actualCount, expectedCount := len(actualTargets), len(expectedContents); actualCount != expectedCount {
+ t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)",
+ description, expectedCount, expectedContents, actualCount, actualTargets)
} else {
- for i, target := range bazelTargets {
- if w, g := tc.expectedBazelTargets[i], target.content; w != g {
+ sort.SliceStable(actualTargets, func(i, j int) bool {
+ return actualTargets[i].name < actualTargets[j].name
+ })
+ sort.SliceStable(expectedContents, func(i, j int) bool {
+ return getTargetName(expectedContents[i]) < getTargetName(expectedContents[j])
+ })
+ for i, actualTarget := range actualTargets {
+ if w, g := expectedContents[i], actualTarget.content; w != g {
t.Errorf(
- "%s: Expected generated Bazel target to be `%s`, got `%s`",
- tc.description, w, g)
+ "%s[%d]: Expected generated Bazel target to be `%s`, got `%s`",
+ description, i, w, g)
}
}
}
@@ -173,11 +300,12 @@
Bool_prop bool
Bool_ptr_prop *bool
// Ensure that properties tagged `blueprint:mutated` are omitted
- Int_prop int `blueprint:"mutated"`
- Int64_ptr_prop *int64
- String_prop string
- String_ptr_prop *string
- String_list_prop []string
+ Int_prop int `blueprint:"mutated"`
+ Int64_ptr_prop *int64
+ String_prop string
+ String_literal_prop *string `android:"arch_variant"`
+ String_ptr_prop *string
+ String_list_prop []string
Nested_props nestedProps
Nested_props_ptr *nestedProps
@@ -187,6 +315,8 @@
// Prop used to indicate this conversion should be 1 module -> multiple targets
One_to_many_prop *bool
+
+ Api *string // File describing the APIs of this module
}
type customModule struct {
@@ -213,12 +343,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
@@ -281,23 +435,38 @@
type customBazelModuleAttributes struct {
EmbeddedAttr
*OtherEmbeddedAttr
- String_ptr_prop *string
- String_list_prop []string
- Arch_paths bazel.LabelListAttribute
+ String_literal_prop bazel.StringAttribute
+ String_ptr_prop *string
+ String_list_prop []string
+ Arch_paths bazel.LabelListAttribute
+ Api bazel.LabelAttribute
}
func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- paths := bazel.LabelListAttribute{}
-
if p := m.props.One_to_many_prop; p != nil && *p {
customBp2buildOneToMany(ctx, m)
return
}
+ paths := bazel.LabelListAttribute{}
+ strAttr := bazel.StringAttribute{}
for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
for config, props := range configToProps {
- if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
- paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, archProps.Arch_paths, archProps.Arch_paths_exclude))
+ if custProps, ok := props.(*customProps); ok {
+ if custProps.Arch_paths != nil {
+ paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, custProps.Arch_paths, custProps.Arch_paths_exclude))
+ }
+ if custProps.String_literal_prop != nil {
+ strAttr.SetSelectValue(axis, config, custProps.String_literal_prop)
+ }
+ }
+ }
+ }
+ productVariableProps := android.ProductVariableProperties(ctx, ctx.Module())
+ if props, ok := productVariableProps["String_literal_prop"]; ok {
+ for c, p := range props {
+ if val, ok := p.(*string); ok {
+ strAttr.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val)
}
}
}
@@ -305,10 +474,12 @@
paths.ResolveExcludes()
attrs := &customBazelModuleAttributes{
- String_ptr_prop: m.props.String_ptr_prop,
- String_list_prop: m.props.String_list_prop,
- Arch_paths: paths,
+ String_literal_prop: strAttr,
+ String_ptr_prop: m.props.String_ptr_prop,
+ String_list_prop: m.props.String_list_prop,
+ Arch_paths: paths,
}
+
attrs.Embedded_attr = m.props.Embedded_prop
if m.props.OtherEmbeddedProps != nil {
attrs.OtherEmbeddedAttr = &OtherEmbeddedAttr{Other_embedded_attr: m.props.OtherEmbeddedProps.Other_embedded_prop}
@@ -321,6 +492,23 @@
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
}
+var _ android.ApiProvider = (*customModule)(nil)
+
+func (c *customModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "custom_api_contribution",
+ }
+ apiAttribute := bazel.MakeLabelAttribute(
+ android.BazelLabelForModuleSrcSingle(ctx, proptools.String(c.props.Api)).Label,
+ )
+ attrs := &customBazelModuleAttributes{
+ Api: *apiAttribute,
+ }
+ ctx.CreateBazelTargetModule(props,
+ android.CommonAttributes{Name: c.Name()},
+ attrs)
+}
+
// A bp2build mutator that uses load statements and creates a 1:M mapping from
// module to target.
func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) {
@@ -351,11 +539,14 @@
func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) (BazelTargets, []error) {
// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
res, err := GenerateBazelTargets(codegenCtx, false)
+ if err != nil {
+ return BazelTargets{}, err
+ }
return res.buildFileToTargets[dir], err
}
func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
- ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
ctx.RegisterForBazelConversion()
}
@@ -367,15 +558,106 @@
}`, typ, name)
}
-type attrNameToString map[string]string
+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) {
+ if name != "" {
+ attrStrings = append(attrStrings, fmt.Sprintf(` name = "%s",`, name))
+ }
+ for _, k := range android.SortedKeys(attrs) {
attrStrings = append(attrStrings, fmt.Sprintf(" %s = %s,", k, attrs[k]))
}
return fmt.Sprintf(`%s(
%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)
+}
+
+type ExpectedRuleTarget struct {
+ Rule string
+ Name string
+ Attrs AttrNameToString
+ Hod android.HostOrDeviceSupported
+}
+
+func (ebr ExpectedRuleTarget) String() string {
+ return makeBazelTargetHostOrDevice(ebr.Rule, ebr.Name, ebr.Attrs, ebr.Hod)
+}
+
+func makeCcStubSuiteTargets(name string, attrs AttrNameToString) string {
+ if _, hasStubs := attrs["stubs_symbol_file"]; !hasStubs {
+ return ""
+ }
+ STUB_SUITE_ATTRS := map[string]string{
+ "stubs_symbol_file": "symbol_file",
+ "stubs_versions": "versions",
+ "soname": "soname",
+ "source_library_label": "source_library_label",
+ }
+
+ stubSuiteAttrs := AttrNameToString{}
+ for key, _ := range attrs {
+ if _, stubSuiteAttr := STUB_SUITE_ATTRS[key]; stubSuiteAttr {
+ stubSuiteAttrs[STUB_SUITE_ATTRS[key]] = attrs[key]
+ } else {
+ panic(fmt.Sprintf("unused cc_stub_suite attr %q\n", key))
+ }
+ }
+ return MakeBazelTarget("cc_stub_suite", name+"_stub_libs", stubSuiteAttrs)
+}
+
+func MakeNeverlinkDuplicateTarget(moduleType string, name string) string {
+ return MakeNeverlinkDuplicateTargetWithAttrs(moduleType, name, AttrNameToString{})
+}
+
+func MakeNeverlinkDuplicateTargetWithAttrs(moduleType string, name string, extraAttrs AttrNameToString) string {
+ attrs := extraAttrs
+ attrs["neverlink"] = `True`
+ attrs["exports"] = `[":` + name + `"]`
+ return MakeBazelTarget(moduleType, name+"-neverlink", attrs)
+}
+
+func getTargetName(targetContent string) string {
+ data := strings.Split(targetContent, "name = \"")
+ if len(data) < 2 {
+ return ""
+ } else {
+ endIndex := strings.Index(data[1], "\"")
+ return data[1][:endIndex]
+ }
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 14b2d84..45009c1 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -17,9 +17,14 @@
import (
"fmt"
"io"
+ "path/filepath"
+ "runtime"
"strings"
"android/soong/android"
+ "android/soong/bazel"
+ "android/soong/bazel/cquery"
+ "android/soong/cc"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -28,6 +33,7 @@
func init() {
registerBpfBuildComponents(android.InitRegistrationContext)
pctx.Import("android/soong/cc/config")
+ pctx.StaticVariable("relPwd", cc.PwdPrefix())
}
var (
@@ -37,7 +43,7 @@
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- Command: "$ccCmd --target=bpf -c $cFlags -MD -MF ${out}.d -o $out $in",
+ Command: "$relPwd $ccCmd --target=bpf -c $cFlags -MD -MF ${out}.d -o $out $in",
CommandDeps: []string{"$ccCmd"},
},
"ccCmd", "cFlags")
@@ -68,12 +74,23 @@
}
type BpfProperties struct {
- Srcs []string `android:"path"`
- Cflags []string
+ // source paths to the files.
+ Srcs []string `android:"path"`
+
+ // additional cflags that should be used to build the bpf variant of
+ // the C/C++ module.
+ Cflags []string
+
+ // directories (relative to the root of the source tree) that will
+ // be added to the include paths using -I.
Include_dirs []string
- Sub_dir string
- // If set to true, generate BTF debug info for maps & programs
- Btf *bool
+
+ // optional subdirectory under which this module is installed into.
+ Sub_dir string
+
+ // if set to true, generate BTF debug info for maps & programs.
+ Btf *bool
+
Vendor *bool
VendorInternal bool `blueprint:"mutated"`
@@ -81,6 +98,7 @@
type bpf struct {
android.ModuleBase
+ android.BazelModuleBase
properties BpfProperties
@@ -149,11 +167,17 @@
if proptools.Bool(bpf.properties.Btf) {
cflags = append(cflags, "-g")
+ if runtime.GOOS != "darwin" {
+ cflags = append(cflags, "-fdebug-prefix-map=/proc/self/cwd=")
+ }
}
srcs := android.PathsForModuleSrc(ctx, bpf.properties.Srcs)
for _, src := range srcs {
+ if strings.ContainsRune(filepath.Base(src.String()), '_') {
+ ctx.ModuleErrorf("invalid character '_' in source name")
+ }
obj := android.ObjPathWithExt(ctx, "unstripped", src, "o")
ctx.Build(pctx, android.BuildParams{
@@ -203,7 +227,7 @@
for _, obj := range bpf.objs {
objName := name + "_" + obj.Base()
names = append(names, objName)
- fmt.Fprintln(w, "include $(CLEAR_VARS)")
+ fmt.Fprintln(w, "include $(CLEAR_VARS)", " # bpf.bpf.obj")
fmt.Fprintln(w, "LOCAL_MODULE := ", objName)
data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String())
@@ -213,15 +237,44 @@
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
fmt.Fprintln(w)
}
- fmt.Fprintln(w, "include $(CLEAR_VARS)")
+ fmt.Fprintln(w, "include $(CLEAR_VARS)", " # bpf.bpf")
fmt.Fprintln(w, "LOCAL_MODULE := ", name)
data.Entries.WriteLicenseVariables(w)
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(names, " "))
+ android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", names)
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
},
}
}
+var _ android.MixedBuildBuildable = (*bpf)(nil)
+
+func (bpf *bpf) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+ return true
+}
+
+func (bpf *bpf) QueueBazelCall(ctx android.BaseModuleContext) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(
+ bpf.GetBazelLabel(ctx, bpf),
+ cquery.GetOutputFiles,
+ android.GetConfigKey(ctx))
+}
+
+func (bpf *bpf) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+ bazelCtx := ctx.Config().BazelContext
+ objPaths, err := bazelCtx.GetOutputFiles(bpf.GetBazelLabel(ctx, bpf), android.GetConfigKey(ctx))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
+ }
+
+ bazelOuts := android.Paths{}
+ for _, p := range objPaths {
+ bazelOuts = append(bazelOuts, android.PathForBazelOut(ctx, p))
+ }
+ bpf.objs = bazelOuts
+}
+
// Implements OutputFileFileProducer interface so that the obj output can be used in the data property
// of other modules.
func (bpf *bpf) OutputFiles(tag string) (android.Paths, error) {
@@ -245,5 +298,39 @@
module.AddProperties(&module.properties)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitBazelModule(module)
return module
}
+
+type bazelBpfAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Copts bazel.StringListAttribute
+ Absolute_includes bazel.StringListAttribute
+ Btf *bool
+ // TODO(b/249528391): Add support for sub_dir
+}
+
+// bpf bp2build converter
+func (b *bpf) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ if ctx.ModuleType() != "bpf" {
+ return
+ }
+
+ srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, b.properties.Srcs))
+ copts := bazel.MakeStringListAttribute(b.properties.Cflags)
+ absolute_includes := bazel.MakeStringListAttribute(b.properties.Include_dirs)
+ btf := b.properties.Btf
+
+ attrs := bazelBpfAttributes{
+ Srcs: srcs,
+ Copts: copts,
+ Absolute_includes: absolute_includes,
+ Btf: btf,
+ }
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "bpf",
+ Bzl_load_location: "//build/bazel/rules/bpf:bpf.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: b.Name()}, &attrs)
+}
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
index 51fbc15..a2010ff 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -30,8 +30,9 @@
cc.PrepareForTestWithCcDefaultModules,
android.FixtureMergeMockFs(
map[string][]byte{
- "bpf.c": nil,
- "BpfTest.cpp": nil,
+ "bpf.c": nil,
+ "bpf_invalid_name.c": nil,
+ "BpfTest.cpp": nil,
},
),
PrepareForTestWithBpf,
@@ -58,3 +59,38 @@
// value is not available for testing from this package.
// TODO(jungjw): Add a check for data or move this test to the cc package.
}
+
+func TestBpfSourceName(t *testing.T) {
+ bp := `
+ bpf {
+ name: "bpf_invalid_name.o",
+ srcs: ["bpf_invalid_name.c"],
+ }
+ `
+ prepareForBpfTest.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+ `\QAndroid.bp:2:3: module "bpf_invalid_name.o" variant "android_common": invalid character '_' in source name\E`)).
+ RunTestWithBp(t, bp)
+}
+
+func TestBpfWithBazel(t *testing.T) {
+ bp := `
+ bpf {
+ name: "bpf.o",
+ srcs: ["bpf.c"],
+ bazel_module: { label: "//bpf" },
+ }
+ `
+
+ result := android.GroupFixturePreparers(
+ prepareForBpfTest, android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToOutputFiles: map[string][]string{
+ "//bpf": []string{"bpf.o"}}}
+ })).RunTestWithBp(t, bp)
+
+ output := result.Module("bpf.o", "android_common").(*bpf)
+
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/bpf.o"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, output.objs.Strings())
+}
diff --git a/build_kzip.bash b/build_kzip.bash
index aff2d6d..eeef7d4 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,16 +44,23 @@
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
-# in the rules file. Generate this file on the fly with corpus value set from the
-# environment variable.
-for dir in blueprint soong; do
- (cd "build/$dir";
+# For the Go code, we invoke the extractor directly. The two caveats are that
+# the extractor's rewrite rules are generated on the fly as they depend on the
+# value of XREF_CORPUS, and that the name of the kzip file is derived from the
+# directory name by replacing '/' with '_'.
+# Go extractor should succeed.
+declare -ar go_modules=(build/blueprint build/soong
+ build/make/tools/canoninja build/make/tools/compliance build/make/tools/rbcrun)
+set -e
+for dir in "${go_modules[@]}"; do
+ (cd "$dir";
+ outfile=$(echo "$dir" | sed -r 's|/|_|g;s|(.*)|\1.go.kzip|');
KYTHE_ROOT_DIRECTORY="${source_root}" "$go_extractor" --goroot="$go_root" \
--rules=<(printf '[{"pattern": "(.*)","vname": {"path": "@1@", "corpus":"%s"}}]' "${XREF_CORPUS}") \
- --canonicalize_package_corpus --output "${abspath_out}/soong/build_${dir}.go.kzip" ./...
+ --canonicalize_package_corpus --output "${abspath_out}/soong/$outfile" ./...
)
done
+set +e
declare -r kzip_count=$(find "$out" -name '*.kzip' | wc -l)
(($kzip_count>100000)) || { printf "Too few kzip files were generated: %d\n" $kzip_count; exit 1; }
diff --git a/build_test.bash b/build_test.bash
index 1dc6660..defdd82 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -25,14 +25,24 @@
# Products that are broken or otherwise don't work with multiproduct_kati
SKIPPED_PRODUCTS=(
- # Both of these products are for soong-only builds, and will fail the kati stage.
+ # These products are for soong-only builds, and will fail the kati stage.
+ linux_bionic
mainline_sdk
ndk
+
+ # New architecture bringup, fails without ALLOW_MISSING_DEPENDENCIES=true
+ aosp_riscv64
)
-# To track how long we took to startup. %N isn't supported on Darwin, but
-# that's detected in the Go code, which skips calculating the startup time.
-export TRACE_BEGIN_SOONG=$(date +%s%N)
+# To track how long we took to startup.
+case $(uname -s) in
+ Darwin)
+ export TRACE_BEGIN_SOONG=`$T/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
+ ;;
+ *)
+ export TRACE_BEGIN_SOONG=$(date +%s%N)
+ ;;
+esac
# Remove BUILD_NUMBER so that incremental builds on build servers don't
# re-read makefiles every time.
@@ -44,8 +54,10 @@
case $(uname) in
Linux)
- export LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so
- export SEGFAULT_USE_ALTSTACK=1
+ if [[ -f /lib/x86_64-linux-gnu/libSegFault.so ]]; then
+ export LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so
+ export SEGFAULT_USE_ALTSTACK=1
+ fi
ulimit -a
;;
esac
@@ -58,7 +70,7 @@
echo
echo "Running Bazel smoke test..."
-STANDALONE_BAZEL=true "${TOP}/tools/bazel" --batch --max_idle_secs=1 info
+STANDALONE_BAZEL=true "${TOP}/build/bazel/bin/bazel" --batch --max_idle_secs=1 help
echo
echo "Running Soong test..."
diff --git a/cc/Android.bp b/cc/Android.bp
index 9103a48..be2cc5a 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -15,11 +15,14 @@
"soong-etc",
"soong-fuzz",
"soong-genrule",
+ "soong-multitree",
"soong-snapshot",
"soong-tradefed",
],
srcs: [
"afdo.go",
+ "fdo_profile.go",
+
"androidmk.go",
"api_level.go",
"bp2build.go",
@@ -51,7 +54,6 @@
"vndk.go",
"vndk_prebuilt.go",
- "cflag_artifacts.go",
"cmakelists.go",
"compdb.go",
"compiler.go",
@@ -65,6 +67,7 @@
"library.go",
"library_headers.go",
"library_sdk_member.go",
+ "library_stub.go",
"native_bridge_sdk_trait.go",
"object.go",
"test.go",
@@ -89,17 +92,23 @@
],
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",
+ "lto_test.go",
+ "ndk_test.go",
"object_test.go",
"prebuilt_test.go",
"proto_test.go",
"sanitize_test.go",
+ "sdk_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..c4d82d2 100644
--- a/cc/OWNERS
+++ b/cc/OWNERS
@@ -1,4 +1 @@
per-file ndk_*.go = danalbert@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 fb66bbe..137ea97 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -18,11 +18,13 @@
"fmt"
"strings"
- "github.com/google/blueprint/proptools"
-
"android/soong/android"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
+// TODO(b/267229066): Remove globalAfdoProfileProjects after implementing bp2build converter for fdo_profile
var (
globalAfdoProfileProjects = []string{
"vendor/google_data/pgo_profile/sampling/",
@@ -32,25 +34,25 @@
var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
-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 {
- return append(globalAfdoProfileProjects, config.AfdoAdditionalProfileDirs()...)
- })
-}
+const afdoCFlagsFormat = "-fprofile-sample-use=%s"
func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) {
getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
}
+type afdoRdep struct {
+ VariationName *string
+ ProfilePath *string
+}
+
type AfdoProperties struct {
// Afdo allows developers self-service enroll for
// automatic feedback-directed optimization using profile data.
Afdo bool
- AfdoTarget *string `blueprint:"mutated"`
- AfdoDeps []string `blueprint:"mutated"`
+ FdoProfilePath *string `blueprint:"mutated"`
+
+ AfdoRDeps []afdoRdep `blueprint:"mutated"`
}
type afdo struct {
@@ -61,79 +63,87 @@
return []interface{}{&afdo.Properties}
}
-func (afdo *afdo) AfdoEnabled() bool {
- return afdo != nil && afdo.Properties.Afdo && afdo.Properties.AfdoTarget != nil
-}
-
-// Get list of profile file names, ordered by level of specialisation. For example:
-// 1. libfoo_arm64.afdo
-// 2. libfoo.afdo
-//
-// Add more specialisation as needed.
-func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string {
- var files []string
- files = append(files, moduleName+"_"+ctx.Arch().ArchType.String()+".afdo")
- files = append(files, moduleName+".afdo")
- return files
-}
-
-func (props *AfdoProperties) GetAfdoProfileFile(ctx android.BaseModuleContext, module string) android.OptionalPath {
- // Test if the profile_file is present in any of the Afdo profile projects
- for _, profileFile := range getProfileFiles(ctx, module) {
- for _, profileProject := range getAfdoProfileProjects(ctx.DeviceConfig()) {
- path := android.ExistentPathForSource(ctx, profileProject, profileFile)
- if path.Valid() {
- return path
- }
- }
- }
-
- // Record that this module's profile file is absent
- missing := ctx.ModuleDir() + ":" + module
- recordMissingAfdoProfileFile(ctx, missing)
-
- return android.OptionalPathForPath(nil)
-}
-
-func (afdo *afdo) begin(ctx BaseModuleContext) {
- if ctx.Host() {
- return
- }
- if ctx.static() && !ctx.staticBinary() {
- return
- }
- if afdo.Properties.Afdo {
- module := ctx.ModuleName()
- if afdo.Properties.GetAfdoProfileFile(ctx, module).Valid() {
- afdo.Properties.AfdoTarget = proptools.StringPtr(module)
- }
- }
+// afdoEnabled returns true for binaries and shared libraries
+// that set afdo prop to True and there is a profile available
+func (afdo *afdo) afdoEnabled() bool {
+ return afdo != nil && afdo.Properties.Afdo
}
func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags {
- if profile := afdo.Properties.AfdoTarget; profile != nil {
- if profileFile := afdo.Properties.GetAfdoProfileFile(ctx, *profile); profileFile.Valid() {
- profileFilePath := profileFile.Path()
+ if afdo.Properties.Afdo {
+ // We use `-funique-internal-linkage-names` to associate profiles to the right internal
+ // functions. This option should be used before generating a profile. Because a profile
+ // generated for a binary without unique names doesn't work well building a binary with
+ // unique names (they have different internal function names).
+ // To avoid a chicken-and-egg problem, we enable `-funique-internal-linkage-names` when
+ // `afdo=true`, whether a profile exists or not.
+ // The profile can take effect in three steps:
+ // 1. Add `afdo: true` in Android.bp, and build the binary.
+ // 2. Collect an AutoFDO profile for the binary.
+ // 3. Make the profile searchable by the build system. So it's used the next time the binary
+ // is built.
+ flags.Local.CFlags = append([]string{"-funique-internal-linkage-names"}, flags.Local.CFlags...)
+ }
+ if path := afdo.Properties.FdoProfilePath; path != nil {
+ // The flags are prepended to allow overriding.
+ profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, *path)
+ flags.Local.CFlags = append([]string{profileUseFlag}, flags.Local.CFlags...)
+ flags.Local.LdFlags = append([]string{profileUseFlag, "-Wl,-mllvm,-no-warn-sample-unused=true"}, flags.Local.LdFlags...)
- profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, profileFile)
- flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlag)
- flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlag)
- flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true")
-
- // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
- // if profileFile gets updated
- flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath)
- flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath)
- }
+ // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
+ // if profileFile gets updated
+ pathForSrc := android.PathForSource(ctx, *path)
+ flags.CFlagsDeps = append(flags.CFlagsDeps, pathForSrc)
+ flags.LdFlagsDeps = append(flags.LdFlagsDeps, pathForSrc)
}
return flags
}
-// Propagate afdo requirements down from binaries
+func (afdo *afdo) addDep(ctx BaseModuleContext, actx android.BottomUpMutatorContext) {
+ if ctx.Host() {
+ return
+ }
+
+ if ctx.static() && !ctx.staticBinary() {
+ return
+ }
+
+ if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
+ if fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName()); fdoProfileName != nil && err == nil {
+ actx.AddFarVariationDependencies(
+ []blueprint.Variation{
+ {Mutator: "arch", Variation: actx.Target().ArchVariation()},
+ {Mutator: "os", Variation: "android"},
+ },
+ FdoProfileTag,
+ []string{*fdoProfileName}...,
+ )
+ }
+ }
+}
+
+// FdoProfileMutator reads the FdoProfileProvider from a direct dep with FdoProfileTag
+// assigns FdoProfileInfo.Path to the FdoProfilePath mutated property
+func (c *Module) fdoProfileMutator(ctx android.BottomUpMutatorContext) {
+ if !c.Enabled() {
+ return
+ }
+
+ ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) {
+ if ctx.OtherModuleHasProvider(m, FdoProfileProvider) {
+ info := ctx.OtherModuleProvider(m, FdoProfileProvider).(FdoProfileInfo)
+ c.afdo.Properties.FdoProfilePath = proptools.StringPtr(info.Path.String())
+ }
+ })
+}
+
+var _ FdoProfileMutatorInterface = (*Module)(nil)
+
+// Propagate afdo requirements down from binaries and shared libraries
func afdoDepsMutator(mctx android.TopDownMutatorContext) {
- if m, ok := mctx.Module().(*Module); ok && m.afdo.AfdoEnabled() {
- afdoTarget := *m.afdo.Properties.AfdoTarget
+ if m, ok := mctx.Module().(*Module); ok && m.afdo.afdoEnabled() {
+ path := m.afdo.Properties.FdoProfilePath
mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
tag := mctx.OtherModuleDependencyTag(dep)
libTag, isLibTag := tag.(libraryDependencyTag)
@@ -150,7 +160,13 @@
}
if dep, ok := dep.(*Module); ok {
- dep.afdo.Properties.AfdoDeps = append(dep.afdo.Properties.AfdoDeps, afdoTarget)
+ dep.afdo.Properties.AfdoRDeps = append(
+ dep.afdo.Properties.AfdoRDeps,
+ afdoRdep{
+ VariationName: proptools.StringPtr(encodeTarget(m.Name())),
+ ProfilePath: path,
+ },
+ )
}
return true
@@ -161,16 +177,30 @@
// Create afdo variants for modules that need them
func afdoMutator(mctx android.BottomUpMutatorContext) {
if m, ok := mctx.Module().(*Module); ok && m.afdo != nil {
- if m.afdo.AfdoEnabled() && !m.static() {
- afdoTarget := *m.afdo.Properties.AfdoTarget
- mctx.SetDependencyVariation(encodeTarget(afdoTarget))
+ if !m.static() && m.afdo.Properties.Afdo {
+ mctx.SetDependencyVariation(encodeTarget(m.Name()))
+ return
}
variationNames := []string{""}
- afdoDeps := android.FirstUniqueStrings(m.afdo.Properties.AfdoDeps)
- for _, dep := range afdoDeps {
- variationNames = append(variationNames, encodeTarget(dep))
+
+ variantNameToProfilePath := make(map[string]*string)
+
+ for _, afdoRDep := range m.afdo.Properties.AfdoRDeps {
+ variantName := *afdoRDep.VariationName
+ // An rdep can be set twice in AfdoRDeps because there can be
+ // more than one path from an afdo-enabled module to
+ // a static dep such as
+ // afdo_enabled_foo -> static_bar ----> static_baz
+ // \ ^
+ // ----------------------|
+ // We only need to create one variant per unique rdep
+ if _, exists := variantNameToProfilePath[variantName]; !exists {
+ variationNames = append(variationNames, variantName)
+ variantNameToProfilePath[variantName] = afdoRDep.ProfilePath
+ }
}
+
if len(variationNames) > 1 {
modules := mctx.CreateVariations(variationNames...)
for i, name := range variationNames {
@@ -180,7 +210,8 @@
variation := modules[i].(*Module)
variation.Properties.PreventInstall = true
variation.Properties.HideFromMake = true
- variation.afdo.Properties.AfdoTarget = proptools.StringPtr(decodeTarget(name))
+ variation.afdo.Properties.Afdo = true
+ variation.afdo.Properties.FdoProfilePath = variantNameToProfilePath[name]
}
}
}
diff --git a/cc/afdo_test.go b/cc/afdo_test.go
index 5515464..b250ad1 100644
--- a/cc/afdo_test.go
+++ b/cc/afdo_test.go
@@ -15,56 +15,449 @@
package cc
import (
+ "strings"
"testing"
"android/soong/android"
+
"github.com/google/blueprint"
)
+type visitDirectDepsInterface interface {
+ VisitDirectDeps(blueprint.Module, func(dep blueprint.Module))
+}
+
+func hasDirectDep(ctx visitDirectDepsInterface, m android.Module, wantDep android.Module) bool {
+ var found bool
+ ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+ if dep == wantDep {
+ found = true
+ }
+ })
+ return found
+}
+
func TestAfdoDeps(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "libTest",
+ srcs: ["test.c"],
+ static_libs: ["libFoo"],
+ afdo: true,
+ }
+
+ cc_library_static {
+ name: "libFoo",
+ srcs: ["foo.c"],
+ static_libs: ["libBar"],
+ }
+
+ cc_library_static {
+ name: "libBar",
+ srcs: ["bar.c"],
+ }
+ `
+
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithFdoProfile,
+ prepareForCcTest,
+ android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AfdoProfiles = []string{
+ "libTest://afdo_profiles_package:libTest_afdo",
+ }
+ }),
+ android.MockFS{
+ "afdo_profiles_package/Android.bp": []byte(`
+ fdo_profile {
+ name: "libTest_afdo",
+ profile: "libTest.afdo",
+ }
+ `),
+ }.AddToFixture(),
+ ).RunTestWithBp(t, bp)
+
+ expectedCFlag := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo"
+
+ libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
+ libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
+ libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest")
+
+ // Check cFlags of afdo-enabled module and the afdo-variant of its static deps
+ cFlags := libTest.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+ }
+
+ cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+ }
+
+ cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+ }
+
+ // Check dependency edge from afdo-enabled module to static deps
+ if !hasDirectDep(result, libTest.Module(), libFooAfdoVariant.Module()) {
+ t.Errorf("libTest missing dependency on afdo variant of libFoo")
+ }
+
+ if !hasDirectDep(result, libFooAfdoVariant.Module(), libBarAfdoVariant.Module()) {
+ t.Errorf("libTest missing dependency on afdo variant of libBar")
+ }
+
+ // Verify non-afdo variant exists and doesn't contain afdo
+ libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
+ libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
+
+ cFlags = libFoo.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+ }
+ cFlags = libBar.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+ }
+
+ // Check dependency edges of static deps
+ if hasDirectDep(result, libTest.Module(), libFoo.Module()) {
+ t.Errorf("libTest should not depend on non-afdo variant of libFoo")
+ }
+
+ if !hasDirectDep(result, libFoo.Module(), libBar.Module()) {
+ t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
+ }
+}
+
+func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "libTest",
+ srcs: ["foo.c"],
+ static_libs: ["libFoo"],
+ }
+
+ cc_library_static {
+ name: "libFoo",
+ srcs: ["foo.c"],
+ static_libs: ["libBar"],
+ afdo: true, // TODO(b/256670524): remove support for enabling afdo from static only libraries, this can only propagate from shared libraries/binaries
+ }
+
+ cc_library_static {
+ name: "libBar",
+ }
+ `
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ PrepareForTestWithFdoProfile,
+ android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libFoo.afdo", ""),
+ android.MockFS{
+ "afdo_profiles_package/Android.bp": []byte(`
+ soong_namespace {
+ }
+ fdo_profile {
+ name: "libFoo_afdo",
+ profile: "libFoo.afdo",
+ }
+ `),
+ }.AddToFixture(),
+ ).RunTestWithBp(t, bp)
+
+ libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared").Module()
+ libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
+ libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static").Module()
+
+ if !hasDirectDep(result, libTest, libFoo.Module()) {
+ t.Errorf("libTest missing dependency on afdo variant of libFoo")
+ }
+
+ if !hasDirectDep(result, libFoo.Module(), libBar) {
+ t.Errorf("libFoo missing dependency on afdo variant of libBar")
+ }
+
+ fooVariants := result.ModuleVariantsForTests("foo")
+ for _, v := range fooVariants {
+ if strings.Contains(v, "afdo-") {
+ t.Errorf("Expected no afdo variant of 'foo', got %q", v)
+ }
+ }
+
+ cFlags := libFoo.Rule("cc").Args["cFlags"]
+ if w := "-fprofile-sample-accurate"; strings.Contains(cFlags, w) {
+ t.Errorf("Expected 'foo' to not enable afdo, but found %q in cflags %q", w, cFlags)
+ }
+
+ barVariants := result.ModuleVariantsForTests("bar")
+ for _, v := range barVariants {
+ if strings.Contains(v, "afdo-") {
+ t.Errorf("Expected no afdo variant of 'bar', got %q", v)
+ }
+ }
+}
+
+func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) {
bp := `
cc_library {
name: "libTest",
srcs: ["foo.c"],
- static_libs: ["libFoo"],
+ runtime_libs: ["libFoo"],
afdo: true,
}
cc_library {
name: "libFoo",
- static_libs: ["libBar"],
- }
-
- cc_library {
- name: "libBar",
}
`
- prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST")
result := android.GroupFixturePreparers(
prepareForCcTest,
- prepareForAfdoTest,
+ PrepareForTestWithFdoProfile,
+ android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AfdoProfiles = []string{
+ "libTest://afdo_profiles_package:libTest_afdo",
+ }
+ }),
+ android.MockFS{
+ "afdo_profiles_package/Android.bp": []byte(`
+ fdo_profile {
+ name: "libTest_afdo",
+ profile: "libTest.afdo",
+ }
+ `),
+ }.AddToFixture(),
).RunTestWithBp(t, bp)
- libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared").Module()
- libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest").Module()
- libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest").Module()
+ libFooVariants := result.ModuleVariantsForTests("libFoo")
+ for _, v := range libFooVariants {
+ if strings.Contains(v, "afdo-") {
+ t.Errorf("Expected no afdo variant of 'foo', got %q", v)
+ }
+ }
+}
- hasDep := func(m android.Module, wantDep android.Module) bool {
- var found bool
- result.VisitDirectDeps(m, func(dep blueprint.Module) {
- if dep == wantDep {
- found = true
+func TestAfdoEnabledWithMultiArchs(t *testing.T) {
+ bp := `
+ cc_library_shared {
+ name: "foo",
+ srcs: ["test.c"],
+ afdo: true,
+ compile_multilib: "both",
+ }
+`
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithFdoProfile,
+ prepareForCcTest,
+ android.FixtureAddTextFile("afdo_profiles_package/foo_arm.afdo", ""),
+ android.FixtureAddTextFile("afdo_profiles_package/foo_arm64.afdo", ""),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AfdoProfiles = []string{
+ "foo://afdo_profiles_package:foo_afdo",
}
- })
- return found
+ }),
+ android.MockFS{
+ "afdo_profiles_package/Android.bp": []byte(`
+ soong_namespace {
+ }
+ fdo_profile {
+ name: "foo_afdo",
+ arch: {
+ arm: {
+ profile: "foo_arm.afdo",
+ },
+ arm64: {
+ profile: "foo_arm64.afdo",
+ }
+ }
+ }
+ `),
+ }.AddToFixture(),
+ ).RunTestWithBp(t, bp)
+
+ fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared")
+ fooArmCFlags := fooArm.Rule("cc").Args["cFlags"]
+ if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm.afdo"; !strings.Contains(fooArmCFlags, w) {
+ t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArmCFlags)
}
- if !hasDep(libTest, libFoo) {
+ fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a_shared")
+ fooArm64CFlags := fooArm64.Rule("cc").Args["cFlags"]
+ if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm64.afdo"; !strings.Contains(fooArm64CFlags, w) {
+ t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArm64CFlags)
+ }
+}
+
+func TestMultipleAfdoRDeps(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "libTest",
+ srcs: ["test.c"],
+ static_libs: ["libFoo"],
+ afdo: true,
+ }
+
+ cc_library_shared {
+ name: "libBar",
+ srcs: ["bar.c"],
+ static_libs: ["libFoo"],
+ afdo: true,
+ }
+
+ cc_library_static {
+ name: "libFoo",
+ srcs: ["foo.c"],
+ }
+ `
+
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithFdoProfile,
+ prepareForCcTest,
+ android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""),
+ android.FixtureAddTextFile("afdo_profiles_package/libBar.afdo", ""),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AfdoProfiles = []string{
+ "libTest://afdo_profiles_package:libTest_afdo",
+ "libBar://afdo_profiles_package:libBar_afdo",
+ }
+ }),
+ android.MockFS{
+ "afdo_profiles_package/Android.bp": []byte(`
+ fdo_profile {
+ name: "libTest_afdo",
+ profile: "libTest.afdo",
+ }
+ fdo_profile {
+ name: "libBar_afdo",
+ profile: "libBar.afdo",
+ }
+ `),
+ }.AddToFixture(),
+ ).RunTestWithBp(t, bp)
+
+ expectedCFlagLibTest := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo"
+ expectedCFlagLibBar := "-fprofile-sample-use=afdo_profiles_package/libBar.afdo"
+
+ libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
+ libFooAfdoVariantWithLibTest := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
+
+ libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_shared")
+ libFooAfdoVariantWithLibBar := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libBar")
+
+ // Check cFlags of afdo-enabled module and the afdo-variant of its static deps
+ cFlags := libTest.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlagLibTest) {
+ t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags)
+ }
+ cFlags = libBar.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlagLibBar) {
+ t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags)
+ }
+
+ cFlags = libFooAfdoVariantWithLibTest.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlagLibTest) {
+ t.Errorf("Expected 'libFooAfdoVariantWithLibTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags)
+ }
+
+ cFlags = libFooAfdoVariantWithLibBar.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlagLibBar) {
+ t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags)
+ }
+
+ // Check dependency edges of static deps
+ if !hasDirectDep(result, libTest.Module(), libFooAfdoVariantWithLibTest.Module()) {
t.Errorf("libTest missing dependency on afdo variant of libFoo")
}
- if !hasDep(libFoo, libBar) {
+ if !hasDirectDep(result, libBar.Module(), libFooAfdoVariantWithLibBar.Module()) {
+ t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
+ }
+}
+
+func TestAfdoDepsWithoutProfile(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "libTest",
+ srcs: ["test.c"],
+ static_libs: ["libFoo"],
+ afdo: true,
+ }
+
+ cc_library_static {
+ name: "libFoo",
+ srcs: ["foo.c"],
+ static_libs: ["libBar"],
+ }
+
+ cc_library_static {
+ name: "libBar",
+ srcs: ["bar.c"],
+ }
+ `
+
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithFdoProfile,
+ prepareForCcTest,
+ ).RunTestWithBp(t, bp)
+
+ // Even without a profile path, the afdo enabled libraries should be built with
+ // -funique-internal-linkage-names.
+ expectedCFlag := "-funique-internal-linkage-names"
+
+ libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
+ libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
+ libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest")
+
+ // Check cFlags of afdo-enabled module and the afdo-variant of its static deps
+ cFlags := libTest.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+ }
+
+ cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+ }
+
+ cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"]
+ if !strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+ }
+ // Check dependency edge from afdo-enabled module to static deps
+ if !hasDirectDep(result, libTest.Module(), libFooAfdoVariant.Module()) {
+ t.Errorf("libTest missing dependency on afdo variant of libFoo")
+ }
+
+ if !hasDirectDep(result, libFooAfdoVariant.Module(), libBarAfdoVariant.Module()) {
t.Errorf("libTest missing dependency on afdo variant of libBar")
}
+
+ // Verify non-afdo variant exists and doesn't contain afdo
+ libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
+ libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
+
+ cFlags = libFoo.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+ }
+ cFlags = libBar.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, expectedCFlag) {
+ t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+ }
+
+ // Check dependency edges of static deps
+ if hasDirectDep(result, libTest.Module(), libFoo.Module()) {
+ t.Errorf("libTest should not depend on non-afdo variant of libFoo")
+ }
+
+ if !hasDirectDep(result, libFoo.Module(), libBar.Module()) {
+ t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
+ }
}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ff5ba45..980dd07 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -23,6 +23,7 @@
"strings"
"android/soong/android"
+ "android/soong/multitree"
)
var (
@@ -123,14 +124,17 @@
}
}
}
- if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
+ if c.Properties.IsSdkVariant {
// Make the SDK variant uninstallable so that there are not two rules to install
// to the same location.
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
- // Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
- // dependencies to the .sdk suffix when building a module that uses the SDK.
- entries.SetString("SOONG_SDK_VARIANT_MODULES",
- "$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
+
+ if c.Properties.SdkAndPlatformVariantVisibleToMake {
+ // Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
+ // dependencies to the .sdk suffix when building a module that uses the SDK.
+ entries.SetString("SOONG_SDK_VARIANT_MODULES",
+ "$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
+ }
}
},
},
@@ -227,15 +231,15 @@
}
func (library *libraryDecorator) androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries *android.AndroidMkEntries) {
- if library.sAbiDiff.Valid() && !library.static() {
- entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", library.sAbiDiff.String())
+ if !library.static() {
+ entries.AddPaths("LOCAL_ADDITIONAL_DEPENDENCIES", library.sAbiDiff)
}
}
// TODO(ccross): remove this once apex/androidmk.go is converted to AndroidMkEntries
func (library *libraryDecorator) androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer) {
- if library.sAbiDiff.Valid() && !library.static() {
- fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", library.sAbiDiff.String())
+ if !library.static() {
+ fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", strings.Join(library.sAbiDiff.Strings(), " "))
}
}
@@ -401,14 +405,13 @@
entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
}
entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", test.Properties.Test_mainline_modules...)
- if Bool(test.Properties.Test_options.Unit_test) {
- entries.SetBool("LOCAL_IS_UNIT_TEST", true)
- }
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...)
}
+
+ test.Properties.Test_options.CommonTestOptions.SetAndroidMkEntries(entries)
})
AndroidMkWriteTestData(test.data, entries)
@@ -530,13 +533,15 @@
entries.SubName = ""
- if c.sanitizerProperties.CfiEnabled {
+ if c.isSanitizerEnabled(cfi) {
entries.SubName += ".cfi"
+ } else if c.isSanitizerEnabled(Hwasan) {
+ entries.SubName += ".hwasan"
}
entries.SubName += c.baseProperties.Androidmk_suffix
- entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
c.libraryDecorator.androidMkWriteExportedFlags(entries)
if c.shared() || c.static() {
@@ -557,6 +562,10 @@
if c.tocFile.Valid() {
entries.SetString("LOCAL_SOONG_TOC", c.tocFile.String())
}
+
+ if c.shared() && len(c.Properties.Overrides) > 0 {
+ entries.SetString("LOCAL_OVERRIDES_MODULES", strings.Join(makeOverrideModuleNames(ctx, c.Properties.Overrides), " "))
+ }
}
if !c.shared() { // static or header
@@ -616,6 +625,34 @@
androidMkWriteAllowUndefinedSymbols(p.baseLinker, entries)
}
+func (a *apiLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+ entries.Class = "SHARED_LIBRARIES"
+ entries.SubName += multitree.GetApiImportSuffix()
+
+ entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ a.libraryDecorator.androidMkWriteExportedFlags(entries)
+ src := *a.properties.Src
+ path, file := filepath.Split(src)
+ stem, suffix, ext := android.SplitFileExt(file)
+ entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
+ entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
+ entries.SetString("LOCAL_MODULE_PATH", path)
+ entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+ entries.SetString("LOCAL_SOONG_TOC", a.toc().String())
+ })
+}
+
+func (a *apiHeadersDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+ entries.Class = "HEADER_LIBRARIES"
+ entries.SubName += multitree.GetApiImportSuffix()
+
+ entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ a.libraryDecorator.androidMkWriteExportedFlags(entries)
+ entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+ })
+}
+
func androidMkWriteAllowUndefinedSymbols(linker *baseLinker, entries *android.AndroidMkEntries) {
allow := linker.Properties.Allow_undefined_symbols
if allow != nil {
diff --git a/cc/api_level.go b/cc/api_level.go
index 8c2b2c2..a5571f3 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -20,7 +20,9 @@
"android/soong/android"
)
-func minApiForArch(ctx android.BaseModuleContext,
+// MinApiLevelForArch returns the ApiLevel for the Android version that
+// first supported the architecture.
+func MinApiForArch(ctx android.EarlyModuleContext,
arch android.ArchType) android.ApiLevel {
switch arch {
@@ -38,7 +40,7 @@
func nativeApiLevelFromUser(ctx android.BaseModuleContext,
raw string) (android.ApiLevel, error) {
- min := minApiForArch(ctx, ctx.Arch().ArchType)
+ min := MinApiForArch(ctx, ctx.Arch().ArchType)
if raw == "minimum" {
return min, nil
}
diff --git a/cc/binary.go b/cc/binary.go
index c5017c1..097f822 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -17,6 +17,8 @@
import (
"path/filepath"
+ "android/soong/bazel/cquery"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -149,7 +151,7 @@
// modules common to most binaries, such as bionic libraries.
func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
deps = binary.baseLinker.linkerDeps(ctx, deps)
- if !Bool(binary.baseLinker.Properties.Nocrt) {
+ if binary.baseLinker.Properties.crt() {
if binary.static() {
deps.CrtBegin = ctx.toolchain().CrtBeginStaticBinary()
deps.CrtEnd = ctx.toolchain().CrtEndStaticBinary()
@@ -182,7 +184,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")
}
@@ -510,7 +512,7 @@
}
binary.baseInstaller.subDir = "bootstrap"
}
- binary.baseInstaller.install(ctx, file)
+ binary.baseInstaller.installExecutable(ctx, file)
var preferredArchSymlinkPath android.OptionalPath
for _, symlink := range binary.symlinks {
@@ -538,6 +540,12 @@
return binary.toolPath
}
+func (binary *binaryDecorator) overriddenModules() []string {
+ return binary.Properties.Overrides
+}
+
+var _ overridable = (*binaryDecorator)(nil)
+
func init() {
pctx.HostBinToolVariable("verifyHostBionicCmd", "host_bionic_verify")
}
@@ -562,28 +570,36 @@
}
type ccBinaryBazelHandler struct {
- android.BazelHandler
-
module *Module
}
-func (handler *ccBinaryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+var _ BazelHandler = (*ccBinaryBazelHandler)(nil)
+
+func (handler *ccBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
- filePaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
- if ok {
- if len(filePaths) != 1 {
- ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, filePaths)
- return false
- }
- 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
- }
- return ok
+ bazelCtx.QueueBazelRequest(label, cquery.GetCcUnstrippedInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
}
-func binaryBp2build(ctx android.TopDownMutatorContext, m *Module, typ string) {
+func (handler *ccBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ info, err := bazelCtx.GetCcUnstrippedInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
+ }
+
+ var outputFilePath android.Path = android.PathForBazelOut(ctx, info.OutputFile)
+ if len(info.TidyFiles) > 0 {
+ handler.module.tidyFiles = android.PathsForBazelOut(ctx, info.TidyFiles)
+ outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+ }
+ handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+ handler.module.linker.(*binaryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+ handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
+}
+
+func binaryBp2buildAttrs(ctx android.TopDownMutatorContext, m *Module) binaryAttributes {
baseAttrs := bp2BuildParseBaseProps(ctx, m)
binaryLinkerAttrs := bp2buildBinaryLinkerProps(ctx, m)
@@ -593,7 +609,7 @@
baseAttrs.implementationDeps.Add(baseAttrs.protoDependency)
}
- attrs := &binaryAttributes{
+ attrs := binaryAttributes{
binaryLinkerAttrs: binaryLinkerAttrs,
Srcs: baseAttrs.srcs,
@@ -609,12 +625,11 @@
Dynamic_deps: baseAttrs.implementationDynamicDeps,
Whole_archive_deps: baseAttrs.wholeArchiveDeps,
System_deps: baseAttrs.systemDynamicDeps,
+ Runtime_deps: baseAttrs.runtimeDeps,
Local_includes: baseAttrs.localIncludes,
Absolute_includes: baseAttrs.absoluteIncludes,
Linkopts: baseAttrs.linkopts,
- Link_crt: baseAttrs.linkCrt,
- Use_libcrt: baseAttrs.useLibcrt,
Use_version_lib: baseAttrs.useVersionLib,
Rtti: baseAttrs.rtti,
Stl: baseAttrs.stl,
@@ -633,14 +648,26 @@
Features: baseAttrs.features,
sdkAttributes: bp2BuildParseSdkAttributes(m),
+
+ Native_coverage: baseAttrs.Native_coverage,
}
+ m.convertTidyAttributes(ctx, &attrs.tidyAttributes)
+
+ return attrs
+}
+
+func binaryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+ // shared with cc_test
+ binaryAttrs := binaryBp2buildAttrs(ctx, m)
+
+ tags := android.ApexAvailableTags(m)
ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
Rule_class: "cc_binary",
Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
},
- android.CommonAttributes{Name: m.Name()},
- attrs)
+ android.CommonAttributes{Name: m.Name(), Tags: tags},
+ &binaryAttrs)
}
// binaryAttributes contains Bazel attributes corresponding to a cc binary
@@ -659,16 +686,14 @@
Dynamic_deps bazel.LabelListAttribute
Whole_archive_deps bazel.LabelListAttribute
System_deps bazel.LabelListAttribute
+ Runtime_deps bazel.LabelListAttribute
Local_includes bazel.StringListAttribute
Absolute_includes bazel.StringListAttribute
Linkopts bazel.StringListAttribute
Additional_linker_inputs bazel.LabelListAttribute
-
- Link_crt bazel.BoolAttribute
- Use_libcrt bazel.BoolAttribute
- Use_version_lib bazel.BoolAttribute
+ Use_version_lib bazel.BoolAttribute
Rtti bazel.BoolAttribute
Stl *string
@@ -679,4 +704,8 @@
Features bazel.StringListAttribute
sdkAttributes
+
+ tidyAttributes
+
+ Native_coverage *bool
}
diff --git a/cc/binary_test.go b/cc/binary_test.go
index 8ec3871..2fac002 100644
--- a/cc/binary_test.go
+++ b/cc/binary_test.go
@@ -17,10 +17,13 @@
import (
"testing"
+ "android/soong/bazel/cquery"
+
"android/soong/android"
)
func TestCcBinaryWithBazel(t *testing.T) {
+ t.Parallel()
bp := `
cc_binary {
name: "foo",
@@ -30,8 +33,11 @@
config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.BazelContext = android.MockBazelContext{
OutputBaseDir: "outputbase",
- LabelToOutputFiles: map[string][]string{
- "//foo/bar:bar": []string{"foo"},
+ LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+ "//foo/bar:bar": cquery.CcUnstrippedInfo{
+ OutputFile: "foo",
+ UnstrippedOutput: "foo.unstripped",
+ },
},
}
ctx := testCcWithConfig(t, config)
@@ -46,6 +52,71 @@
android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
- expectedUnStrippedFile := "outputbase/execroot/__main__/foo"
+ expectedUnStrippedFile := "outputbase/execroot/__main__/foo.unstripped"
android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, binMod)[0]
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_binary", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+}
+
+func TestCcBinaryWithBazelValidations(t *testing.T) {
+ t.Parallel()
+ bp := `
+cc_binary {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+ tidy: true,
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+ "//foo/bar:bar": cquery.CcUnstrippedInfo{
+ OutputFile: "foo",
+ UnstrippedOutput: "foo.unstripped",
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ },
+ }
+ ctx := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureMergeEnv(map[string]string{
+ "ALLOW_LOCAL_TIDY_TRUE": "1",
+ }),
+ ).RunTestWithConfig(t, config).TestContext
+
+ 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{"out/soong/.intermediates/foo/android_arm64_armv8-a/validated/foo"}
+ android.AssertPathsRelativeToTopEquals(t, "output files", expectedOutputFiles, outputFiles)
+
+ unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
+ expectedUnStrippedFile := "outputbase/execroot/__main__/foo.unstripped"
+ android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
+}
+
+func TestBinaryLinkerScripts(t *testing.T) {
+ t.Parallel()
+ 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 cc378b3..adf5a08 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -20,6 +20,7 @@
"android/soong/android"
"android/soong/bazel"
+ "android/soong/cc/config"
"github.com/google/blueprint"
@@ -27,20 +28,28 @@
)
const (
- cSrcPartition = "c"
- asSrcPartition = "as"
- cppSrcPartition = "cpp"
- protoSrcPartition = "proto"
+ cSrcPartition = "c"
+ asSrcPartition = "as"
+ asmSrcPartition = "asm"
+ lSrcPartition = "l"
+ llSrcPartition = "ll"
+ cppSrcPartition = "cpp"
+ protoSrcPartition = "proto"
+ aidlSrcPartition = "aidl"
+ syspropSrcPartition = "sysprop"
+
+ stubsSuffix = "_stub_libs_current"
)
// 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 {
- Srcs bazel.LabelListAttribute
- Srcs_c bazel.LabelListAttribute
- Srcs_as bazel.LabelListAttribute
- Hdrs bazel.LabelListAttribute
- Copts bazel.StringListAttribute
+ Srcs bazel.LabelListAttribute
+ Srcs_c bazel.LabelListAttribute
+ Srcs_as bazel.LabelListAttribute
+ Srcs_aidl bazel.LabelListAttribute
+ Hdrs bazel.LabelListAttribute
+ Copts bazel.StringListAttribute
Deps bazel.LabelListAttribute
Implementation_deps bazel.LabelListAttribute
@@ -48,12 +57,59 @@
Implementation_dynamic_deps bazel.LabelListAttribute
Whole_archive_deps bazel.LabelListAttribute
Implementation_whole_archive_deps bazel.LabelListAttribute
+ Runtime_deps bazel.LabelListAttribute
System_dynamic_deps bazel.LabelListAttribute
Enabled bazel.BoolAttribute
+ Native_coverage *bool
+
+ Apex_available []string
+
sdkAttributes
+
+ tidyAttributes
+}
+
+type tidyAttributes struct {
+ Tidy *string
+ Tidy_flags []string
+ Tidy_checks []string
+ Tidy_checks_as_errors []string
+ Tidy_disabled_srcs bazel.LabelListAttribute
+ Tidy_timeout_srcs bazel.LabelListAttribute
+}
+
+func (m *Module) convertTidyAttributes(ctx android.BaseMutatorContext, moduleAttrs *tidyAttributes) {
+ for _, f := range m.features {
+ if tidy, ok := f.(*tidyFeature); ok {
+ var tidyAttr *string
+ if tidy.Properties.Tidy != nil {
+ if *tidy.Properties.Tidy {
+ tidyAttr = proptools.StringPtr("local")
+ } else {
+ tidyAttr = proptools.StringPtr("never")
+ }
+ }
+ moduleAttrs.Tidy = tidyAttr
+ moduleAttrs.Tidy_flags = tidy.Properties.Tidy_flags
+ moduleAttrs.Tidy_checks = tidy.Properties.Tidy_checks
+ moduleAttrs.Tidy_checks_as_errors = tidy.Properties.Tidy_checks_as_errors
+ }
+
+ }
+ archVariantProps := m.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
+ for axis, configToProps := range archVariantProps {
+ for cfg, _props := range configToProps {
+ if archProps, ok := _props.(*BaseCompilerProperties); ok {
+ archDisabledSrcs := android.BazelLabelForModuleSrc(ctx, archProps.Tidy_disabled_srcs)
+ moduleAttrs.Tidy_disabled_srcs.SetSelectValue(axis, cfg, archDisabledSrcs)
+ archTimeoutSrcs := android.BazelLabelForModuleSrc(ctx, archProps.Tidy_timeout_srcs)
+ moduleAttrs.Tidy_timeout_srcs.SetSelectValue(axis, cfg, archTimeoutSrcs)
+ }
+ }
+ }
}
// groupSrcsByExtension partitions `srcs` into groups based on file extension.
@@ -61,10 +117,17 @@
// 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)
+ return func(otherModuleCtx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+
+ m, exists := otherModuleCtx.ModuleFromName(label.OriginalModuleName)
labelStr := label.Label
- if !exists || !android.IsFilegroup(ctx, m) {
+ if !exists || !android.IsFilegroup(otherModuleCtx, m) {
+ return labelStr, false
+ }
+ // If the filegroup is already converted to aidl_library or proto_library,
+ // skip creating _c_srcs, _as_srcs, _cpp_srcs filegroups
+ fg, _ := m.(android.FileGroupAsLibrary)
+ if fg.ShouldConvertToAidlLibrary(ctx) || fg.ShouldConvertToProtoLibrary(ctx) {
return labelStr, false
}
return labelStr + suffix, true
@@ -76,9 +139,18 @@
protoSrcPartition: android.ProtoSrcLabelPartition,
cSrcPartition: bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
asSrcPartition: bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
+ asmSrcPartition: bazel.LabelPartition{Extensions: []string{".asm"}},
+ aidlSrcPartition: android.AidlSrcLabelPartition,
+ // 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},
+ cppSrcPartition: bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
+ syspropSrcPartition: bazel.LabelPartition{Extensions: []string{".sysprop"}},
}
return bazel.PartitionLabelListAttribute(ctx, &srcs, labels)
@@ -141,6 +213,14 @@
}
}
+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 cfg, props := range configToProps {
+ parseFunc(axis, cfg, props)
+ }
+ }
+}
+
// 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{}
@@ -166,22 +246,21 @@
// empty list -> no values specified
attrs.System_dynamic_deps.ForceSpecifyEmptyList = true
+ var apexAvailable []string
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)
- }
+ apexAvailable = lib.StaticProperties.Static.Apex_available
+ 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)
- }
+ apexAvailable = lib.SharedProperties.Shared.Apex_available
+ 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)
@@ -189,6 +268,8 @@
attrs.Srcs_c = partitionedSrcs[cSrcPartition]
attrs.Srcs_as = partitionedSrcs[asSrcPartition]
+ attrs.Apex_available = android.ConvertApexAvailableToTags(apexAvailable)
+
if !partitionedSrcs[protoSrcPartition].IsEmpty() {
// TODO(b/208815215): determine whether this is used and add support if necessary
ctx.ModuleErrorf("Migrating static/shared only proto srcs is not currently supported")
@@ -203,32 +284,30 @@
Enabled bazel.BoolAttribute
}
+func parseSrc(ctx android.BazelConversionPathContext, srcLabelAttribute *bazel.LabelAttribute, axis bazel.ConfigurationAxis, config string, srcs []string) {
+ srcFileError := func() {
+ ctx.ModuleErrorf("parseSrc: Expected at most one source file for %s %s\n", axis, config)
+ }
+ if len(srcs) > 1 {
+ srcFileError()
+ return
+ } else if len(srcs) == 0 {
+ return
+ }
+ if srcLabelAttribute.SelectValue(axis, config) != nil {
+ srcFileError()
+ return
+ }
+ srcLabelAttribute.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, srcs[0]))
+}
+
// NOTE: Used outside of Soong repo project, in the clangprebuilts.go bootstrap_go_package
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
-
- 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)
+ parseSrc(ctx, &srcLabelAttribute, axis, config, prebuiltLinkerProperties.Srcs)
}
})
@@ -237,7 +316,7 @@
if props.Enabled != nil {
enabledLabelAttribute.SetSelectValue(axis, config, props.Enabled)
}
- parseSrcs(ctx, axis, config, props.Srcs)
+ parseSrc(ctx, &srcLabelAttribute, axis, config, props.Srcs)
}
if isStatic {
@@ -260,11 +339,29 @@
}
}
-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)
+func bp2BuildParsePrebuiltBinaryProps(ctx android.BazelConversionPathContext, module *Module) prebuiltAttributes {
+ var srcLabelAttribute bazel.LabelAttribute
+ bp2BuildPropParseHelper(ctx, module, &prebuiltLinkerProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if props, ok := props.(*prebuiltLinkerProperties); ok {
+ parseSrc(ctx, &srcLabelAttribute, axis, config, props.Srcs)
}
+ })
+
+ return prebuiltAttributes{
+ Src: srcLabelAttribute,
+ }
+}
+
+func bp2BuildParsePrebuiltObjectProps(ctx android.BazelConversionPathContext, module *Module) prebuiltAttributes {
+ var srcLabelAttribute bazel.LabelAttribute
+ bp2BuildPropParseHelper(ctx, module, &prebuiltObjectProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if props, ok := props.(*prebuiltObjectProperties); ok {
+ parseSrc(ctx, &srcLabelAttribute, axis, config, props.Srcs)
+ }
+ })
+
+ return prebuiltAttributes{
+ Src: srcLabelAttribute,
}
}
@@ -272,7 +369,11 @@
compilerAttributes
linkerAttributes
+ // A combination of compilerAttributes.features and linkerAttributes.features, as well as sanitizer features
+ features bazel.StringListAttribute
protoDependency *bazel.LabelAttribute
+ aidlDependency *bazel.LabelAttribute
+ Native_coverage *bool
}
// Convenience struct to hold all attributes parsed from compiler properties.
@@ -282,6 +383,7 @@
// Assembly options and sources
asFlags bazel.StringListAttribute
asSrcs bazel.LabelListAttribute
+ asmSrcs bazel.LabelListAttribute
// C options and sources
conlyFlags bazel.StringListAttribute
cSrcs bazel.LabelListAttribute
@@ -289,6 +391,14 @@
cppFlags bazel.StringListAttribute
srcs bazel.LabelListAttribute
+ // Lex sources and options
+ lSrcs bazel.LabelListAttribute
+ llSrcs bazel.LabelListAttribute
+ lexopts bazel.StringListAttribute
+
+ // Sysprop sources
+ syspropSrcs bazel.LabelListAttribute
+
hdrs bazel.LabelListAttribute
rtti bazel.BoolAttribute
@@ -304,9 +414,16 @@
includes BazelIncludes
protoSrcs bazel.LabelListAttribute
+ aidlSrcs bazel.LabelListAttribute
stubsSymbolFile *string
stubsVersions bazel.StringListAttribute
+
+ features bazel.StringListAttribute
+
+ suffix bazel.StringAttribute
+
+ fdoProfile bazel.LabelAttribute
}
type filterOutFn func(string) bool
@@ -315,10 +432,25 @@
return strings.HasPrefix(flag, "-std=")
}
-func parseCommandLineFlags(soongFlags []string, filterOut filterOutFn) []string {
+func filterOutClangUnknownCflags(flag string) bool {
+ for _, f := range config.ClangUnknownCflags {
+ if f == flag {
+ return true
+ }
+ }
+ return false
+}
+
+func parseCommandLineFlags(soongFlags []string, filterOut ...filterOutFn) []string {
var result []string
for _, flag := range soongFlags {
- if filterOut != nil && filterOut(flag) {
+ skipFlag := false
+ for _, filter := range filterOut {
+ if filter != nil && filter(flag) {
+ skipFlag = true
+ }
+ }
+ if skipFlag {
continue
}
// Soong's cflags can contain spaces, like `-include header.h`. For
@@ -347,40 +479,45 @@
ca.absoluteIncludes.SetSelectValue(axis, config, props.Include_dirs)
ca.localIncludes.SetSelectValue(axis, config, localIncludeDirs)
+ instructionSet := proptools.StringDefault(props.Instruction_set, "")
+ if instructionSet == "arm" {
+ ca.features.SetSelectValue(axis, config, []string{"arm_isa_arm", "-arm_isa_thumb"})
+ } else if instructionSet != "" && instructionSet != "thumb" {
+ ctx.ModuleErrorf("Unknown value for instruction_set: %s", instructionSet)
+ }
+
// In Soong, cflags occur on the command line before -std=<val> flag, resulting in the value being
// overridden. In Bazel we always allow overriding, via flags; however, this can cause
// incompatibilities, so we remove "-std=" flags from Cflag properties while leaving it in other
// cases.
- ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
+ ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag, filterOutClangUnknownCflags))
ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, nil))
- ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, nil))
- ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, nil))
+ ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, filterOutClangUnknownCflags))
+ ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, filterOutClangUnknownCflags))
ca.rtti.SetSelectValue(axis, config, props.Rtti)
}
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 {
+ stl := deduplicateStlInput(*stlProps.Stl)
+ ca.stl = &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) {
productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
"Cflags": &ca.copts,
"Asflags": &ca.asFlags,
- "CppFlags": &ca.cppFlags,
+ "Cppflags": &ca.cppFlags,
}
for propName, attr := range productVarPropNameToAttribute {
if productConfigProps, exists := productVariableProps[propName]; exists {
@@ -401,6 +538,7 @@
partitionedSrcs := groupSrcsByExtension(ctx, ca.srcs)
ca.protoSrcs = partitionedSrcs[protoSrcPartition]
+ ca.aidlSrcs = partitionedSrcs[aidlSrcPartition]
for p, lla := range partitionedSrcs {
// if there are no sources, there is no need for headers
@@ -414,6 +552,10 @@
ca.srcs = partitionedSrcs[cppSrcPartition]
ca.cSrcs = partitionedSrcs[cSrcPartition]
ca.asSrcs = partitionedSrcs[asSrcPartition]
+ ca.asmSrcs = partitionedSrcs[asmSrcPartition]
+ ca.lSrcs = partitionedSrcs[lSrcPartition]
+ ca.llSrcs = partitionedSrcs[llSrcPartition]
+ ca.syspropSrcs = partitionedSrcs[syspropSrcPartition]
ca.absoluteIncludes.DeduplicateAxesFromBase()
ca.localIncludes.DeduplicateAxesFromBase()
@@ -430,38 +572,41 @@
}
allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, props.Srcs, props.Exclude_srcs)
+
if len(props.Srcs) > 0 || len(props.Exclude_srcs) > 0 {
anySrcs = true
}
+
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
@@ -491,6 +636,61 @@
return relative, absolute
}
+type YasmAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Flags bazel.StringListAttribute
+ Include_dirs bazel.StringListAttribute
+}
+
+func bp2BuildYasm(ctx android.Bp2buildMutatorContext, m *Module, ca compilerAttributes) *bazel.LabelAttribute {
+ if ca.asmSrcs.IsEmpty() {
+ return nil
+ }
+
+ // Yasm needs the include directories from both local_includes and
+ // export_include_dirs. We don't care about actually exporting them from the
+ // yasm rule though, because they will also be present on the cc_ rule that
+ // wraps this yasm rule.
+ includes := ca.localIncludes.Clone()
+ bp2BuildPropParseHelper(ctx, m, &FlagExporterProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
+ if len(flagExporterProperties.Export_include_dirs) > 0 {
+ x := bazel.StringListAttribute{}
+ x.SetSelectValue(axis, config, flagExporterProperties.Export_include_dirs)
+ includes.Append(x)
+ }
+ }
+ })
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "yasm",
+ Bzl_load_location: "//build/bazel/rules/cc:yasm.bzl",
+ },
+ android.CommonAttributes{Name: m.Name() + "_yasm"},
+ &YasmAttributes{
+ Srcs: ca.asmSrcs,
+ Flags: ca.asFlags,
+ Include_dirs: *includes,
+ })
+
+ // We only want to add a dependency on the _yasm target if there are asm
+ // sources in the current configuration. If there are unconfigured asm
+ // sources, always add the dependency. Otherwise, add the dependency only
+ // on the configuration axes and values that had asm sources.
+ if len(ca.asmSrcs.Value.Includes) > 0 {
+ return bazel.MakeLabelAttribute(":" + m.Name() + "_yasm")
+ }
+
+ ret := &bazel.LabelAttribute{}
+ for _, axis := range ca.asmSrcs.SortedConfigurationAxes() {
+ for cfg := range ca.asmSrcs.ConfigurableValues[axis] {
+ ret.SetSelectValue(axis, cfg, bazel.Label{Label: ":" + m.Name() + "_yasm"})
+ }
+ }
+ return ret
+}
+
// bp2BuildParseBaseProps returns all compiler, linker, library attributes of a cc module..
func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module) baseAttributes {
archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
@@ -505,8 +705,8 @@
if _, ok := axisToConfigs[axis]; !ok {
axisToConfigs[axis] = map[string]bool{}
}
- for config, _ := range configMap {
- axisToConfigs[axis][config] = true
+ for cfg := range configMap {
+ axisToConfigs[axis][cfg] = true
}
}
}
@@ -517,42 +717,61 @@
compilerAttrs := compilerAttributes{}
linkerAttrs := linkerAttributes{}
- for axis, configs := range axisToConfigs {
- for config, _ := range configs {
+ // Iterate through these axes in a deterministic order. This is required
+ // because processing certain dependencies may result in concatenating
+ // elements along other axes. (For example, processing NoConfig may result
+ // in elements being added to InApex). This is thus the only way to ensure
+ // that the order of entries in each list is in a predictable order.
+ for _, axis := range bazel.SortedConfigurationAxes(axisToConfigs) {
+ configs := axisToConfigs[axis]
+ for cfg := range configs {
var allHdrs []string
- if baseCompilerProps, ok := archVariantCompilerProps[axis][config].(*BaseCompilerProperties); ok {
+ if baseCompilerProps, ok := archVariantCompilerProps[axis][cfg].(*BaseCompilerProperties); ok {
allHdrs = baseCompilerProps.Generated_headers
-
- (&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, config, baseCompilerProps)
+ if baseCompilerProps.Lex != nil {
+ compilerAttrs.lexopts.SetSelectValue(axis, cfg, baseCompilerProps.Lex.Flags)
+ }
+ (&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, cfg, baseCompilerProps)
}
var exportHdrs []string
- if baseLinkerProps, ok := archVariantLinkerProps[axis][config].(*BaseLinkerProperties); ok {
+ if baseLinkerProps, ok := archVariantLinkerProps[axis][cfg].(*BaseLinkerProperties); ok {
exportHdrs = baseLinkerProps.Export_generated_headers
- (&linkerAttrs).bp2buildForAxisAndConfig(ctx, module.Binary(), axis, config, baseLinkerProps)
+ (&linkerAttrs).bp2buildForAxisAndConfig(ctx, module, axis, cfg, baseLinkerProps)
}
headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
- implementationHdrs.SetSelectValue(axis, config, headers.implementation)
- compilerAttrs.hdrs.SetSelectValue(axis, config, headers.export)
+ implementationHdrs.SetSelectValue(axis, cfg, headers.implementation)
+ compilerAttrs.hdrs.SetSelectValue(axis, cfg, headers.export)
exportIncludes, exportAbsoluteIncludes := includesFromLabelList(headers.export)
- compilerAttrs.includes.Includes.SetSelectValue(axis, config, exportIncludes)
- compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, config, exportAbsoluteIncludes)
+ compilerAttrs.includes.Includes.SetSelectValue(axis, cfg, exportIncludes)
+ compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, cfg, exportAbsoluteIncludes)
includes, absoluteIncludes := includesFromLabelList(headers.implementation)
- currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, config)
+ currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, cfg)
currAbsoluteIncludes = android.FirstUniqueStrings(append(currAbsoluteIncludes, absoluteIncludes...))
- compilerAttrs.absoluteIncludes.SetSelectValue(axis, config, currAbsoluteIncludes)
- currIncludes := compilerAttrs.localIncludes.SelectValue(axis, config)
- currIncludes = android.FirstUniqueStrings(append(currIncludes, includes...))
- compilerAttrs.localIncludes.SetSelectValue(axis, config, currIncludes)
- if libraryProps, ok := archVariantLibraryProperties[axis][config].(*LibraryProperties); ok {
+ compilerAttrs.absoluteIncludes.SetSelectValue(axis, cfg, currAbsoluteIncludes)
+
+ currIncludes := compilerAttrs.localIncludes.SelectValue(axis, cfg)
+ currIncludes = android.FirstUniqueStrings(append(currIncludes, includes...))
+
+ compilerAttrs.localIncludes.SetSelectValue(axis, cfg, currIncludes)
+
+ if libraryProps, ok := archVariantLibraryProperties[axis][cfg].(*LibraryProperties); ok {
if axis == bazel.NoConfigAxis {
- compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
- compilerAttrs.stubsVersions.SetSelectValue(axis, config, libraryProps.Stubs.Versions)
+ if libraryProps.Stubs.Symbol_file != nil {
+ compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
+ versions := android.CopyOf(libraryProps.Stubs.Versions)
+ normalizeVersions(ctx, versions)
+ versions = addCurrentVersionIfNotPresent(versions)
+ compilerAttrs.stubsVersions.SetSelectValue(axis, cfg, versions)
+ }
+ }
+ if suffix := libraryProps.Suffix; suffix != nil {
+ compilerAttrs.suffix.SetSelectValue(axis, cfg, suffix)
}
}
}
@@ -561,7 +780,13 @@
compilerAttrs.convertStlProps(ctx, module)
(&linkerAttrs).convertStripProps(ctx, module)
- productVariableProps := android.ProductVariableProperties(ctx)
+ var nativeCoverage *bool
+ if module.coverage != nil && module.coverage.Properties.Native_coverage != nil &&
+ !Bool(module.coverage.Properties.Native_coverage) {
+ nativeCoverage = BoolPtr(false)
+ }
+
+ productVariableProps := android.ProductVariableProperties(ctx, ctx.Module())
(&compilerAttrs).convertProductVariables(ctx, productVariableProps)
(&linkerAttrs).convertProductVariables(ctx, productVariableProps)
@@ -569,6 +794,8 @@
(&compilerAttrs).finalize(ctx, implementationHdrs)
(&linkerAttrs).finalize(ctx)
+ (&compilerAttrs.srcs).Add(bp2BuildYasm(ctx, module, compilerAttrs))
+
protoDep := bp2buildProto(ctx, module, compilerAttrs.protoSrcs)
// bp2buildProto will only set wholeStaticLib or implementationWholeStaticLib, but we don't know
@@ -577,13 +804,168 @@
(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
+ aidlDep := bp2buildCcAidlLibrary(ctx, module, compilerAttrs.aidlSrcs, linkerAttrs)
+ if aidlDep != nil {
+ if lib, ok := module.linker.(*libraryDecorator); ok {
+ if proptools.Bool(lib.Properties.Aidl.Export_aidl_headers) {
+ (&linkerAttrs).wholeArchiveDeps.Add(aidlDep)
+ } else {
+ (&linkerAttrs).implementationWholeArchiveDeps.Add(aidlDep)
+ }
+ }
+ }
+
+ convertedLSrcs := bp2BuildLex(ctx, module.Name(), compilerAttrs)
+ (&compilerAttrs).srcs.Add(&convertedLSrcs.srcName)
+ (&compilerAttrs).cSrcs.Add(&convertedLSrcs.cSrcName)
+
+ if module.afdo != nil && module.afdo.Properties.Afdo {
+ fdoProfileDep := bp2buildFdoProfile(ctx, module)
+ if fdoProfileDep != nil {
+ (&compilerAttrs).fdoProfile.SetValue(*fdoProfileDep)
+ }
+ }
+
+ if !compilerAttrs.syspropSrcs.IsEmpty() {
+ (&linkerAttrs).wholeArchiveDeps.Add(bp2buildCcSysprop(ctx, module.Name(), module.Properties.Min_sdk_version, compilerAttrs.syspropSrcs))
+ }
+
+ linkerAttrs.wholeArchiveDeps.Prepend = true
+ linkerAttrs.deps.Prepend = true
+ compilerAttrs.localIncludes.Prepend = true
+ compilerAttrs.absoluteIncludes.Prepend = true
+ compilerAttrs.hdrs.Prepend = true
+
+ features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module))
+ features = features.Append(bp2buildLtoFeatures(ctx, module))
+ features.DeduplicateAxesFromBase()
+
+ addMuslSystemDynamicDeps(ctx, linkerAttrs)
+
return baseAttributes{
compilerAttrs,
linkerAttrs,
+ *features,
protoDep.protoDep,
+ aidlDep,
+ nativeCoverage,
}
}
+// As a workaround for b/261657184, we are manually adding the default value
+// of system_dynamic_deps for the linux_musl os.
+// TODO: Solve this properly
+func addMuslSystemDynamicDeps(ctx android.Bp2buildMutatorContext, attrs linkerAttributes) {
+ systemDynamicDeps := attrs.systemDynamicDeps.SelectValue(bazel.OsConfigurationAxis, "linux_musl")
+ if attrs.systemDynamicDeps.HasAxisSpecificValues(bazel.OsConfigurationAxis) && systemDynamicDeps.IsNil() {
+ attrs.systemDynamicDeps.SetSelectValue(bazel.OsConfigurationAxis, "linux_musl", android.BazelLabelForModuleDeps(ctx, config.MuslDefaultSharedLibraries))
+ }
+}
+
+type fdoProfileAttributes struct {
+ Absolute_path_profile string
+}
+
+func bp2buildFdoProfile(
+ ctx android.Bp2buildMutatorContext,
+ m *Module,
+) *bazel.Label {
+ for _, project := range globalAfdoProfileProjects {
+ // Ensure handcrafted BUILD file exists in the project
+ BUILDPath := android.ExistentPathForSource(ctx, project, "BUILD")
+ if BUILDPath.Valid() {
+ // We handcraft a BUILD file with fdo_profile targets that use the existing profiles in the project
+ // This implementation is assuming that every afdo profile in globalAfdoProfileProjects already has
+ // an associated fdo_profile target declared in the same package.
+ // TODO(b/260714900): Handle arch-specific afdo profiles (e.g. `<module-name>-arm<64>.afdo`)
+ path := android.ExistentPathForSource(ctx, project, m.Name()+".afdo")
+ if path.Valid() {
+ // FIXME: Some profiles only exist internally and are not released to AOSP.
+ // When generated BUILD files are checked in, we'll run into merge conflict.
+ // The cc_library_shared target in AOSP won't have reference to an fdo_profile target because
+ // the profile doesn't exist. Internally, the same cc_library_shared target will
+ // have reference to the fdo_profile.
+ // For more context, see b/258682955#comment2
+ fdoProfileLabel := "//" + strings.TrimSuffix(project, "/") + ":" + m.Name()
+ return &bazel.Label{
+ Label: fdoProfileLabel,
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+func bp2buildCcAidlLibrary(
+ ctx android.Bp2buildMutatorContext,
+ m *Module,
+ aidlLabelList bazel.LabelListAttribute,
+ linkerAttrs linkerAttributes,
+) *bazel.LabelAttribute {
+ if !aidlLabelList.IsEmpty() {
+ aidlLibs, aidlSrcs := aidlLabelList.Partition(func(src bazel.Label) bool {
+ if fg, ok := android.ToFileGroupAsLibrary(ctx, src.OriginalModuleName); ok &&
+ fg.ShouldConvertToAidlLibrary(ctx) {
+ return true
+ }
+ return false
+ })
+
+ apexAvailableTags := android.ApexAvailableTags(ctx.Module())
+ sdkAttrs := bp2BuildParseSdkAttributes(m)
+
+ if !aidlSrcs.IsEmpty() {
+ aidlLibName := m.Name() + "_aidl_library"
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "aidl_library",
+ Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl",
+ },
+ android.CommonAttributes{Name: aidlLibName},
+ &aidlLibraryAttributes{
+ Srcs: aidlSrcs,
+ Tags: apexAvailableTags,
+ },
+ )
+ aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
+ }
+
+ if !aidlLibs.IsEmpty() {
+ ccAidlLibrarylabel := m.Name() + "_cc_aidl_library"
+ // Since parent cc_library already has these dependencies, we can add them as implementation
+ // deps so that they don't re-export
+ implementationDeps := linkerAttrs.deps.Clone()
+ implementationDeps.Append(linkerAttrs.implementationDeps)
+ implementationDynamicDeps := linkerAttrs.dynamicDeps.Clone()
+ implementationDynamicDeps.Append(linkerAttrs.implementationDynamicDeps)
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_aidl_library",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_aidl_library.bzl",
+ },
+ android.CommonAttributes{Name: ccAidlLibrarylabel},
+ &ccAidlLibraryAttributes{
+ Deps: aidlLibs,
+ Implementation_deps: *implementationDeps,
+ Implementation_dynamic_deps: *implementationDynamicDeps,
+ Tags: apexAvailableTags,
+ sdkAttributes: sdkAttrs,
+ },
+ )
+ label := &bazel.LabelAttribute{
+ Value: &bazel.Label{
+ Label: ":" + ccAidlLibrarylabel,
+ },
+ }
+ return label
+ }
+ }
+
+ return nil
+}
+
func bp2BuildParseSdkAttributes(module *Module) sdkAttributes {
return sdkAttributes{
Sdk_version: module.Properties.Sdk_version,
@@ -602,13 +984,12 @@
implementationDeps bazel.LabelListAttribute
dynamicDeps bazel.LabelListAttribute
implementationDynamicDeps bazel.LabelListAttribute
+ runtimeDeps bazel.LabelListAttribute
wholeArchiveDeps bazel.LabelListAttribute
implementationWholeArchiveDeps bazel.LabelListAttribute
systemDynamicDeps bazel.LabelListAttribute
usedSystemDynamicDepAsDynamicDep map[string]bool
- linkCrt bazel.BoolAttribute
- useLibcrt bazel.BoolAttribute
useVersionLib bazel.BoolAttribute
linkopts bazel.StringListAttribute
additionalLinkerInputs bazel.LabelListAttribute
@@ -622,19 +1003,65 @@
var (
soongSystemSharedLibs = []string{"libc", "libm", "libdl"}
+ versionLib = "libbuildversion"
)
-func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, isBinary bool, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
+// resolveTargetApex re-adds the shared and static libs in target.apex.exclude_shared|static_libs props to non-apex variant
+// since all libs are already excluded by default
+func (la *linkerAttributes) resolveTargetApexProp(ctx android.BazelConversionPathContext, props *BaseLinkerProperties) {
+ excludeSharedLibs := bazelLabelForSharedDeps(ctx, props.Target.Apex.Exclude_shared_libs)
+ sharedExcludes := bazel.LabelList{Excludes: excludeSharedLibs.Includes}
+ sharedExcludesLabelList := bazel.LabelListAttribute{}
+ sharedExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, sharedExcludes)
+
+ la.dynamicDeps.Append(sharedExcludesLabelList)
+ la.implementationDynamicDeps.Append(sharedExcludesLabelList)
+
+ excludeStaticLibs := bazelLabelForStaticDeps(ctx, props.Target.Apex.Exclude_static_libs)
+ staticExcludes := bazel.LabelList{Excludes: excludeStaticLibs.Includes}
+ staticExcludesLabelList := bazel.LabelListAttribute{}
+ staticExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, staticExcludes)
+
+ la.deps.Append(staticExcludesLabelList)
+ la.implementationDeps.Append(staticExcludesLabelList)
+}
+
+func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, module *Module, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
+ isBinary := module.Binary()
// 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))
+ staticLibs := android.FirstUniqueStrings(android.RemoveListFromList(props.Static_libs, wholeStaticLibs))
+ if axis == bazel.NoConfigAxis {
+ la.useVersionLib.SetSelectValue(axis, config, props.Use_version_lib)
+ if proptools.Bool(props.Use_version_lib) {
+ versionLibAlreadyInDeps := android.InList(versionLib, wholeStaticLibs)
+ // remove from static libs so there is no duplicate dependency
+ _, staticLibs = android.RemoveFromList(versionLib, staticLibs)
+ // only add the dep if it is not in progress
+ if !versionLibAlreadyInDeps {
+ if isBinary {
+ wholeStaticLibs = append(wholeStaticLibs, versionLib)
+ } else {
+ la.implementationWholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, []string{versionLib}, 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(android.RemoveListFromList(props.Static_libs, wholeStaticLibs))
+ la.wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, props.Exclude_static_libs))
- staticDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, staticLibs, props.Exclude_static_libs, props.Export_static_lib_headers, bazelLabelForStaticDepsExcludes)
+ staticDeps := maybePartitionExportedAndImplementationsDepsExcludes(
+ ctx,
+ !isBinary,
+ staticLibs,
+ props.Exclude_static_libs,
+ props.Export_static_lib_headers,
+ bazelLabelForStaticDepsExcludes,
+ )
headerLibs := android.FirstUniqueStrings(props.Header_libs)
hDeps := maybePartitionExportedAndImplementationsDeps(ctx, !isBinary, headerLibs, props.Export_header_lib_headers, bazelLabelForHeaderDeps)
@@ -666,9 +1093,28 @@
la.usedSystemDynamicDepAsDynamicDep[el] = true
}
- sharedDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, sharedLibs, props.Exclude_shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDepsExcludes)
+ sharedDeps := maybePartitionExportedAndImplementationsDepsExcludes(
+ ctx,
+ !isBinary,
+ sharedLibs,
+ props.Exclude_shared_libs,
+ props.Export_shared_lib_headers,
+ bazelLabelForSharedDepsExcludes,
+ )
la.dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
la.implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
+ la.resolveTargetApexProp(ctx, props)
+
+ if axis == bazel.NoConfigAxis || (axis == bazel.OsConfigurationAxis && config == bazel.OsAndroid) {
+ // If a dependency in la.implementationDynamicDeps or la.dynamicDeps has stubs, its
+ // stub variant should be used when the dependency is linked in a APEX. The
+ // dependencies in NoConfigAxis and OsConfigurationAxis/OsAndroid are grouped by
+ // having stubs or not, so Bazel select() statement can be used to choose
+ // source/stub variants of them.
+ apexAvailable := module.ApexAvailable()
+ setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.export, &la.dynamicDeps, 0)
+ setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.implementation, &la.implementationDynamicDeps, 1)
+ }
if !BoolDefault(props.Pack_relocations, packRelocationsDefault) {
axisFeatures = append(axisFeatures, "disable_pack_relocations")
@@ -686,44 +1132,109 @@
axisFeatures = append(axisFeatures, "-static_flag")
}
}
+
+ if !props.libCrt() {
+ axisFeatures = append(axisFeatures, "-use_libcrt")
+ }
+ if !props.crt() {
+ axisFeatures = append(axisFeatures, "-link_crt")
+ }
+
+ // This must happen before the addition of flags for Version Script and
+ // Dynamic List, as these flags must be split on spaces and those must not
+ linkerFlags = parseCommandLineFlags(linkerFlags, filterOutClangUnknownCflags)
+
+ additionalLinkerInputs := bazel.LabelList{}
if props.Version_script != nil {
label := android.BazelLabelForModuleSrcSingle(ctx, *props.Version_script)
- la.additionalLinkerInputs.SetSelectValue(axis, config, bazel.LabelList{Includes: []bazel.Label{label}})
+ additionalLinkerInputs.Add(&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)
+ additionalLinkerInputs.Add(&label)
+ linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--dynamic-list,$(location %s)", label.Label))
+ }
+
+ la.additionalLinkerInputs.SetSelectValue(axis, config, additionalLinkerInputs)
la.linkopts.SetSelectValue(axis, config, linkerFlags)
- la.useLibcrt.SetSelectValue(axis, config, props.libCrt())
-
- if axis == bazel.NoConfigAxis {
- la.useVersionLib.SetSelectValue(axis, config, props.Use_version_lib)
- }
-
- // it's very unlikely for nocrt to be arch variant, so bp2build doesn't support it.
- if props.crt() != nil {
- if axis == bazel.NoConfigAxis {
- la.linkCrt.SetSelectValue(axis, config, props.crt())
- } else if axis == bazel.ArchConfigurationAxis {
- ctx.ModuleErrorf("nocrt is not supported for arch variants")
- }
- }
if axisFeatures != nil {
la.features.SetSelectValue(axis, config, axisFeatures)
}
+
+ runtimeDeps := android.BazelLabelForModuleDepsExcludes(ctx, props.Runtime_libs, props.Exclude_runtime_libs)
+ if !runtimeDeps.IsEmpty() {
+ la.runtimeDeps.SetSelectValue(axis, config, runtimeDeps)
+ }
}
-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)
+var (
+ apiSurfaceModuleLibCurrentPackage = "@api_surfaces//" + android.ModuleLibApi.String() + "/current:"
+)
+
+func availableToSameApexes(a, b []string) bool {
+ if len(a) == 0 && len(b) == 0 {
+ return true
+ }
+ differ, _, _ := android.ListSetDifference(a, b)
+ return !differ
+}
+
+func setStubsForDynamicDeps(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis,
+ config string, apexAvailable []string, dynamicLibs bazel.LabelList, dynamicDeps *bazel.LabelListAttribute, ind int) {
+
+ depsWithStubs := []bazel.Label{}
+ for _, l := range dynamicLibs.Includes {
+ dep, _ := ctx.ModuleFromName(l.OriginalModuleName)
+ if d, ok := dep.(*Module); ok && d.HasStubsVariants() {
+ depApexAvailable := d.ApexAvailable()
+ if !availableToSameApexes(apexAvailable, depApexAvailable) {
+ depsWithStubs = append(depsWithStubs, l)
}
}
}
+ if len(depsWithStubs) > 0 {
+ implDynamicDeps := bazel.SubtractBazelLabelList(dynamicLibs, bazel.MakeLabelList(depsWithStubs))
+ dynamicDeps.SetSelectValue(axis, config, implDynamicDeps)
+
+ stubLibLabels := []bazel.Label{}
+ for _, l := range depsWithStubs {
+ stubLabelInApiSurfaces := bazel.Label{
+ Label: apiSurfaceModuleLibCurrentPackage + strings.TrimPrefix(l.OriginalModuleName, ":"),
+ }
+ stubLibLabels = append(stubLibLabels, stubLabelInApiSurfaces)
+ }
+ inApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex)
+ nonApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex)
+ defaultSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey)
+ if axis == bazel.NoConfigAxis {
+ (&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
+ (&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+ (&defaultSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+ dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
+ dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
+ dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, bazel.FirstUniqueBazelLabelList(defaultSelectValue))
+ } else if config == bazel.OsAndroid {
+ (&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
+ (&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+ dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
+ dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
+ }
+ }
+}
+
+func (la *linkerAttributes) convertStripProps(ctx android.BazelConversionPathContext, module *Module) {
+ 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) {
@@ -737,17 +1248,23 @@
depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
}
+ // an intermediate attribute that holds Header_libs info, and will be appended to
+ // implementationDeps at the end, to solve the confliction that both header_libs
+ // and static_libs use implementationDeps.
+ var headerDeps bazel.LabelListAttribute
+
productVarToDepFields := map[string]productVarDep{
// product variables do not support exclude_shared_libs
"Shared_libs": {attribute: &la.implementationDynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
"Static_libs": {"Exclude_static_libs", &la.implementationDeps, bazelLabelForStaticDepsExcludes},
"Whole_static_libs": {"Exclude_static_libs", &la.wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
+ "Header_libs": {attribute: &headerDeps, depResolutionFunc: bazelLabelForHeaderDepsExcludes},
}
for name, dep := range productVarToDepFields {
props, exists := productVariableProps[name]
excludeProps, excludesExists := productVariableProps[dep.excludesField]
- // if neither an include or excludes property exists, then skip it
+ // if neither an include nor excludes property exists, then skip it
if !exists && !excludesExists {
continue
}
@@ -783,18 +1300,32 @@
)
}
}
+ la.implementationDeps.Append(headerDeps)
}
func (la *linkerAttributes) finalize(ctx android.BazelConversionPathContext) {
// if system dynamic deps have the default value, any use of a system dynamic library used will
// result in duplicate library errors for bionic OSes. Here, we explicitly exclude those libraries
- // from bionic OSes.
+ // from bionic OSes and the no config case as these libraries only build for bionic OSes.
if la.systemDynamicDeps.IsNil() && len(la.usedSystemDynamicDepAsDynamicDep) > 0 {
- toRemove := bazelLabelForSharedDeps(ctx, android.SortedStringKeys(la.usedSystemDynamicDepAsDynamicDep))
+ toRemove := bazelLabelForSharedDeps(ctx, android.SortedKeys(la.usedSystemDynamicDepAsDynamicDep))
+ la.dynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+ la.implementationDynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+
+ la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, toRemove)
+ la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, toRemove)
+ stubsToRemove := make([]bazel.Label, 0, len(la.usedSystemDynamicDepAsDynamicDep))
+ for _, lib := range toRemove.Includes {
+ stubLabelInApiSurfaces := bazel.Label{
+ Label: apiSurfaceModuleLibCurrentPackage + lib.OriginalModuleName,
+ }
+ stubsToRemove = append(stubsToRemove, stubLabelInApiSurfaces)
+ }
+ la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.MakeLabelList(stubsToRemove))
}
la.deps.ResolveExcludes()
@@ -837,40 +1368,30 @@
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...)))
- }
+
+ // cc library Export_include_dirs and Export_system_include_dirs are marked
+ // "variant_prepend" in struct tag, set their prepend property to true to make
+ // sure bp2build generates correct result.
+ exported.Includes.Prepend = true
+ exported.SystemIncludes.Prepend = true
+
+ 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()
@@ -878,10 +1399,14 @@
return exported
}
+func BazelLabelNameForStaticModule(baseLabel string) string {
+ return baseLabel + "_bp2build_cc_library_static"
+}
+
func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
label := android.BazelModuleLabel(ctx, m)
- if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GenerateCcLibraryStaticOnly(m.Name()) {
- label += "_bp2build_cc_library_static"
+ if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary {
+ return BazelLabelNameForStaticModule(label)
}
return label
}
@@ -928,32 +1453,102 @@
return bazelLabelForSharedDeps(ctx, modules)
}
+func bazelLabelForHeaderDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+ // This is only used when product_variable header_libs is processed, to follow
+ // the pattern of depResolutionFunc
+ return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
+}
+
func bazelLabelForSharedDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
}
type binaryLinkerAttrs struct {
Linkshared *bool
+ Suffix bazel.StringAttribute
}
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")
}
- }
+ if suffix := linkerProps.Suffix; suffix != nil {
+ attrs.Suffix.SetSelectValue(axis, config, suffix)
+ }
+ })
return attrs
}
+
+func bp2buildSanitizerFeatures(ctx android.BazelConversionPathContext, m *Module) bazel.StringListAttribute {
+ sanitizerFeatures := bazel.StringListAttribute{}
+ bp2BuildPropParseHelper(ctx, m, &SanitizeProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ var features []string
+ if sanitizerProps, ok := props.(*SanitizeProperties); ok {
+ if sanitizerProps.Sanitize.Integer_overflow != nil && *sanitizerProps.Sanitize.Integer_overflow {
+ features = append(features, "ubsan_integer_overflow")
+ }
+ for _, sanitizer := range sanitizerProps.Sanitize.Misc_undefined {
+ features = append(features, "ubsan_"+sanitizer)
+ }
+ sanitizerFeatures.SetSelectValue(axis, config, features)
+ }
+ })
+ return sanitizerFeatures
+}
+
+func bp2buildLtoFeatures(ctx android.BazelConversionPathContext, m *Module) bazel.StringListAttribute {
+ lto_feature_name := "android_thin_lto"
+ ltoBoolFeatures := bazel.BoolAttribute{}
+ bp2BuildPropParseHelper(ctx, m, <OProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+ if ltoProps, ok := props.(*LTOProperties); ok {
+ thinProp := ltoProps.Lto.Thin != nil && *ltoProps.Lto.Thin
+ thinPropSetToFalse := ltoProps.Lto.Thin != nil && !*ltoProps.Lto.Thin
+ neverProp := ltoProps.Lto.Never != nil && *ltoProps.Lto.Never
+ if thinProp {
+ ltoBoolFeatures.SetSelectValue(axis, config, BoolPtr(true))
+ return
+ }
+ if neverProp || thinPropSetToFalse {
+ if thinProp {
+ ctx.ModuleErrorf("lto.thin and lto.never are mutually exclusive but were specified together")
+ } else {
+ ltoBoolFeatures.SetSelectValue(axis, config, BoolPtr(false))
+ }
+ return
+ }
+ }
+ ltoBoolFeatures.SetSelectValue(axis, config, nil)
+ })
+
+ props := m.GetArchVariantProperties(ctx, <OProperties{})
+ ltoStringFeatures, err := ltoBoolFeatures.ToStringListAttribute(func(boolPtr *bool, axis bazel.ConfigurationAxis, config string) []string {
+ if boolPtr == nil {
+ return []string{}
+ }
+ if !*boolPtr {
+ return []string{"-" + lto_feature_name}
+ }
+ features := []string{lto_feature_name}
+ if ltoProps, ok := props[axis][config].(*LTOProperties); ok {
+ if ltoProps.Whole_program_vtables != nil && *ltoProps.Whole_program_vtables {
+ features = append(features, "android_thin_lto_whole_program_vtables")
+ }
+ }
+ return features
+ })
+ if err != nil {
+ ctx.ModuleErrorf("Error processing LTO attributes: %s", err)
+ }
+ return ltoStringFeatures
+}
diff --git a/cc/builder.go b/cc/builder.go
index c390a93..fef00d4 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -63,10 +63,10 @@
ld, ldRE = pctx.RemoteStaticRules("ld",
blueprint.RuleParams{
Command: "$reTemplate$ldCmd ${crtBegin} @${out}.rsp " +
- "${libFlags} ${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}",
+ "${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}",
CommandDeps: []string{"$ldCmd"},
Rspfile: "${out}.rsp",
- RspfileContent: "${in}",
+ RspfileContent: "${in} ${libFlags}",
// clang -Wl,--out-implib doesn't update its output file if it hasn't changed.
Restat: true,
},
@@ -202,36 +202,22 @@
},
"clangBin", "format")
- // Rule for invoking clang-tidy (a clang-based linter).
- clangTidyDep, clangTidyDepRE = pctx.RemoteStaticRules("clangTidyDep",
- blueprint.RuleParams{
- Depfile: "$out",
- Deps: blueprint.DepsGCC,
- Command: "${config.CcWrapper}$ccCmd $cFlags -E -o /dev/null $in " +
- "-MQ $tidyFile -MD -MF $out",
- CommandDeps: []string{"$ccCmd"},
- },
- &remoteexec.REParams{
- Labels: map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
- ExecStrategy: "${config.REClangTidyExecStrategy}",
- Inputs: []string{"$in"},
- Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
- }, []string{"ccCmd", "cFlags", "tidyFile"}, []string{})
-
+ // Rules for invoking clang-tidy (a clang-based linter).
clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- Command: "cp ${out}.dep ${out}.d && " +
- "$tidyVars$reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && " +
- "touch $out",
- CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
+ 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", "${out}.dep"},
- EnvironmentVariables: []string{"TIDY_TIMEOUT"},
+ Inputs: []string{"$in"},
+ 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.
@@ -239,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{"cFlags", "tidyFlags", "tidyVars"}, []string{})
+ }, []string{"cFlags", "ccCmd", "clangCmd", "tidyCmd", "tidyFlags", "tidyVars"}, []string{})
_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
@@ -296,7 +282,7 @@
sAbiDiff = pctx.RuleFunc("sAbiDiff",
func(ctx android.PackageRuleContext) blueprint.RuleParams {
commandStr := "($sAbiDiffer ${extraFlags} -lib ${libName} -arch ${arch} -o ${out} -new ${in} -old ${referenceDump})"
- commandStr += "|| (echo 'error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py ${createReferenceDumpFlags} -l ${libName}'"
+ commandStr += "|| (echo '${errorMessage}'"
commandStr += " && (mkdir -p $$DIST_DIR/abidiffs && cp ${out} $$DIST_DIR/abidiffs/)"
commandStr += " && exit 1)"
return blueprint.RuleParams{
@@ -304,13 +290,7 @@
CommandDeps: []string{"$sAbiDiffer"},
}
},
- "extraFlags", "referenceDump", "libName", "arch", "createReferenceDumpFlags")
-
- // Rule to unzip a reference abi dump.
- unzipRefSAbiDump = pctx.AndroidStaticRule("unzipRefSAbiDump",
- blueprint.RuleParams{
- Command: "gunzip -c $in > $out",
- })
+ "extraFlags", "referenceDump", "libName", "arch", "errorMessage")
// Rule to zip files.
zip = pctx.AndroidStaticRule("zip",
@@ -539,6 +519,13 @@
cppflags += " ${config.NoOverrideGlobalCflags}"
toolingCppflags += " ${config.NoOverrideGlobalCflags}"
+ if flags.toolchain.Is64Bit() {
+ cflags += " ${config.NoOverride64GlobalCflags}"
+ toolingCflags += " ${config.NoOverride64GlobalCflags}"
+ cppflags += " ${config.NoOverride64GlobalCflags}"
+ toolingCppflags += " ${config.NoOverride64GlobalCflags}"
+ }
+
modulePath := android.PathForModuleSrc(ctx).String()
if android.IsThirdPartyPath(modulePath) {
cflags += " ${config.NoOverrideExternalGlobalCflags}"
@@ -636,6 +623,7 @@
continue
}
+ // ccCmd is "clang" or "clang++"
ccDesc := ccCmd
ccCmd = "${config.ClangBin}/" + ccCmd
@@ -681,43 +669,35 @@
// Even with tidy, some src file could be skipped by noTidySrcsMap.
if tidy && !noTidySrcsMap[srcFile.String()] {
tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
- tidyDepFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy.dep")
tidyFiles = append(tidyFiles, tidyFile)
+ tidyCmd := "${config.ClangBin}/clang-tidy"
- ruleDep := clangTidyDep
rule := clangTidy
+ reducedCFlags := moduleFlags
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
- ruleDep = clangTidyDepRE
rule = clangTidyRE
+ // b/248371171, work around RBE input processor problem
+ // some cflags rejected by input processor, but usually
+ // do not affect included files or clang-tidy
+ reducedCFlags = config.TidyReduceCFlags(reducedCFlags)
}
- sharedCFlags := shareFlags("cFlags", moduleFlags)
+ sharedCFlags := shareFlags("cFlags", reducedCFlags)
srcRelPath := srcFile.Rel()
- // Add the .tidy.d rule
- ctx.Build(pctx, android.BuildParams{
- Rule: ruleDep,
- Description: "clang-tidy-dep " + srcRelPath,
- Output: tidyDepFile,
- Input: srcFile,
- Implicits: cFlagsDeps,
- OrderOnly: pathDeps,
- Args: map[string]string{
- "ccCmd": ccCmd,
- "cFlags": sharedCFlags,
- "tidyFile": tidyFile.String(),
- },
- })
- // Add the .tidy rule with order only dependency on the .tidy.d file
+ // Add the .tidy rule
ctx.Build(pctx, android.BuildParams{
Rule: rule,
Description: "clang-tidy " + srcRelPath,
Output: tidyFile,
Input: srcFile,
Implicits: cFlagsDeps,
- OrderOnly: append(android.Paths{}, tidyDepFile),
+ OrderOnly: pathDeps,
Args: map[string]string{
"cFlags": sharedCFlags,
+ "ccCmd": ccCmd,
+ "clangCmd": ccDesc,
+ "tidyCmd": tidyCmd,
"tidyFlags": shareFlags("tidyFlags", config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags)),
"tidyVars": tidyVars, // short and not shared
},
@@ -728,8 +708,10 @@
sAbiDumpFile := android.ObjPathWithExt(ctx, subdir, srcFile, "sdump")
sAbiDumpFiles = append(sAbiDumpFiles, sAbiDumpFile)
- // TODO(b/226497964): dumpRule = sAbiDumpRE if USE_RBE and RBE_ABI_DUMPER are true.
dumpRule := sAbiDump
+ if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_ABI_DUMPER") {
+ dumpRule = sAbiDumpRE
+ }
ctx.Build(pctx, android.BuildParams{
Rule: dumpRule,
Description: "header-abi-dumper " + srcFile.Rel(),
@@ -930,54 +912,16 @@
return android.OptionalPathForPath(outputFile)
}
-// unzipRefDump registers a build statement to unzip a reference abi dump.
-func unzipRefDump(ctx android.ModuleContext, zippedRefDump android.Path, baseName string) android.Path {
- outputFile := android.PathForModuleOut(ctx, baseName+"_ref.lsdump")
- ctx.Build(pctx, android.BuildParams{
- Rule: unzipRefSAbiDump,
- Description: "gunzip" + outputFile.Base(),
- Output: outputFile,
- Input: zippedRefDump,
- })
- return outputFile
-}
+func transformAbiDumpToAbiDiff(ctx android.ModuleContext, inputDump, referenceDump android.Path,
+ baseName, nameExt string, extraFlags []string, errorMessage string) android.Path {
-// 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, diffFlags []string,
- checkAllApis, isLlndk, isNdk, isVndkExt bool) android.OptionalPath {
-
- outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
- libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
- createReferenceDumpFlags := ""
-
- var extraFlags []string
- if checkAllApis {
- extraFlags = append(extraFlags, "-check-all-apis")
+ var outputFile android.ModuleOutPath
+ if nameExt != "" {
+ outputFile = android.PathForModuleOut(ctx, baseName+"."+nameExt+".abidiff")
} else {
- extraFlags = append(extraFlags,
- "-allow-unreferenced-changes",
- "-allow-unreferenced-elf-symbol-changes")
+ outputFile = android.PathForModuleOut(ctx, baseName+".abidiff")
}
-
- if exportedHeaderFlags == "" {
- extraFlags = append(extraFlags, "-advice-only")
- }
-
- if isLlndk || isNdk {
- createReferenceDumpFlags = "--llndk"
- if isLlndk {
- // TODO(b/130324828): "-consider-opaque-types-different" should apply to
- // both LLNDK and NDK shared libs. However, a known issue in header-abi-diff
- // breaks libaaudio. Remove the if-guard after the issue is fixed.
- extraFlags = append(extraFlags, "-consider-opaque-types-different")
- }
- }
- if isVndkExt {
- extraFlags = append(extraFlags, "-allow-extensions")
- }
- // TODO(b/232891473): Simplify the above logic with diffFlags.
- extraFlags = append(extraFlags, diffFlags...)
+ libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
ctx.Build(pctx, android.BuildParams{
Rule: sAbiDiff,
@@ -986,14 +930,14 @@
Input: inputDump,
Implicit: referenceDump,
Args: map[string]string{
- "referenceDump": referenceDump.String(),
- "libName": libName,
- "arch": ctx.Arch().ArchType.Name,
- "extraFlags": strings.Join(extraFlags, " "),
- "createReferenceDumpFlags": createReferenceDumpFlags,
+ "referenceDump": referenceDump.String(),
+ "libName": libName,
+ "arch": ctx.Arch().ArchType.Name,
+ "extraFlags": strings.Join(extraFlags, " "),
+ "errorMessage": errorMessage,
},
})
- return android.OptionalPathForPath(outputFile)
+ return outputFile
}
// Generate a rule for extracting a table of contents from a shared library (.so)
diff --git a/cc/cc.go b/cc/cc.go
index 456b736..0addb60 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -28,9 +28,11 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel/cquery"
"android/soong/cc/config"
"android/soong/fuzz"
"android/soong/genrule"
+ "android/soong/multitree"
"android/soong/snapshot"
)
@@ -48,10 +50,9 @@
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()
+ ctx.BottomUp("fdo_profile", fdoProfileMutator)
})
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
@@ -62,6 +63,8 @@
ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
+ ctx.TopDown("fuzz_deps", fuzzMutatorDeps)
+
ctx.BottomUp("coverage", coverageMutator).Parallel()
ctx.TopDown("afdo_deps", afdoDepsMutator)
@@ -96,6 +99,10 @@
HeaderLibs []string
RuntimeLibs []string
+ // UnexportedStaticLibs are static libraries that are also passed to -Wl,--exclude-libs= to
+ // prevent automatically exporting symbols.
+ UnexportedStaticLibs []string
+
// Used for data dependencies adjacent to tests
DataLibs []string
DataBins []string
@@ -157,6 +164,7 @@
GeneratedDeps android.Paths
Flags []string
+ LdFlags []string
IncludeDirs android.Paths
SystemIncludeDirs android.Paths
ReexportedDirs android.Paths
@@ -478,6 +486,7 @@
static() bool
staticBinary() bool
testBinary() bool
+ testLibrary() bool
header() bool
binary() bool
object() bool
@@ -507,6 +516,7 @@
getVndkExtendsModuleName() string
isAfdoCompile() bool
isPgoCompile() bool
+ isCfi() bool
isNDKStubLibrary() bool
useClangLld(actx ModuleContext) bool
isForPlatform() bool
@@ -608,6 +618,10 @@
XrefCcFiles() android.Paths
}
+type overridable interface {
+ overriddenModules() []string
+}
+
type libraryDependencyKind int
const (
@@ -679,6 +693,9 @@
// Whether or not this dependency has to be followed for the apex variants
excludeInApex bool
+
+ // If true, don't automatically export symbols from the static library into a shared library.
+ unexportedSymbols bool
}
// header returns true if the libraryDependencyTag is tagging a header lib dependency.
@@ -746,6 +763,8 @@
runtimeDepTag = installDependencyTag{name: "runtime lib"}
testPerSrcDepTag = dependencyTag{name: "test_per_src"}
stubImplDepTag = dependencyTag{name: "stub_impl"}
+ JniFuzzLibTag = dependencyTag{name: "jni_fuzz_lib_tag"}
+ FdoProfileTag = dependencyTag{name: "fdo_profile"}
)
func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
@@ -772,6 +791,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",
@@ -789,7 +821,6 @@
type Module struct {
fuzz.FuzzModule
- android.SdkBase
android.BazelModuleBase
VendorProperties VendorProperties
@@ -811,12 +842,13 @@
compiler compiler
linker linker
installer installer
- bazelHandler android.BazelHandler
+ bazelHandler BazelHandler
features []feature
stl *stl
sanitize *sanitize
coverage *coverage
+ fuzzer *fuzzer
sabi *sabi
vndkdep *vndkdep
lto *lto
@@ -969,6 +1001,7 @@
return library.shared()
}
}
+
panic(fmt.Errorf("Shared() called on non-library module: %q", c.BaseModuleName()))
}
@@ -1035,6 +1068,31 @@
return false
}
+func (c *Module) IsFuzzModule() bool {
+ if _, ok := c.compiler.(*fuzzBinary); ok {
+ return true
+ }
+ return false
+}
+
+func (c *Module) FuzzModuleStruct() fuzz.FuzzModule {
+ return c.FuzzModule
+}
+
+func (c *Module) FuzzPackagedModule() fuzz.FuzzPackagedModule {
+ if fuzzer, ok := c.compiler.(*fuzzBinary); ok {
+ return fuzzer.fuzzPackagedModule
+ }
+ panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) FuzzSharedLibraries() android.Paths {
+ if fuzzer, ok := c.compiler.(*fuzzBinary); ok {
+ return fuzzer.sharedLibraries
+ }
+ panic(fmt.Errorf("FuzzSharedLibraries called on non-fuzz module: %q", c.BaseModuleName()))
+}
+
func (c *Module) NonCcVariants() bool {
return false
}
@@ -1141,6 +1199,9 @@
if c.coverage != nil {
c.AddProperties(c.coverage.props()...)
}
+ if c.fuzzer != nil {
+ c.AddProperties(c.fuzzer.props()...)
+ }
if c.sabi != nil {
c.AddProperties(c.sabi.props()...)
}
@@ -1165,12 +1226,13 @@
android.InitBazelModule(c)
}
android.InitApexModule(c)
- android.InitSdkAwareModule(c)
android.InitDefaultableModule(c)
return c
}
+// UseVndk() returns true if this module is built against VNDK.
+// This means the vendor and product variants of a module.
func (c *Module) UseVndk() bool {
return c.Properties.VndkVersion != ""
}
@@ -1264,6 +1326,9 @@
return false
}
+// IsVndk() returns true if this module has a vndk variant.
+// Note that IsVndk() returns true for all variants of vndk-enabled libraries. Not only vendor variant,
+// but also platform and product variants of vndk-enabled libraries return true for IsVndk().
func (c *Module) IsVndk() bool {
if vndkdep := c.vndkdep; vndkdep != nil {
return vndkdep.isVndk()
@@ -1273,7 +1338,7 @@
func (c *Module) isAfdoCompile() bool {
if afdo := c.afdo; afdo != nil {
- return afdo.Properties.AfdoTarget != nil
+ return afdo.Properties.FdoProfilePath != nil
}
return false
}
@@ -1285,6 +1350,13 @@
return false
}
+func (c *Module) isCfi() bool {
+ if sanitize := c.sanitize; sanitize != nil {
+ return Bool(sanitize.Properties.Sanitize.Cfi)
+ }
+ return false
+}
+
func (c *Module) isNDKStubLibrary() bool {
if _, ok := c.compiler.(*stubDecorator); ok {
return true
@@ -1335,6 +1407,13 @@
return false
}
+func (c *Module) IsStubsImplementationRequired() bool {
+ if lib := c.library; lib != nil {
+ return lib.isStubsImplementationRequired()
+ }
+ return false
+}
+
// If this is a stubs library, ImplementationModuleName returns the name of the module that contains
// the implementation. If it is an implementation library it returns its own name.
func (c *Module) ImplementationModuleName(ctx android.BaseModuleContext) string {
@@ -1387,14 +1466,16 @@
func isBionic(name string) bool {
switch name {
- case "libc", "libm", "libdl", "libdl_android", "linker", "linkerconfig":
+ case "libc", "libm", "libdl", "libdl_android", "linker":
return true
}
return false
}
func InstallToBootstrap(name string, config android.Config) bool {
- if name == "libclang_rt.hwasan" {
+ // NOTE: also update //build/bazel/rules/apex/cc.bzl#_installed_to_bootstrap
+ // if this list is updated.
+ if name == "libclang_rt.hwasan" || name == "libc_hwasan" {
return true
}
return isBionic(name)
@@ -1449,6 +1530,10 @@
return ctx.mod.testBinary()
}
+func (ctx *moduleContextImpl) testLibrary() bool {
+ return ctx.mod.testLibrary()
+}
+
func (ctx *moduleContextImpl) header() bool {
return ctx.mod.Header()
}
@@ -1563,6 +1648,10 @@
return ctx.mod.isPgoCompile()
}
+func (ctx *moduleContextImpl) isCfi() bool {
+ return ctx.mod.isCfi()
+}
+
func (ctx *moduleContextImpl) isNDKStubLibrary() bool {
return ctx.mod.isNDKStubLibrary()
}
@@ -1658,6 +1747,7 @@
module.stl = &stl{}
module.sanitize = &sanitize{}
module.coverage = &coverage{}
+ module.fuzzer = &fuzzer{}
module.sabi = &sabi{}
module.vndkdep = &vndkdep{}
module.lto = <o{}
@@ -1768,36 +1858,124 @@
if c.SplitPerApiLevel() {
subName += "." + c.SdkVersion()
}
+ } else if c.IsStubs() && c.IsSdkVariant() {
+ // Public API surface (NDK)
+ // Add a suffix to this stub variant to distinguish it from the module-lib stub variant.
+ subName = sdkSuffix
}
return subName
}
-// Returns true if Bazel was successfully used for the analysis of this module.
-func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool {
+var _ android.MixedBuildBuildable = (*Module)(nil)
+
+func (c *Module) getBazelModuleLabel(ctx android.BaseModuleContext) string {
var bazelModuleLabel 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)
+ bazelModuleLabel = bazelLabelForStaticModule(ctx, c)
} else {
- bazelModuleLabel = c.GetBazelLabel(actx, c)
+ bazelModuleLabel = c.GetBazelLabel(ctx, c)
+ }
+ labelNoPrebuilt := bazelModuleLabel
+ if c.IsPrebuilt() {
+ labelNoPrebuilt = android.RemoveOptionalPrebuiltPrefixFromBazelLabel(bazelModuleLabel)
+ }
+ return labelNoPrebuilt
+}
+
+func (c *Module) QueueBazelCall(ctx android.BaseModuleContext) {
+ c.bazelHandler.QueueBazelCall(ctx, c.getBazelModuleLabel(ctx))
+}
+
+var (
+ mixedBuildSupportedCcTest = []string{
+ "adbd_test",
+ "adb_crypto_test",
+ "adb_pairing_auth_test",
+ "adb_pairing_connection_test",
+ "adb_tls_connection_test",
+ }
+)
+
+// IsMixedBuildSupported returns true if the module should be analyzed by Bazel
+// in any of the --bazel-mode(s). This filters at the module level and takes
+// precedence over the allowlists in allowlists/allowlists.go.
+func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+ _, isForTesting := ctx.Config().BazelContext.(android.MockBazelContext)
+ if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) && !isForTesting {
+ // Per-module rollout of mixed-builds for cc_test modules.
+ return false
}
- 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)
+ // TODO(b/261058727): Remove this (enable mixed builds for modules with UBSan)
+ // Currently we can only support ubsan when minimum runtime is used.
+ return c.bazelHandler != nil && (!isUbsanEnabled(c) || c.MinimalRuntimeNeeded())
+}
+
+func isUbsanEnabled(c *Module) bool {
+ if c.sanitize == nil {
+ return false
}
- return bazelActionsUsed
+ sanitizeProps := &c.sanitize.Properties.SanitizeMutated
+ return Bool(sanitizeProps.Integer_overflow) || len(sanitizeProps.Misc_undefined) > 0
+}
+
+func GetApexConfigKey(ctx android.BaseModuleContext) *android.ApexConfigKey {
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if !apexInfo.IsForPlatform() {
+ if !ctx.Config().BazelContext.IsModuleDclaAllowed(ctx.Module().Name()) {
+ return nil
+ }
+ apexKey := android.ApexConfigKey{
+ WithinApex: true,
+ ApexSdkVersion: findApexSdkVersion(ctx, apexInfo).String(),
+ }
+ return &apexKey
+ }
+
+ return 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
+ }
+
+ c.makeLinkType = GetMakeLinkType(ctx, c)
+
+ mctx := &moduleContext{
+ ModuleContext: ctx,
+ moduleContextImpl: moduleContextImpl{
+ mod: c,
+ },
+ }
+ mctx.ctx = mctx
+
+ // TODO(b/244432500): Get the tradefed config from the bazel target instead
+ // of generating it with Soong.
+ c.maybeInstall(mctx, apexInfo)
+}
+
+func moduleContextFromAndroidModuleContext(actx android.ModuleContext, c *Module) ModuleContext {
+ ctx := &moduleContext{
+ ModuleContext: actx,
+ moduleContextImpl: moduleContextImpl{
+ mod: c,
+ },
+ }
+ ctx.ctx = ctx
+ return ctx
}
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
@@ -1816,18 +1994,7 @@
c.makeLinkType = GetMakeLinkType(actx, c)
- ctx := &moduleContext{
- ModuleContext: actx,
- moduleContextImpl: moduleContextImpl{
- mod: c,
- },
- }
- ctx.ctx = ctx
-
- if c.maybeGenerateBazelActions(actx) {
- c.maybeInstall(ctx, apexInfo)
- return
- }
+ ctx := moduleContextFromAndroidModuleContext(actx, c)
deps := c.depsToPaths(ctx)
if ctx.Failed() {
@@ -1836,6 +2003,8 @@
if c.Properties.Clang != nil && *c.Properties.Clang == false {
ctx.PropertyErrorf("clang", "false (GCC) is no longer supported")
+ } else if c.Properties.Clang != nil && !ctx.DeviceConfig().BuildBrokenClangProperty() {
+ ctx.PropertyErrorf("clang", "property is deprecated, see Changes.md file")
}
flags := Flags{
@@ -1857,6 +2026,9 @@
if c.coverage != nil {
flags, deps = c.coverage.flags(ctx, flags, deps)
}
+ if c.fuzzer != nil {
+ flags = c.fuzzer.flags(ctx, flags)
+ }
if c.lto != nil {
flags = c.lto.flags(ctx, flags)
}
@@ -1886,6 +2058,8 @@
flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-isystem "+dir.String())
}
+ flags.Local.LdFlags = append(flags.Local.LdFlags, deps.LdFlags...)
+
c.flags = flags
// We need access to all the flags seen by a source file.
if c.sabi != nil {
@@ -1942,6 +2116,9 @@
}
}
+// maybeInstall is called at the end of both GenerateAndroidBuildActions and
+// ProcessBazelQueryResponse to run the install hooks for installable modules,
+// like binaries and tests.
func (c *Module) maybeInstall(ctx ModuleContext, apexInfo android.ApexInfo) {
if !proptools.BoolDefault(c.Installable(), true) {
// If the module has been specifically configure to not be installed then
@@ -1964,6 +2141,12 @@
}
}
+func (c *Module) setAndroidMkVariablesFromCquery(info cquery.CcAndroidMkInfo) {
+ c.Properties.AndroidMkSharedLibs = info.LocalSharedLibs
+ c.Properties.AndroidMkStaticLibs = info.LocalStaticLibs
+ c.Properties.AndroidMkWholeStaticLibs = info.LocalWholeStaticLibs
+}
+
func (c *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
if c.cachedToolchain == nil {
c.cachedToolchain = config.FindToolchainWithContext(ctx)
@@ -1990,9 +2173,6 @@
if c.lto != nil {
c.lto.begin(ctx)
}
- if c.afdo != nil {
- c.afdo.begin(ctx)
- }
if c.pgo != nil {
c.pgo.begin(ctx)
}
@@ -2031,12 +2211,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)
@@ -2073,6 +2247,10 @@
}
ctx.ctx = ctx
+ if !actx.Host() || !ctx.static() || ctx.staticBinary() {
+ c.afdo.addDep(ctx, actx)
+ }
+
c.begin(ctx)
}
@@ -2101,6 +2279,13 @@
if err != nil {
ctx.PropertyErrorf("min_sdk_version", err.Error())
}
+
+ // Raise the minSdkVersion to the minimum supported for the architecture.
+ minApiForArch := MinApiForArch(ctx, m.Target().Arch.ArchType)
+ if apiLevel.LessThan(minApiForArch) {
+ apiLevel = minApiForArch
+ }
+
return []blueprint.Variation{
{Mutator: "sdk", Variation: "sdk"},
{Mutator: "version", Variation: apiLevel.String()},
@@ -2116,7 +2301,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 {
@@ -2133,6 +2318,24 @@
}
}
+func GetApiImports(c LinkableInterface, actx android.BottomUpMutatorContext) multitree.ApiImportInfo {
+ apiImportInfo := multitree.ApiImportInfo{}
+
+ if c.Device() {
+ var apiImportModule []blueprint.Module
+ if actx.OtherModuleExists("api_imports") {
+ apiImportModule = actx.AddDependency(c, nil, "api_imports")
+ if len(apiImportModule) > 0 && apiImportModule[0] != nil {
+ apiInfo := actx.OtherModuleProvider(apiImportModule[0], multitree.ApiImportsProvider).(multitree.ApiImportInfo)
+ apiImportInfo = apiInfo
+ actx.SetProvider(multitree.ApiImportsProvider, apiInfo)
+ }
+ }
+ }
+
+ return apiImportInfo
+}
+
func GetSnapshot(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext) SnapshotInfo {
// Only device modules with BOARD_VNDK_VERSION uses snapshot. Others use the zero value of
// SnapshotInfo, which provides no mappings.
@@ -2158,8 +2361,8 @@
return **snapshotInfo
}
-func RewriteSnapshotLib(lib string, snapshotMap map[string]string) string {
- if snapshot, ok := snapshotMap[lib]; ok {
+func GetReplaceModuleName(lib string, replaceMap map[string]string) string {
+ if snapshot, ok := replaceMap[lib]; ok {
return snapshot
}
@@ -2170,13 +2373,18 @@
// of names:
//
// 1. Name of an NDK library that refers to a prebuilt module.
-// For each of these, it adds the name of the prebuilt module (which will be in
-// prebuilts/ndk) to the list of nonvariant libs.
+//
+// For each of these, it adds the name of the prebuilt module (which will be in
+// prebuilts/ndk) to the list of nonvariant libs.
+//
// 2. Name of an NDK library that refers to an ndk_library module.
-// For each of these, it adds the name of the ndk_library module to the list of
-// variant libs.
+//
+// For each of these, it adds the name of the ndk_library module to the list of
+// variant libs.
+//
// 3. Anything else (so anything that isn't an NDK library).
-// It adds these to the nonvariantLibs list.
+//
+// It adds these to the nonvariantLibs list.
//
// The caller can then know to add the variantLibs dependencies differently from the
// nonvariantLibs
@@ -2188,11 +2396,11 @@
// strip #version suffix out
name, _ := StubsLibNameAndVersion(entry)
if c.InRecovery() {
- nonvariantLibs = append(nonvariantLibs, RewriteSnapshotLib(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
+ nonvariantLibs = append(nonvariantLibs, GetReplaceModuleName(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
} else if c.UseSdk() && inList(name, *getNDKKnownLibs(config)) {
variantLibs = append(variantLibs, name+ndkLibrarySuffix)
} else if c.UseVndk() {
- nonvariantLibs = append(nonvariantLibs, RewriteSnapshotLib(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
+ nonvariantLibs = append(nonvariantLibs, GetReplaceModuleName(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
} else {
// put name#version back
nonvariantLibs = append(nonvariantLibs, entry)
@@ -2201,6 +2409,46 @@
return nonvariantLibs, variantLibs
}
+func rewriteLibsForApiImports(c LinkableInterface, libs []string, replaceList map[string]string, config android.Config) ([]string, []string) {
+ nonVariantLibs := []string{}
+ variantLibs := []string{}
+
+ for _, lib := range libs {
+ replaceLibName := GetReplaceModuleName(lib, replaceList)
+ if replaceLibName == lib {
+ // Do not handle any libs which are not in API imports
+ nonVariantLibs = append(nonVariantLibs, replaceLibName)
+ } else if c.UseSdk() && inList(replaceLibName, *getNDKKnownLibs(config)) {
+ variantLibs = append(variantLibs, replaceLibName)
+ } else {
+ nonVariantLibs = append(nonVariantLibs, replaceLibName)
+ }
+ }
+
+ return nonVariantLibs, variantLibs
+}
+
+func (c *Module) shouldUseApiSurface() bool {
+ if c.Os() == android.Android && c.Target().NativeBridge != android.NativeBridgeEnabled {
+ if GetImageVariantType(c) == vendorImageVariant || GetImageVariantType(c) == productImageVariant {
+ // LLNDK Variant
+ return true
+ }
+
+ if c.Properties.IsSdkVariant {
+ // NDK Variant
+ return true
+ }
+
+ if c.isImportedApiLibrary() {
+ // API Library should depend on API headers
+ return true
+ }
+ }
+
+ return false
+}
+
func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
if !c.Enabled() {
return
@@ -2215,6 +2463,18 @@
ctx.ctx = ctx
deps := c.deps(ctx)
+ apiImportInfo := GetApiImports(c, actx)
+
+ apiNdkLibs := []string{}
+ apiLateNdkLibs := []string{}
+
+ if c.shouldUseApiSurface() {
+ deps.SharedLibs, apiNdkLibs = rewriteLibsForApiImports(c, deps.SharedLibs, apiImportInfo.SharedLibs, ctx.Config())
+ deps.LateSharedLibs, apiLateNdkLibs = rewriteLibsForApiImports(c, deps.LateSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
+ deps.SystemSharedLibs, _ = rewriteLibsForApiImports(c, deps.SystemSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
+ deps.ReexportHeaderLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportHeaderLibHeaders, apiImportInfo.SharedLibs, ctx.Config())
+ deps.ReexportSharedLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportSharedLibHeaders, apiImportInfo.SharedLibs, ctx.Config())
+ }
c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
@@ -2228,7 +2488,7 @@
deps.ReexportSharedLibHeaders, _ = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.ReexportSharedLibHeaders)
for idx, lib := range deps.RuntimeLibs {
- deps.RuntimeLibs[idx] = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).SharedLibs)
+ deps.RuntimeLibs[idx] = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).SharedLibs)
}
}
@@ -2238,9 +2498,16 @@
depTag.reexportFlags = true
}
- lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
+ // Check header lib replacement from API surface first, and then check again with VSDK
+ if c.shouldUseApiSurface() {
+ lib = GetReplaceModuleName(lib, apiImportInfo.HeaderLibs)
+ }
+ lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
- if c.IsStubs() {
+ if c.isNDKStubLibrary() {
+ // ndk_headers do not have any variations
+ actx.AddFarVariationDependencies([]blueprint.Variation{}, depTag, lib)
+ } else if c.IsStubs() && !c.isImportedApiLibrary() {
actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
depTag, lib)
} else {
@@ -2251,26 +2518,17 @@
if c.isNDKStubLibrary() {
// NDK stubs depend on their implementation because the ABI dumps are
// generated from the implementation library.
+
actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
c.ImageVariation(),
blueprint.Variation{Mutator: "link", Variation: "shared"},
), stubImplementation, c.BaseModuleName())
}
- // sysprop_library has to support both C++ and Java. So sysprop_library internally creates one
- // C++ implementation library and one Java implementation library. When a module links against
- // sysprop_library, the C++ implementation library has to be linked. syspropImplLibraries is a
- // map from sysprop_library to implementation library; it will be used in whole_static_libs,
- // static_libs, and shared_libs.
- syspropImplLibraries := syspropImplLibraries(actx.Config())
-
for _, lib := range deps.WholeStaticLibs {
depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true, reexportFlags: true}
- if impl, ok := syspropImplLibraries[lib]; ok {
- lib = impl
- }
- lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
+ lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -2286,11 +2544,7 @@
depTag.excludeInApex = true
}
- if impl, ok := syspropImplLibraries[lib]; ok {
- lib = impl
- }
-
- lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
+ lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -2304,7 +2558,7 @@
depTag := libraryDependencyTag{Kind: staticLibraryDependency, staticUnwinder: true}
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
- }, depTag, RewriteSnapshotLib(staticUnwinder(actx), GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+ }, depTag, GetReplaceModuleName(staticUnwinder(actx), GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
}
// shared lib names without the #version suffix
@@ -2319,24 +2573,37 @@
depTag.excludeInApex = true
}
- if impl, ok := syspropImplLibraries[lib]; ok {
- lib = impl
- }
-
name, version := StubsLibNameAndVersion(lib)
+ if apiLibraryName, ok := apiImportInfo.SharedLibs[name]; ok && !ctx.OtherModuleExists(name) {
+ name = apiLibraryName
+ }
sharedLibNames = append(sharedLibNames, name)
variations := []blueprint.Variation{
{Mutator: "link", Variation: "shared"},
}
- AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
+
+ if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) {
+ AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
+ }
+
+ if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok {
+ AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, apiLibraryName, version, false)
+ }
}
for _, lib := range deps.LateStaticLibs {
depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
- }, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+ }, depTag, GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+ }
+
+ for _, lib := range deps.UnexportedStaticLibs {
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency, unexportedSymbols: true}
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, depTag, GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
}
for _, lib := range deps.LateSharedLibs {
@@ -2377,11 +2644,11 @@
actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
for _, crt := range deps.CrtBegin {
actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
- RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
+ GetReplaceModuleName(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
}
for _, crt := range deps.CrtEnd {
actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
- RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
+ GetReplaceModuleName(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
}
if deps.DynamicLinker != "" {
actx.AddDependency(c, dynamicLinkerDepTag, deps.DynamicLinker)
@@ -2394,21 +2661,31 @@
{Mutator: "version", Variation: version},
{Mutator: "link", Variation: "shared"},
}, ndkStubDepTag, variantNdkLibs...)
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "version", Variation: version},
+ {Mutator: "link", Variation: "shared"},
+ }, ndkStubDepTag, apiNdkLibs...)
ndkLateStubDepTag := libraryDependencyTag{Kind: sharedLibraryDependency, Order: lateLibraryDependency, ndk: true, makeSuffix: "." + version}
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "version", Variation: version},
{Mutator: "link", Variation: "shared"},
}, ndkLateStubDepTag, variantLateNdkLibs...)
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "version", Variation: version},
+ {Mutator: "link", Variation: "shared"},
+ }, ndkLateStubDepTag, apiLateNdkLibs...)
if vndkdep := c.vndkdep; vndkdep != nil {
if vndkdep.isVndkExt() {
actx.AddVariationDependencies([]blueprint.Variation{
c.ImageVariation(),
{Mutator: "link", Variation: "shared"},
- }, vndkExtDepTag, RewriteSnapshotLib(vndkdep.getVndkExtendsModuleName(), GetSnapshot(c, &snapshotInfo, actx).SharedLibs))
+ }, vndkExtDepTag, GetReplaceModuleName(vndkdep.getVndkExtendsModuleName(), GetSnapshot(c, &snapshotInfo, actx).SharedLibs))
}
}
+
+ updateImportedLibraryDependency(ctx)
}
func BeginMutator(ctx android.BottomUpMutatorContext) {
@@ -2451,6 +2728,10 @@
}
return
}
+ // TODO(b/244244438) : Remove this once all variants are implemented
+ if ccFrom, ok := from.(*Module); ok && ccFrom.isImportedApiLibrary() {
+ return
+ }
if from.SdkVersion() == "" {
// Platform code can link to anything
return
@@ -2477,6 +2758,10 @@
// the NDK.
return
}
+ if c.isImportedApiLibrary() {
+ // Imported library from the API surface is a stub library built against interface definition.
+ return
+ }
}
if strings.HasPrefix(ctx.ModuleName(), "libclang_rt.") && to.Module().Name() == "libc++" {
@@ -2612,6 +2897,23 @@
}
}
+func findApexSdkVersion(ctx android.BaseModuleContext, apexInfo android.ApexInfo) android.ApiLevel {
+ // For the dependency from platform to apex, use the latest stubs
+ apexSdkVersion := android.FutureApiLevel
+ if !apexInfo.IsForPlatform() {
+ apexSdkVersion = apexInfo.MinSdkVersion
+ }
+
+ if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
+ // In hwasan build, we override apexSdkVersion to the FutureApiLevel(10000)
+ // so that even Q(29/Android10) apexes could use the dynamic unwinder by linking the newer stubs(e.g libc(R+)).
+ // (b/144430859)
+ apexSdkVersion = android.FutureApiLevel
+ }
+
+ return apexSdkVersion
+}
+
// Convert dependencies to paths. Returns a PathDeps containing paths
func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
var depPaths PathDeps
@@ -2627,24 +2929,61 @@
depPaths.ReexportedGeneratedHeaders = append(depPaths.ReexportedGeneratedHeaders, exporter.GeneratedHeaders...)
}
- // For the dependency from platform to apex, use the latest stubs
- c.apexSdkVersion = android.FutureApiLevel
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- if !apexInfo.IsForPlatform() {
- c.apexSdkVersion = apexInfo.MinSdkVersion
- }
+ c.apexSdkVersion = findApexSdkVersion(ctx, apexInfo)
- if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
- // In hwasan build, we override apexSdkVersion to the FutureApiLevel(10000)
- // so that even Q(29/Android10) apexes could use the dynamic unwinder by linking the newer stubs(e.g libc(R+)).
- // (b/144430859)
- c.apexSdkVersion = android.FutureApiLevel
+ skipModuleList := map[string]bool{}
+
+ var apiImportInfo multitree.ApiImportInfo
+ hasApiImportInfo := false
+
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ if dep.Name() == "api_imports" {
+ apiImportInfo = ctx.OtherModuleProvider(dep, multitree.ApiImportsProvider).(multitree.ApiImportInfo)
+ hasApiImportInfo = true
+ }
+ })
+
+ if hasApiImportInfo {
+ targetStubModuleList := map[string]string{}
+ targetOrigModuleList := map[string]string{}
+
+ // Search for dependency which both original module and API imported library with APEX stub exists
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok {
+ targetStubModuleList[apiLibrary] = depName
+ }
+ })
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ if origLibrary, ok := targetStubModuleList[depName]; ok {
+ targetOrigModuleList[origLibrary] = depName
+ }
+ })
+
+ // Decide which library should be used between original and API imported library
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ if apiLibrary, ok := targetOrigModuleList[depName]; ok {
+ if ShouldUseStubForApex(ctx, dep) {
+ skipModuleList[depName] = true
+ } else {
+ skipModuleList[apiLibrary] = true
+ }
+ }
+ })
}
ctx.VisitDirectDeps(func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
depTag := ctx.OtherModuleDependencyTag(dep)
+ if _, ok := skipModuleList[depName]; ok {
+ // skip this module because original module or API imported module matching with this should be used instead.
+ return
+ }
+
if depTag == android.DarwinUniversalVariantTag {
depPaths.DarwinSecondArchOutput = dep.(*Module).OutputFile()
return
@@ -2837,6 +3176,10 @@
panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
}
}
+ if libDepTag.unexportedSymbols {
+ depPaths.LdFlags = append(depPaths.LdFlags,
+ "-Wl,--exclude-libs="+staticLibraryInfo.StaticLibrary.Base())
+ }
}
if libDepTag.static() && !libDepTag.wholeStatic {
@@ -2976,21 +3319,8 @@
return depPaths
}
-// ChooseStubOrImpl determines whether a given dependency should be redirected to the stub variant
-// of the dependency or not, and returns the SharedLibraryInfo and FlagExporterInfo for the right
-// dependency. The stub variant is selected when the dependency crosses a boundary where each side
-// has different level of updatability. For example, if a library foo in an APEX depends on a
-// library bar which provides stable interface and exists in the platform, foo uses the stub variant
-// of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the
-// same APEX as foo, the non-stub variant of bar is used.
-func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) {
+func ShouldUseStubForApex(ctx android.ModuleContext, dep android.Module) bool {
depName := ctx.OtherModuleName(dep)
- depTag := ctx.OtherModuleDependencyTag(dep)
- libDepTag, ok := depTag.(libraryDependencyTag)
- if !ok || !libDepTag.shared() {
- panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
- }
-
thisModule, ok := ctx.Module().(android.ApexModule)
if !ok {
panic(fmt.Errorf("Not an APEX module: %q", ctx.ModuleName()))
@@ -3005,62 +3335,93 @@
bootstrap = linkable.Bootstrap()
}
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+
+ useStubs := false
+
+ if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK
+ if !apexInfo.IsForPlatform() {
+ // For platform libraries, use current version of LLNDK
+ // If this is for use_vendor apex we will apply the same rules
+ // of apex sdk enforcement below to choose right version.
+ useStubs = true
+ }
+ } else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis {
+ // If not building for APEX or the containing APEX allows the use of
+ // platform APIs, use stubs only when it is from an APEX (and not from
+ // platform) However, for host, ramdisk, vendor_ramdisk, recovery or
+ // bootstrap modules, always link to non-stub variant
+ isNotInPlatform := dep.(android.ApexModule).NotInPlatform()
+
+ isApexImportedApiLibrary := false
+
+ if cc, ok := dep.(*Module); ok {
+ if apiLibrary, ok := cc.linker.(*apiLibraryDecorator); ok {
+ if apiLibrary.hasApexStubs() {
+ isApexImportedApiLibrary = true
+ }
+ }
+ }
+
+ useStubs = (isNotInPlatform || isApexImportedApiLibrary) && !bootstrap
+
+ if useStubs {
+ // Another exception: if this module is a test for an APEX, then
+ // it is linked with the non-stub variant of a module in the APEX
+ // as if this is part of the APEX.
+ testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
+ for _, apexContents := range testFor.ApexContents {
+ if apexContents.DirectlyInApex(depName) {
+ useStubs = false
+ break
+ }
+ }
+ }
+ if useStubs {
+ // Yet another exception: If this module and the dependency are
+ // available to the same APEXes then skip stubs between their
+ // platform variants. This complements the test_for case above,
+ // which avoids the stubs on a direct APEX library dependency, by
+ // avoiding stubs for indirect test dependencies as well.
+ //
+ // TODO(b/183882457): This doesn't work if the two libraries have
+ // only partially overlapping apex_available. For that test_for
+ // modules would need to be split into APEX variants and resolved
+ // separately for each APEX they have access to.
+ if !isApexImportedApiLibrary && android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
+ useStubs = false
+ }
+ }
+ } else {
+ // If building for APEX, use stubs when the parent is in any APEX that
+ // the child is not in.
+ useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
+ }
+
+ return useStubs
+}
+
+// ChooseStubOrImpl determines whether a given dependency should be redirected to the stub variant
+// of the dependency or not, and returns the SharedLibraryInfo and FlagExporterInfo for the right
+// dependency. The stub variant is selected when the dependency crosses a boundary where each side
+// has different level of updatability. For example, if a library foo in an APEX depends on a
+// library bar which provides stable interface and exists in the platform, foo uses the stub variant
+// of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the
+// same APEX as foo, the non-stub variant of bar is used.
+func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) {
+ depTag := ctx.OtherModuleDependencyTag(dep)
+ libDepTag, ok := depTag.(libraryDependencyTag)
+ if !ok || !libDepTag.shared() {
+ panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
+ }
+
sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo)
- apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
- useStubs := false
-
- if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK
- if !apexInfo.IsForPlatform() {
- // For platform libraries, use current version of LLNDK
- // If this is for use_vendor apex we will apply the same rules
- // of apex sdk enforcement below to choose right version.
- useStubs = true
- }
- } else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis {
- // If not building for APEX or the containing APEX allows the use of
- // platform APIs, use stubs only when it is from an APEX (and not from
- // platform) However, for host, ramdisk, vendor_ramdisk, recovery or
- // bootstrap modules, always link to non-stub variant
- useStubs = dep.(android.ApexModule).NotInPlatform() && !bootstrap
- if useStubs {
- // Another exception: if this module is a test for an APEX, then
- // it is linked with the non-stub variant of a module in the APEX
- // as if this is part of the APEX.
- testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
- for _, apexContents := range testFor.ApexContents {
- if apexContents.DirectlyInApex(depName) {
- useStubs = false
- break
- }
- }
- }
- if useStubs {
- // Yet another exception: If this module and the dependency are
- // available to the same APEXes then skip stubs between their
- // platform variants. This complements the test_for case above,
- // which avoids the stubs on a direct APEX library dependency, by
- // avoiding stubs for indirect test dependencies as well.
- //
- // TODO(b/183882457): This doesn't work if the two libraries have
- // only partially overlapping apex_available. For that test_for
- // modules would need to be split into APEX variants and resolved
- // separately for each APEX they have access to.
- if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
- useStubs = false
- }
- }
- } else {
- // If building for APEX, use stubs when the parent is in any APEX that
- // the child is not in.
- useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
- }
-
// when to use (unspecified) stubs, use the latest one.
- if useStubs {
+ if ShouldUseStubForApex(ctx, dep) {
stubs := sharedLibraryStubsInfo.SharedStubLibraries
toUse := stubs[len(stubs)-1]
sharedLibraryInfo = toUse.SharedLibraryInfo
@@ -3117,7 +3478,6 @@
nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
if ccDepModule != nil {
- // TODO(ivanlozano) Support snapshots for Rust-produced C library variants.
// Use base module name for snapshots when exporting to Makefile.
if snapshotPrebuilt, ok := ccDepModule.linker.(SnapshotInterface); ok {
baseName := ccDepModule.BaseModuleName()
@@ -3203,6 +3563,11 @@
return android.Paths{c.outputFile.Path()}, nil
}
return android.Paths{}, nil
+ case "unstripped":
+ if c.linker != nil {
+ return android.PathsIfNonNil(c.linker.unstrippedOutputFilePath()), nil
+ }
+ return nil, nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
@@ -3235,6 +3600,15 @@
return false
}
+func (c *Module) testLibrary() bool {
+ if test, ok := c.linker.(interface {
+ testLibrary() bool
+ }); ok {
+ return test.testLibrary()
+ }
+ return false
+}
+
func (c *Module) benchmarkBinary() bool {
if b, ok := c.linker.(interface {
benchmarkBinary() bool
@@ -3330,7 +3704,7 @@
if lib := c.library; lib != nil {
// Stub libs and prebuilt libs in a versioned SDK are not
// installable to APEX even though they are shared libs.
- return lib.shared() && !lib.buildStubs() && c.ContainingSdk().Unversioned()
+ return lib.shared() && !lib.buildStubs()
} else if _, ok := c.linker.(testPerSrc); ok {
return true
}
@@ -3459,6 +3833,7 @@
if _, ok := c.linker.(prebuiltLinkerInterface); ok {
return nil
}
+
minSdkVersion := c.MinSdkVersion()
if minSdkVersion == "apex_inherit" {
return nil
@@ -3480,6 +3855,16 @@
return err
}
+ // A dependency only needs to support a min_sdk_version at least
+ // as high as the api level that the architecture was introduced in.
+ // This allows introducing new architectures in the platform that
+ // need to be included in apexes that normally require an older
+ // min_sdk_version.
+ minApiForArch := MinApiForArch(ctx, c.Target().Arch.ArchType)
+ if sdkVersion.LessThan(minApiForArch) {
+ sdkVersion = minApiForArch
+ }
+
if ver.GreaterThan(sdkVersion) {
return fmt.Errorf("newer SDK(%v)", ver)
}
@@ -3497,9 +3882,17 @@
// 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.
+ // TODO(b/274401041) Find a way to merge APEX variations for vendor apexes.
return c.UseVndk() && c.IsVndk()
}
+func (c *Module) overriddenModules() []string {
+ if o, ok := c.linker.(overridable); ok {
+ return o.overriddenModules()
+ }
+ return nil
+}
+
var _ snapshot.RelativeInstallPath = (*Module)(nil)
type moduleType int
@@ -3512,13 +3905,26 @@
staticLibrary
sharedLibrary
headerLibrary
+ testBin // testBinary already declared
+ ndkLibrary
)
func (c *Module) typ() moduleType {
- if c.Binary() {
+ if c.testBinary() {
+ // testBinary is also a binary, so this comes before the c.Binary()
+ // conditional. A testBinary has additional implicit dependencies and
+ // other test-only semantics.
+ return testBin
+ } else if c.Binary() {
return binary
} else if c.Object() {
return object
+ } else if c.testLibrary() {
+ // TODO(b/244431896) properly convert cc_test_library to its own macro. This
+ // will let them add implicit compile deps on gtest, for example.
+ //
+ // For now, treat them as regular shared libraries.
+ return sharedLibrary
} else if c.CcLibrary() {
static := false
shared := false
@@ -3537,6 +3943,8 @@
return staticLibrary
}
return sharedLibrary
+ } else if c.isNDKStubLibrary() {
+ return ndkLibrary
}
return unknownType
}
@@ -3546,11 +3954,19 @@
prebuilt := c.IsPrebuilt()
switch c.typ() {
case binary:
+ if prebuilt {
+ prebuiltBinaryBp2Build(ctx, c)
+ } else {
+ binaryBp2build(ctx, c)
+ }
+ case testBin:
if !prebuilt {
- binaryBp2build(ctx, c, ctx.ModuleType())
+ testBinaryBp2build(ctx, c)
}
case object:
- if !prebuilt {
+ if prebuilt {
+ prebuiltObjectBp2Build(ctx, c)
+ } else {
objectBp2Build(ctx, c)
}
case fullLibrary:
@@ -3576,9 +3992,27 @@
}
}
-//
+var _ android.ApiProvider = (*Module)(nil)
+
+func (c *Module) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+ if c.IsPrebuilt() {
+ return
+ }
+ switch c.typ() {
+ case fullLibrary:
+ apiContributionBp2Build(ctx, c)
+ case sharedLibrary:
+ apiContributionBp2Build(ctx, c)
+ case headerLibrary:
+ // Aggressively generate api targets for all header modules
+ // This is necessary since the header module does not know if it is a dep of API surface stub library
+ apiLibraryHeadersBp2Build(ctx, c)
+ case ndkLibrary:
+ ndkLibraryBp2build(ctx, c)
+ }
+}
+
// Defaults
-//
type Defaults struct {
android.ModuleBase
android.DefaultsModuleBase
@@ -3640,6 +4074,11 @@
return c.Properties.IsSdkVariant
}
+func (c *Module) isImportedApiLibrary() bool {
+ _, ok := c.linker.(*apiLibraryDecorator)
+ return ok
+}
+
func kytheExtractAllFactory() android.Singleton {
return &kytheExtractAllSingleton{}
}
@@ -3660,6 +4099,15 @@
}
}
+func (c *Module) Partition() string {
+ if p, ok := c.installer.(interface {
+ getPartition() string
+ }); ok {
+ return p.getPartition()
+ }
+ return ""
+}
+
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var BoolPtr = proptools.BoolPtr
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 893c52e..3ae4b15 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -20,12 +20,18 @@
"path/filepath"
"reflect"
"regexp"
+ "runtime"
"strings"
"testing"
"android/soong/android"
+ "android/soong/bazel/cquery"
)
+func init() {
+ registerTestMutators(android.InitRegistrationContext)
+}
+
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
@@ -39,6 +45,36 @@
}),
)
+var ccLibInApex = "cc_lib_in_apex"
+var apexVariationName = "apex28"
+var apexVersion = "28"
+
+func registerTestMutators(ctx android.RegistrationContext) {
+ ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("apex", testApexMutator).Parallel()
+ ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
+ })
+}
+
+func mixedBuildsPrepareMutator(ctx android.BottomUpMutatorContext) {
+ if m := ctx.Module(); m.Enabled() {
+ if mixedBuildMod, ok := m.(android.MixedBuildBuildable); ok {
+ if mixedBuildMod.IsMixedBuildSupported(ctx) && android.MixedBuildsEnabled(ctx) {
+ mixedBuildMod.QueueBazelCall(ctx)
+ }
+ }
+ }
+}
+
+func testApexMutator(mctx android.BottomUpMutatorContext) {
+ modules := mctx.CreateVariations(apexVariationName)
+ apexInfo := android.ApexInfo{
+ ApexVariationName: apexVariationName,
+ MinSdkVersion: android.ApiLevelForTest(apexVersion),
+ }
+ mctx.SetVariationProvider(modules[0], android.ApexInfoProvider, apexInfo)
+}
+
// testCcWithConfig runs tests using the prepareForCcTest
//
// See testCc for an explanation as to how to stop using this deprecated method.
@@ -153,6 +189,7 @@
}
func TestVendorSrc(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library {
name: "libTest",
@@ -219,6 +256,7 @@
}
func TestInstallPartition(t *testing.T) {
+ t.Parallel()
t.Helper()
ctx := prepareForCcTest.RunTestWithBp(t, `
cc_library {
@@ -351,6 +389,7 @@
}
func TestVndk(t *testing.T) {
+ t.Parallel()
bp := `
cc_library {
name: "libvndk",
@@ -568,6 +607,7 @@
}
func TestVndkWithHostSupported(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library {
name: "libvndk_host_supported",
@@ -604,6 +644,7 @@
}
func TestVndkLibrariesTxtAndroidMk(t *testing.T) {
+ t.Parallel()
bp := `
llndk_libraries_txt {
name: "llndk.libraries.txt",
@@ -620,6 +661,7 @@
}
func TestVndkUsingCoreVariant(t *testing.T) {
+ t.Parallel()
bp := `
cc_library {
name: "libvndk",
@@ -672,6 +714,7 @@
}
func TestDataLibs(t *testing.T) {
+ t.Parallel()
bp := `
cc_test_library {
name: "test_lib",
@@ -722,6 +765,7 @@
}
func TestDataLibsRelativeInstallPath(t *testing.T) {
+ t.Parallel()
bp := `
cc_test_library {
name: "test_lib",
@@ -780,6 +824,7 @@
}
func TestTestBinaryTestSuites(t *testing.T) {
+ t.Parallel()
bp := `
cc_test {
name: "main_test",
@@ -811,6 +856,7 @@
}
func TestTestLibraryTestSuites(t *testing.T) {
+ t.Parallel()
bp := `
cc_test_library {
name: "main_test_lib",
@@ -842,6 +888,7 @@
}
func TestVndkWhenVndkVersionIsNotSet(t *testing.T) {
+ t.Parallel()
ctx := testCcNoVndk(t, `
cc_library {
name: "libvndk",
@@ -898,6 +945,7 @@
}
func TestVndkModuleError(t *testing.T) {
+ t.Parallel()
// Check the error message for vendor_available and product_available properties.
testCcErrorProductVndk(t, "vndk: vendor_available must be set to true when `vndk: {enabled: true}`", `
cc_library {
@@ -939,6 +987,7 @@
}
func TestVndkDepError(t *testing.T) {
+ t.Parallel()
// Check whether an error is emitted when a VNDK lib depends on a system lib.
testCcError(t, "dependency \".*\" of \".*\" missing variant", `
cc_library {
@@ -1130,6 +1179,7 @@
}
func TestDoubleLoadbleDep(t *testing.T) {
+ t.Parallel()
// okay to link : LLNDK -> double_loadable VNDK
testCc(t, `
cc_library {
@@ -1234,6 +1284,7 @@
}
func TestDoubleLoadableDepError(t *testing.T) {
+ t.Parallel()
// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
cc_library {
@@ -1316,6 +1367,7 @@
}
func TestCheckVndkMembershipBeforeDoubleLoadable(t *testing.T) {
+ t.Parallel()
testCcError(t, "module \"libvndksp\" variant .*: .*: VNDK-SP must only depend on VNDK-SP", `
cc_library {
name: "libvndksp",
@@ -1341,6 +1393,7 @@
}
func TestVndkExt(t *testing.T) {
+ t.Parallel()
// This test checks the VNDK-Ext properties.
bp := `
cc_library {
@@ -1428,6 +1481,7 @@
}
func TestVndkExtWithoutBoardVndkVersion(t *testing.T) {
+ t.Parallel()
// This test checks the VNDK-Ext properties when BOARD_VNDK_VERSION is not set.
ctx := testCcNoVndk(t, `
cc_library {
@@ -1459,6 +1513,7 @@
}
func TestVndkExtWithoutProductVndkVersion(t *testing.T) {
+ t.Parallel()
// This test checks the VNDK-Ext properties when PRODUCT_PRODUCT_VNDK_VERSION is not set.
ctx := testCcNoProductVndk(t, `
cc_library {
@@ -1490,6 +1545,7 @@
}
func TestVndkExtError(t *testing.T) {
+ t.Parallel()
// This test ensures an error is emitted in ill-formed vndk-ext definition.
testCcError(t, "must set `vendor: true` or `product_specific: true` to set `extends: \".*\"`", `
cc_library {
@@ -1580,6 +1636,7 @@
}
func TestVndkExtInconsistentSupportSystemProcessError(t *testing.T) {
+ t.Parallel()
// This test ensures an error is emitted for inconsistent support_system_process.
testCcError(t, "module \".*\" with mismatched support_system_process", `
cc_library {
@@ -1629,6 +1686,7 @@
}
func TestVndkExtVendorAvailableFalseError(t *testing.T) {
+ t.Parallel()
// This test ensures an error is emitted when a VNDK-Ext library extends a VNDK library
// with `private: true`.
testCcError(t, "`extends` refers module \".*\" which has `private: true`", `
@@ -1679,6 +1737,7 @@
}
func TestVendorModuleUseVndkExt(t *testing.T) {
+ t.Parallel()
// This test ensures a vendor module can depend on a VNDK-Ext library.
testCc(t, `
cc_library {
@@ -1733,6 +1792,7 @@
}
func TestVndkExtUseVendorLib(t *testing.T) {
+ t.Parallel()
// This test ensures a VNDK-Ext library can depend on a vendor library.
testCc(t, `
cc_library {
@@ -1797,6 +1857,7 @@
}
func TestProductVndkExtDependency(t *testing.T) {
+ t.Parallel()
bp := `
cc_library {
name: "libvndk",
@@ -1864,6 +1925,7 @@
}
func TestVndkSpExtUseVndkError(t *testing.T) {
+ t.Parallel()
// This test ensures an error is emitted if a VNDK-SP-Ext library depends on a VNDK
// library.
testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
@@ -1950,6 +2012,7 @@
}
func TestVndkUseVndkExtError(t *testing.T) {
+ t.Parallel()
// This test ensures an error is emitted if a VNDK/VNDK-SP library depends on a
// VNDK-Ext/VNDK-SP-Ext library.
testCcError(t, "dependency \".*\" of \".*\" missing variant", `
@@ -2095,6 +2158,7 @@
}
func TestEnforceProductVndkVersion(t *testing.T) {
+ t.Parallel()
bp := `
cc_library {
name: "libllndk",
@@ -2220,6 +2284,7 @@
}
func TestEnforceProductVndkVersionErrors(t *testing.T) {
+ t.Parallel()
testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.29", `
cc_library {
name: "libprod",
@@ -2317,6 +2382,7 @@
}
func TestMakeLinkType(t *testing.T) {
+ t.Parallel()
bp := `
cc_library {
name: "libvndk",
@@ -2608,6 +2674,7 @@
}
func TestStaticLibDepReordering(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library {
name: "a",
@@ -2647,6 +2714,7 @@
}
func TestStaticLibDepReorderingWithShared(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library {
name: "a",
@@ -2694,6 +2762,7 @@
}
func TestLlndkLibrary(t *testing.T) {
+ t.Parallel()
result := prepareForCcTest.RunTestWithBp(t, `
cc_library {
name: "libllndk",
@@ -2781,6 +2850,7 @@
}
func TestLlndkHeaders(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library_headers {
name: "libllndk_headers",
@@ -2913,6 +2983,7 @@
`
func TestRuntimeLibs(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, runtimeLibAndroidBp)
// runtime_libs for core variants use the module names without suffixes.
@@ -2949,6 +3020,7 @@
}
func TestExcludeRuntimeLibs(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, runtimeLibAndroidBp)
variant := "android_arm64_armv8-a_shared"
@@ -2961,6 +3033,7 @@
}
func TestRuntimeLibsNoVndk(t *testing.T) {
+ t.Parallel()
ctx := testCcNoVndk(t, runtimeLibAndroidBp)
// If DeviceVndkVersion is not defined, then runtime_libs are copied as-is.
@@ -3001,6 +3074,7 @@
`
func TestStaticLibDepExport(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, staticLibAndroidBp)
// Check the shared version of lib2.
@@ -3015,6 +3089,253 @@
checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins"}, module)
}
+func TestLibDepAndroidMkExportInMixedBuilds(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "static_dep",
+ }
+ cc_library {
+ name: "whole_static_dep",
+ }
+ cc_library {
+ name: "shared_dep",
+ }
+ cc_library {
+ name: "lib",
+ bazel_module: { label: "//:lib" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ cc_test {
+ name: "test",
+ bazel_module: { label: "//:test" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ gtest: false,
+ }
+ cc_binary {
+ name: "binary",
+ bazel_module: { label: "//:binary" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ cc_library_headers {
+ name: "lib_headers",
+ bazel_module: { label: "//:lib_headers" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ cc_prebuilt_library {
+ name: "lib_prebuilt",
+ bazel_module: { label: "//:lib_prebuilt" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ `
+
+ testCases := []struct {
+ name string
+ moduleName string
+ variant string
+ androidMkInfo cquery.CcAndroidMkInfo
+ }{
+ {
+ name: "shared lib",
+ moduleName: "lib",
+ variant: "android_arm64_armv8-a_shared",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "static lib",
+ moduleName: "lib",
+ variant: "android_arm64_armv8-a_static",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_test arm64",
+ moduleName: "test",
+ variant: "android_arm64_armv8-a",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_test arm",
+ moduleName: "test",
+ variant: "android_arm_armv7-a-neon",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_binary",
+ moduleName: "binary",
+ variant: "android_arm64_armv8-a",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_library_headers",
+ moduleName: "lib_headers",
+ variant: "android_arm64_armv8-a",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "prebuilt lib static",
+ moduleName: "lib_prebuilt",
+ variant: "android_arm64_armv8-a_static",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "prebuilt lib shared",
+ moduleName: "lib_prebuilt",
+ variant: "android_arm64_armv8-a_shared",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ }
+
+ outputBaseDir := "out/bazel"
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outputBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//:lib": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ RootDynamicLibraries: []string{""},
+ },
+ "//:lib_bp2build_cc_library_static": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ RootStaticArchives: []string{""},
+ },
+ "//:lib_headers": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ OutputFiles: []string{""},
+ },
+ "//:lib_prebuilt": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ "//:lib_prebuilt_bp2build_cc_library_static": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ },
+ LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+ "//:test": cquery.CcUnstrippedInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ "//:binary": cquery.CcUnstrippedInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ ctx := result.TestContext
+
+ module := ctx.ModuleForTests(tc.moduleName, tc.variant).Module().(*Module)
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+ if !reflect.DeepEqual(module.Properties.AndroidMkStaticLibs, tc.androidMkInfo.LocalStaticLibs) {
+ t.Errorf("incorrect static_libs"+
+ "\nactual: %v"+
+ "\nexpected: %v",
+ module.Properties.AndroidMkStaticLibs,
+ tc.androidMkInfo.LocalStaticLibs,
+ )
+ }
+ staticDepsDiffer, missingStaticDeps, additionalStaticDeps := android.ListSetDifference(
+ entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+ tc.androidMkInfo.LocalStaticLibs,
+ )
+ if staticDepsDiffer {
+ t.Errorf(
+ "expected LOCAL_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+ tc.androidMkInfo.LocalStaticLibs,
+ entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+ missingStaticDeps,
+ additionalStaticDeps,
+ )
+ }
+
+ if !reflect.DeepEqual(module.Properties.AndroidMkWholeStaticLibs, tc.androidMkInfo.LocalWholeStaticLibs) {
+ t.Errorf("expected module.Properties.AndroidMkWholeStaticLibs to be %q, but was %q",
+ tc.androidMkInfo.LocalWholeStaticLibs,
+ module.Properties.AndroidMkWholeStaticLibs,
+ )
+ }
+ wholeStaticDepsDiffer, missingWholeStaticDeps, additionalWholeStaticDeps := android.ListSetDifference(
+ entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+ tc.androidMkInfo.LocalWholeStaticLibs,
+ )
+ if wholeStaticDepsDiffer {
+ t.Errorf(
+ "expected LOCAL_WHOLE_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+ tc.androidMkInfo.LocalWholeStaticLibs,
+ entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+ missingWholeStaticDeps,
+ additionalWholeStaticDeps,
+ )
+ }
+
+ if !reflect.DeepEqual(module.Properties.AndroidMkSharedLibs, tc.androidMkInfo.LocalSharedLibs) {
+ t.Errorf("incorrect shared_libs"+
+ "\nactual: %v"+
+ "\nexpected: %v",
+ module.Properties.AndroidMkSharedLibs,
+ tc.androidMkInfo.LocalSharedLibs,
+ )
+ }
+ sharedDepsDiffer, missingSharedDeps, additionalSharedDeps := android.ListSetDifference(
+ entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+ tc.androidMkInfo.LocalSharedLibs,
+ )
+ if sharedDepsDiffer {
+ t.Errorf(
+ "expected LOCAL_SHARED_LIBRARIES to be %q but was %q; missing %q; extra %q",
+ tc.androidMkInfo.LocalSharedLibs,
+ entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+ missingSharedDeps,
+ additionalSharedDeps,
+ )
+ }
+ })
+ }
+}
+
var compilerFlagsTestCases = []struct {
in string
out bool
@@ -3088,6 +3409,7 @@
}
func TestCompilerFlags(t *testing.T) {
+ t.Parallel()
for _, testCase := range compilerFlagsTestCases {
ctx := &mockContext{result: true}
CheckBadCompilerFlags(ctx, "", []string{testCase.in})
@@ -3101,6 +3423,7 @@
}
func TestRecovery(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library_shared {
name: "librecovery",
@@ -3136,6 +3459,7 @@
}
func TestDataLibsPrebuiltSharedTestLibrary(t *testing.T) {
+ t.Parallel()
bp := `
cc_prebuilt_test_library_shared {
name: "test_lib",
@@ -3182,6 +3506,7 @@
}
func TestVersionedStubs(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library_shared {
name: "libFoo",
@@ -3247,7 +3572,243 @@
}
}
+func TestStubsForLibraryInMultipleApexes(t *testing.T) {
+ // TODO(b/275313114): Test exposes non-determinism which should be corrected and the test
+ // reenabled.
+ t.Skip()
+ t.Parallel()
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "libFoo",
+ srcs: ["foo.c"],
+ stubs: {
+ symbol_file: "foo.map.txt",
+ versions: ["current"],
+ },
+ apex_available: ["bar", "a1"],
+ }
+
+ cc_library_shared {
+ name: "libBar",
+ srcs: ["bar.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["a1"],
+ }
+
+ cc_library_shared {
+ name: "libA1",
+ srcs: ["a1.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["a1"],
+ }
+
+ cc_library_shared {
+ name: "libBarA1",
+ srcs: ["bara1.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["bar", "a1"],
+ }
+
+ cc_library_shared {
+ name: "libAnyApex",
+ srcs: ["anyApex.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["//apex_available:anyapex"],
+ }
+
+ cc_library_shared {
+ name: "libBaz",
+ srcs: ["baz.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["baz"],
+ }
+
+ cc_library_shared {
+ name: "libQux",
+ srcs: ["qux.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["qux", "bar"],
+ }`)
+
+ variants := ctx.ModuleVariantsForTests("libFoo")
+ expectedVariants := []string{
+ "android_arm64_armv8-a_shared",
+ "android_arm64_armv8-a_shared_current",
+ "android_arm_armv7-a-neon_shared",
+ "android_arm_armv7-a-neon_shared_current",
+ }
+ variantsMismatch := false
+ if len(variants) != len(expectedVariants) {
+ variantsMismatch = true
+ } else {
+ for _, v := range expectedVariants {
+ if !inList(v, variants) {
+ variantsMismatch = false
+ }
+ }
+ }
+ if variantsMismatch {
+ t.Errorf("variants of libFoo expected:\n")
+ for _, v := range expectedVariants {
+ t.Errorf("%q\n", v)
+ }
+ t.Errorf(", but got:\n")
+ for _, v := range variants {
+ t.Errorf("%q\n", v)
+ }
+ }
+
+ linkAgainstFoo := []string{"libBarA1"}
+ linkAgainstFooStubs := []string{"libBar", "libA1", "libBaz", "libQux", "libAnyApex"}
+
+ libFooPath := "libFoo/android_arm64_armv8-a_shared/libFoo.so"
+ for _, lib := range linkAgainstFoo {
+ libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
+ libFlags := libLinkRule.Args["libFlags"]
+ if !strings.Contains(libFlags, libFooPath) {
+ t.Errorf("%q: %q is not found in %q", lib, libFooPath, libFlags)
+ }
+ }
+
+ libFooStubPath := "libFoo/android_arm64_armv8-a_shared_current/libFoo.so"
+ for _, lib := range linkAgainstFooStubs {
+ libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
+ libFlags := libLinkRule.Args["libFlags"]
+ if !strings.Contains(libFlags, libFooStubPath) {
+ t.Errorf("%q: %q is not found in %q", lib, libFooStubPath, libFlags)
+ }
+ }
+}
+
+func TestMixedBuildUsesStubs(t *testing.T) {
+ // TODO(b/275313114): Test exposes non-determinism which should be corrected and the test
+ // reenabled.
+ t.Skip()
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "libFoo",
+ bazel_module: { label: "//:libFoo" },
+ srcs: ["foo.c"],
+ stubs: {
+ symbol_file: "foo.map.txt",
+ versions: ["current"],
+ },
+ apex_available: ["bar", "a1"],
+ }
+
+ cc_library_shared {
+ name: "libBar",
+ srcs: ["bar.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["a1"],
+ }
+
+ cc_library_shared {
+ name: "libA1",
+ srcs: ["a1.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["a1"],
+ }
+
+ cc_library_shared {
+ name: "libBarA1",
+ srcs: ["bara1.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["bar", "a1"],
+ }
+
+ cc_library_shared {
+ name: "libAnyApex",
+ srcs: ["anyApex.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["//apex_available:anyapex"],
+ }
+
+ cc_library_shared {
+ name: "libBaz",
+ srcs: ["baz.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["baz"],
+ }
+
+ cc_library_shared {
+ name: "libQux",
+ srcs: ["qux.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["qux", "bar"],
+ }`
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "out/bazel",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//:libFoo": {
+ RootDynamicLibraries: []string{"libFoo.so"},
+ },
+ "//:libFoo_stub_libs-current": {
+ RootDynamicLibraries: []string{"libFoo_stub_libs-current.so"},
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ ctx := result.TestContext
+
+ variants := ctx.ModuleVariantsForTests("libFoo")
+ expectedVariants := []string{
+ "android_arm64_armv8-a_shared",
+ "android_arm64_armv8-a_shared_current",
+ "android_arm_armv7-a-neon_shared",
+ "android_arm_armv7-a-neon_shared_current",
+ }
+ variantsMismatch := false
+ if len(variants) != len(expectedVariants) {
+ variantsMismatch = true
+ } else {
+ for _, v := range expectedVariants {
+ if !inList(v, variants) {
+ variantsMismatch = false
+ }
+ }
+ }
+ if variantsMismatch {
+ t.Errorf("variants of libFoo expected:\n")
+ for _, v := range expectedVariants {
+ t.Errorf("%q\n", v)
+ }
+ t.Errorf(", but got:\n")
+ for _, v := range variants {
+ t.Errorf("%q\n", v)
+ }
+ }
+
+ linkAgainstFoo := []string{"libBarA1"}
+ linkAgainstFooStubs := []string{"libBar", "libA1", "libBaz", "libQux", "libAnyApex"}
+
+ libFooPath := "out/bazel/execroot/__main__/libFoo.so"
+ for _, lib := range linkAgainstFoo {
+ libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
+ libFlags := libLinkRule.Args["libFlags"]
+ if !strings.Contains(libFlags, libFooPath) {
+ t.Errorf("%q: %q is not found in %q", lib, libFooPath, libFlags)
+ }
+ }
+
+ libFooStubPath := "out/bazel/execroot/__main__/libFoo_stub_libs-current.so"
+ for _, lib := range linkAgainstFooStubs {
+ libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
+ libFlags := libLinkRule.Args["libFlags"]
+ if !strings.Contains(libFlags, libFooStubPath) {
+ t.Errorf("%q: %q is not found in %q", lib, libFooStubPath, libFlags)
+ }
+ }
+}
+
func TestVersioningMacro(t *testing.T) {
+ t.Parallel()
for _, tc := range []struct{ moduleName, expected string }{
{"libc", "__LIBC_API__"},
{"libfoo", "__LIBFOO_API__"},
@@ -3259,7 +3820,106 @@
}
}
+func pathsToBase(paths android.Paths) []string {
+ var ret []string
+ for _, p := range paths {
+ ret = append(ret, p.Base())
+ }
+ return ret
+}
+
+func TestStaticLibArchiveArgs(t *testing.T) {
+ t.Parallel()
+ ctx := testCc(t, `
+ cc_library_static {
+ name: "foo",
+ srcs: ["foo.c"],
+ }
+
+ cc_library_static {
+ name: "bar",
+ srcs: ["bar.c"],
+ }
+
+ cc_library_shared {
+ name: "qux",
+ srcs: ["qux.c"],
+ }
+
+ cc_library_static {
+ name: "baz",
+ srcs: ["baz.c"],
+ static_libs: ["foo"],
+ shared_libs: ["qux"],
+ whole_static_libs: ["bar"],
+ }`)
+
+ variant := "android_arm64_armv8-a_static"
+ arRule := ctx.ModuleForTests("baz", variant).Rule("ar")
+
+ // For static libraries, the object files of a whole static dep are included in the archive
+ // directly
+ if g, w := pathsToBase(arRule.Inputs), []string{"bar.o", "baz.o"}; !reflect.DeepEqual(w, g) {
+ t.Errorf("Expected input objects %q, got %q", w, g)
+ }
+
+ // non whole static dependencies are not linked into the archive
+ if len(arRule.Implicits) > 0 {
+ t.Errorf("Expected 0 additional deps, got %q", arRule.Implicits)
+ }
+}
+
+func TestSharedLibLinkingArgs(t *testing.T) {
+ t.Parallel()
+ ctx := testCc(t, `
+ cc_library_static {
+ name: "foo",
+ srcs: ["foo.c"],
+ }
+
+ cc_library_static {
+ name: "bar",
+ srcs: ["bar.c"],
+ }
+
+ cc_library_shared {
+ name: "qux",
+ srcs: ["qux.c"],
+ }
+
+ cc_library_shared {
+ name: "baz",
+ srcs: ["baz.c"],
+ static_libs: ["foo"],
+ shared_libs: ["qux"],
+ whole_static_libs: ["bar"],
+ }`)
+
+ variant := "android_arm64_armv8-a_shared"
+ linkRule := ctx.ModuleForTests("baz", variant).Rule("ld")
+ libFlags := linkRule.Args["libFlags"]
+ // When dynamically linking, we expect static dependencies to be found on the command line
+ if expected := "foo.a"; !strings.Contains(libFlags, expected) {
+ t.Errorf("Static lib %q was not found in %q", expected, libFlags)
+ }
+ // When dynamically linking, we expect whole static dependencies to be found on the command line
+ if expected := "bar.a"; !strings.Contains(libFlags, expected) {
+ t.Errorf("Static lib %q was not found in %q", expected, libFlags)
+ }
+
+ // When dynamically linking, we expect shared dependencies to be found on the command line
+ if expected := "qux.so"; !strings.Contains(libFlags, expected) {
+ t.Errorf("Shared lib %q was not found in %q", expected, libFlags)
+ }
+
+ // We should only have the objects from the shared library srcs, not the whole static dependencies
+ if g, w := pathsToBase(linkRule.Inputs), []string{"baz.o"}; !reflect.DeepEqual(w, g) {
+ t.Errorf("Expected input objects %q, got %q", w, g)
+ }
+}
+
func TestStaticExecutable(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_binary {
name: "static_test",
@@ -3285,6 +3945,7 @@
}
func TestStaticDepsOrderWithStubs(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_binary {
name: "mybin",
@@ -3325,6 +3986,7 @@
}
func TestErrorsIfAModuleDependsOnDisabled(t *testing.T) {
+ t.Parallel()
testCcError(t, `module "libA" .* depends on disabled module "libB"`, `
cc_library {
name: "libA",
@@ -3342,9 +4004,133 @@
`)
}
+func VerifyAFLFuzzTargetVariant(t *testing.T, variant string) {
+ bp := `
+ cc_fuzz {
+ name: "test_afl_fuzz_target",
+ srcs: ["foo.c"],
+ host_supported: true,
+ static_libs: [
+ "afl_fuzz_static_lib",
+ ],
+ shared_libs: [
+ "afl_fuzz_shared_lib",
+ ],
+ fuzzing_frameworks: {
+ afl: true,
+ libfuzzer: false,
+ },
+ }
+ cc_library {
+ name: "afl_fuzz_static_lib",
+ host_supported: true,
+ srcs: ["static_file.c"],
+ }
+ cc_library {
+ name: "libfuzzer_only_static_lib",
+ host_supported: true,
+ srcs: ["static_file.c"],
+ }
+ cc_library {
+ name: "afl_fuzz_shared_lib",
+ host_supported: true,
+ srcs: ["shared_file.c"],
+ static_libs: [
+ "second_static_lib",
+ ],
+ }
+ cc_library_headers {
+ name: "libafl_headers",
+ vendor_available: true,
+ host_supported: true,
+ export_include_dirs: [
+ "include",
+ "instrumentation",
+ ],
+ }
+ cc_object {
+ name: "afl-compiler-rt",
+ vendor_available: true,
+ host_supported: true,
+ cflags: [
+ "-fPIC",
+ ],
+ srcs: [
+ "instrumentation/afl-compiler-rt.o.c",
+ ],
+ }
+ cc_library {
+ name: "second_static_lib",
+ host_supported: true,
+ srcs: ["second_file.c"],
+ }
+ cc_object {
+ name: "aflpp_driver",
+ host_supported: true,
+ srcs: [
+ "aflpp_driver.c",
+ ],
+ }`
+
+ testEnv := map[string]string{
+ "FUZZ_FRAMEWORK": "AFL",
+ }
+
+ ctx := android.GroupFixturePreparers(prepareForCcTest, android.FixtureMergeEnv(testEnv)).RunTestWithBp(t, bp)
+
+ checkPcGuardFlag := func(
+ modName string, variantName string, shouldHave bool) {
+ cc := ctx.ModuleForTests(modName, variantName).Rule("cc")
+
+ cFlags, ok := cc.Args["cFlags"]
+ if !ok {
+ t.Errorf("Could not find cFlags for module %s and variant %s",
+ modName, variantName)
+ }
+
+ if strings.Contains(
+ cFlags, "-fsanitize-coverage=trace-pc-guard") != shouldHave {
+ t.Errorf("Flag was found: %t. Expected to find flag: %t. "+
+ "Test failed for module %s and variant %s",
+ !shouldHave, shouldHave, modName, variantName)
+ }
+ }
+
+ moduleName := "test_afl_fuzz_target"
+ checkPcGuardFlag(moduleName, variant+"_fuzzer", true)
+
+ moduleName = "afl_fuzz_static_lib"
+ checkPcGuardFlag(moduleName, variant+"_static", false)
+ checkPcGuardFlag(moduleName, variant+"_static_fuzzer", true)
+
+ moduleName = "second_static_lib"
+ checkPcGuardFlag(moduleName, variant+"_static", false)
+ checkPcGuardFlag(moduleName, variant+"_static_fuzzer", true)
+
+ ctx.ModuleForTests("afl_fuzz_shared_lib",
+ "android_arm64_armv8-a_shared").Rule("cc")
+ ctx.ModuleForTests("afl_fuzz_shared_lib",
+ "android_arm64_armv8-a_shared_fuzzer").Rule("cc")
+}
+
+func TestAFLFuzzTargetForDevice(t *testing.T) {
+ t.Parallel()
+ VerifyAFLFuzzTargetVariant(t, "android_arm64_armv8-a")
+}
+
+func TestAFLFuzzTargetForLinuxHost(t *testing.T) {
+ t.Parallel()
+ if runtime.GOOS != "linux" {
+ t.Skip("requires linux")
+ }
+
+ VerifyAFLFuzzTargetVariant(t, "linux_glibc_x86_64")
+}
+
// Simple smoke test for the cc_fuzz target that ensures the rule compiles
// correctly.
func TestFuzzTarget(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_fuzz {
name: "fuzz_smoke_test",
@@ -3355,9 +4141,6 @@
ctx.ModuleForTests("fuzz_smoke_test", variant).Rule("cc")
}
-func TestAidl(t *testing.T) {
-}
-
func assertString(t *testing.T, got, expected string) {
t.Helper()
if got != expected {
@@ -3382,10 +4165,11 @@
func assertMapKeys(t *testing.T, m map[string]string, expected []string) {
t.Helper()
- assertArrayString(t, android.SortedStringKeys(m), expected)
+ assertArrayString(t, android.SortedKeys(m), expected)
}
func TestDefaults(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_defaults {
name: "defaults",
@@ -3421,14 +4205,6 @@
defaults: ["defaults"],
}`)
- pathsToBase := func(paths android.Paths) []string {
- var ret []string
- for _, p := range paths {
- ret = append(ret, p.Base())
- }
- return ret
- }
-
shared := ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared").Rule("ld")
if g, w := pathsToBase(shared.Inputs), []string{"foo.o", "baz.o"}; !reflect.DeepEqual(w, g) {
t.Errorf("libshared ld rule wanted %q, got %q", w, g)
@@ -3453,6 +4229,7 @@
}
func TestProductVariableDefaults(t *testing.T) {
+ t.Parallel()
bp := `
cc_defaults {
name: "libfoo_defaults",
@@ -3514,6 +4291,7 @@
}
func TestInstallSharedLibs(t *testing.T) {
+ t.Parallel()
bp := `
cc_binary {
name: "bin",
@@ -3609,6 +4387,7 @@
}
func TestStubsLibReexportsHeaders(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library_shared {
name: "libclient",
@@ -3641,6 +4420,7 @@
}
func TestAidlFlagsPassedToTheAidlCompiler(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library {
name: "libfoo",
@@ -3659,6 +4439,7 @@
}
func TestAidlFlagsWithMinSdkVersion(t *testing.T) {
+ t.Parallel()
for _, tc := range []struct {
name string
sdkVersion string
@@ -3711,6 +4492,7 @@
}
func TestMinSdkVersionInClangTriple(t *testing.T) {
+ t.Parallel()
ctx := testCc(t, `
cc_library_shared {
name: "libfoo",
@@ -3722,7 +4504,28 @@
android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29")
}
+func TestNonDigitMinSdkVersionInClangTriple(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ min_sdk_version: "S",
+ }
+ `
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_version_active_codenames = []string{"UpsideDownCake", "Tiramisu"}
+ }),
+ ).RunTestWithBp(t, bp)
+ ctx := result.TestContext
+ cFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+ android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android31")
+}
+
func TestIncludeDirsExporting(t *testing.T) {
+ t.Parallel()
// Trim spaces from the beginning, end and immediately after any newline characters. Leaves
// embedded newline characters alone.
@@ -3970,7 +4773,7 @@
name: "libfoo",
srcs: [
"foo.c",
- "a.sysprop",
+ "path/to/a.sysprop",
"b.aidl",
"a.proto",
],
@@ -3983,17 +4786,18 @@
`),
expectedSystemIncludeDirs(``),
expectedGeneratedHeaders(`
- .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/path/to/a.sysprop.h
`),
expectedOrderOnlyDeps(`
- .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h
- .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/public/include/a.sysprop.h
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/path/to/a.sysprop.h
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/public/include/path/to/a.sysprop.h
`),
)
})
}
func TestIncludeDirectoryOrdering(t *testing.T) {
+ t.Parallel()
baseExpectedFlags := []string{
"${config.ArmThumbCflags}",
"${config.ArmCflags}",
@@ -4004,7 +4808,7 @@
"${config.ArmArmv7ANeonCflags}",
"${config.ArmGenericCflags}",
"-target",
- "armv7a-linux-androideabi20",
+ "armv7a-linux-androideabi21",
}
expectedIncludes := []string{
@@ -4035,14 +4839,13 @@
"external/foo/lib32",
"external/foo/libandroid_arm",
"defaults/cc/common/ndk_libc++_shared",
- "defaults/cc/common/ndk_libandroid_support",
}
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{
@@ -4076,7 +4879,7 @@
{
name: "assemble",
src: "foo.s",
- expected: combineSlices(baseExpectedFlags, []string{"-D__ASSEMBLY__", "-fdebug-default-version=4"}, expectedIncludes, lastIncludes),
+ expected: combineSlices(baseExpectedFlags, []string{"${config.CommonGlobalAsflags}"}, expectedIncludes, lastIncludes),
},
}
@@ -4128,20 +4931,20 @@
},
},
stl: "libc++",
- sdk_version: "20",
+ sdk_version: "minimum",
}
cc_library_headers {
name: "libheader1",
export_include_dirs: ["libheader1"],
- sdk_version: "20",
+ sdk_version: "minimum",
stl: "none",
}
cc_library_headers {
name: "libheader2",
export_include_dirs: ["libheader2"],
- sdk_version: "20",
+ sdk_version: "minimum",
stl: "none",
}
`, tc.src)
@@ -4165,7 +4968,7 @@
cc_library {
name: "%s",
export_include_dirs: ["%s"],
- sdk_version: "20",
+ sdk_version: "minimum",
stl: "none",
}
`, lib, lib)
@@ -4196,3 +4999,232 @@
}
}
+
+func TestAddnoOverride64GlobalCflags(t *testing.T) {
+ t.Parallel()
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "libclient",
+ srcs: ["foo.c"],
+ shared_libs: ["libfoo#1"],
+ }
+
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ shared_libs: ["libbar"],
+ export_shared_lib_headers: ["libbar"],
+ stubs: {
+ symbol_file: "foo.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ }
+
+ cc_library_shared {
+ name: "libbar",
+ export_include_dirs: ["include/libbar"],
+ srcs: ["foo.c"],
+ }`)
+
+ cFlags := ctx.ModuleForTests("libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+
+ if !strings.Contains(cFlags, "${config.NoOverride64GlobalCflags}") {
+ t.Errorf("expected %q in cflags, got %q", "${config.NoOverride64GlobalCflags}", cFlags)
+ }
+}
+
+func TestCcBuildBrokenClangProperty(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ name string
+ clang bool
+ BuildBrokenClangProperty bool
+ err string
+ }{
+ {
+ name: "error when clang is set to false",
+ clang: false,
+ err: "is no longer supported",
+ },
+ {
+ name: "error when clang is set to true",
+ clang: true,
+ err: "property is deprecated, see Changes.md",
+ },
+ {
+ name: "no error when BuildBrokenClangProperty is explicitly set to true",
+ clang: true,
+ BuildBrokenClangProperty: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ bp := fmt.Sprintf(`
+ cc_library {
+ name: "foo",
+ clang: %t,
+ }`, test.clang)
+
+ if test.err == "" {
+ android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if test.BuildBrokenClangProperty {
+ variables.BuildBrokenClangProperty = test.BuildBrokenClangProperty
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ } else {
+ prepareForCcTest.
+ ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(test.err)).
+ RunTestWithBp(t, bp)
+ }
+ })
+ }
+}
+
+func TestCcBuildBrokenClangAsFlags(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ name string
+ clangAsFlags []string
+ BuildBrokenClangAsFlags bool
+ err string
+ }{
+ {
+ name: "error when clang_asflags is set",
+ clangAsFlags: []string{"-a", "-b"},
+ err: "clang_asflags: property is deprecated",
+ },
+ {
+ name: "no error when BuildBrokenClangAsFlags is explicitly set to true",
+ clangAsFlags: []string{"-a", "-b"},
+ BuildBrokenClangAsFlags: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ bp := fmt.Sprintf(`
+ cc_library {
+ name: "foo",
+ clang_asflags: %s,
+ }`, `["`+strings.Join(test.clangAsFlags, `","`)+`"]`)
+
+ if test.err == "" {
+ android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if test.BuildBrokenClangAsFlags {
+ variables.BuildBrokenClangAsFlags = test.BuildBrokenClangAsFlags
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ } else {
+ prepareForCcTest.
+ ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(test.err)).
+ RunTestWithBp(t, bp)
+ }
+ })
+ }
+}
+
+func TestCcBuildBrokenClangCFlags(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ name string
+ clangCFlags []string
+ BuildBrokenClangCFlags bool
+ err string
+ }{
+ {
+ name: "error when clang_cflags is set",
+ clangCFlags: []string{"-a", "-b"},
+ err: "clang_cflags: property is deprecated",
+ },
+ {
+ name: "no error when BuildBrokenClangCFlags is explicitly set to true",
+ clangCFlags: []string{"-a", "-b"},
+ BuildBrokenClangCFlags: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ bp := fmt.Sprintf(`
+ cc_library {
+ name: "foo",
+ clang_cflags: %s,
+ }`, `["`+strings.Join(test.clangCFlags, `","`)+`"]`)
+
+ if test.err == "" {
+ android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if test.BuildBrokenClangCFlags {
+ variables.BuildBrokenClangCFlags = test.BuildBrokenClangCFlags
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ } else {
+ prepareForCcTest.
+ ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(test.err)).
+ RunTestWithBp(t, bp)
+ }
+ })
+ }
+}
+
+func TestDclaLibraryInApex(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "cc_lib_in_apex",
+ srcs: ["foo.cc"],
+ apex_available: ["myapex"],
+ bazel_module: { label: "//foo/bar:bar" },
+ }`
+ label := "//foo/bar:bar"
+ arch64 := "arm64_armv8-a"
+ arch32 := "arm_armv7-a-neon"
+ apexCfgKey := android.ApexConfigKey{
+ WithinApex: true,
+ ApexSdkVersion: "28",
+ }
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureRegisterWithContext(registerTestMutators),
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ android.BuildMockBazelContextResultKey(label, arch32, android.Android, apexCfgKey): cquery.CcInfo{
+ RootDynamicLibraries: []string{"foo.so"},
+ },
+ android.BuildMockBazelContextResultKey(label, arch64, android.Android, apexCfgKey): cquery.CcInfo{
+ RootDynamicLibraries: []string{"foo.so"},
+ },
+ },
+ BazelRequests: make(map[string]bool),
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ ctx := result.TestContext
+
+ // Test if the bazel request is queued correctly
+ key := android.BuildMockBazelContextRequestKey(label, cquery.GetCcInfo, arch32, android.Android, apexCfgKey)
+ if !ctx.Config().BazelContext.(android.MockBazelContext).BazelRequests[key] {
+ t.Errorf("Bazel request was not queued: %s", key)
+ }
+
+ sharedFoo := ctx.ModuleForTests(ccLibInApex, "android_arm_armv7-a-neon_shared_"+apexVariationName).Module()
+ producer := sharedFoo.(android.OutputFileProducer)
+ outputFiles, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.so"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+}
diff --git a/cc/cflag_artifacts.go b/cc/cflag_artifacts.go
deleted file mode 100644
index be46fc0..0000000
--- a/cc/cflag_artifacts.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package cc
-
-import (
- "fmt"
- "sort"
- "strings"
-
- "github.com/google/blueprint/proptools"
-
- "android/soong/android"
-)
-
-func init() {
- android.RegisterSingletonType("cflag_artifacts_text", cflagArtifactsTextFactory)
-}
-
-var (
- TrackedCFlags = []string{
- "-Wall",
- "-Werror",
- "-Wextra",
- "-Wthread-safety",
- "-O3",
- }
-
- TrackedCFlagsDir = []string{
- "device/google/",
- "vendor/google/",
- }
-)
-
-const FileBP = 50
-
-// Stores output files.
-type cflagArtifactsText struct {
- interOutputs map[string]android.WritablePaths
- outputs android.WritablePaths
-}
-
-// allowedDir verifies if the directory/project is part of the TrackedCFlagsDir
-// filter.
-func allowedDir(subdir string) bool {
- subdir += "/"
- return android.HasAnyPrefix(subdir, TrackedCFlagsDir)
-}
-
-func (s *cflagArtifactsText) genFlagFilename(flag string) string {
- return fmt.Sprintf("module_cflags%s.txt", flag)
-}
-
-// incrementFile is used to generate an output path object with the passed in flag
-// and part number.
-// e.g. FLAG + part # -> out/soong/cflags/module_cflags-FLAG.txt.0
-func (s *cflagArtifactsText) incrementFile(ctx android.SingletonContext,
- flag string, part int) (string, android.OutputPath) {
-
- filename := fmt.Sprintf("%s.%d", s.genFlagFilename(flag), part)
- filepath := android.PathForOutput(ctx, "cflags", filename)
- s.interOutputs[flag] = append(s.interOutputs[flag], filepath)
- return filename, filepath
-}
-
-// GenCFlagArtifactParts is used to generate the build rules which produce the
-// intermediary files for each desired C Flag artifact
-// e.g. module_cflags-FLAG.txt.0, module_cflags-FLAG.txt.1, ...
-func (s *cflagArtifactsText) GenCFlagArtifactParts(ctx android.SingletonContext,
- flag string, using bool, modules []string, part int) int {
-
- cleanedName := strings.Replace(flag, "=", "_", -1)
- filename, filepath := s.incrementFile(ctx, cleanedName, part)
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().Textf("rm -f %s", filepath.String())
-
- if using {
- rule.Command().
- Textf("echo '# Modules using %s'", flag).
- FlagWithOutput(">> ", filepath)
- } else {
- rule.Command().
- Textf("echo '# Modules not using %s'", flag).
- FlagWithOutput(">> ", filepath)
- }
-
- length := len(modules)
-
- if length == 0 {
- rule.Build(filename, "gen "+filename)
- part++
- }
-
- // Following loop splits the module list for each tracked C Flag into
- // chunks of length FileBP (file breakpoint) and generates a partial artifact
- // (intermediary file) build rule for each split.
- moduleShards := android.ShardStrings(modules, FileBP)
- for index, shard := range moduleShards {
- rule.Command().
- Textf("for m in %s; do echo $m",
- strings.Join(proptools.ShellEscapeList(shard), " ")).
- FlagWithOutput(">> ", filepath).
- Text("; done")
- rule.Build(filename, "gen "+filename)
-
- if index+1 != len(moduleShards) {
- filename, filepath = s.incrementFile(ctx, cleanedName, part+index+1)
- rule = android.NewRuleBuilder(pctx, ctx)
- rule.Command().Textf("rm -f %s", filepath.String())
- }
- }
-
- return part + len(moduleShards)
-}
-
-// GenCFlagArtifacts is used to generate build rules which combine the
-// intermediary files of a specific tracked flag into a single C Flag artifact
-// for each tracked flag.
-// e.g. module_cflags-FLAG.txt.0 + module_cflags-FLAG.txt.1 = module_cflags-FLAG.txt
-func (s *cflagArtifactsText) GenCFlagArtifacts(ctx android.SingletonContext) {
- // Scans through s.interOutputs and creates a build rule for each tracked C
- // Flag that concatenates the associated intermediary file into a single
- // artifact.
- for _, flag := range TrackedCFlags {
- // Generate build rule to combine related intermediary files into a
- // C Flag artifact
- rule := android.NewRuleBuilder(pctx, ctx)
- filename := s.genFlagFilename(flag)
- outputpath := android.PathForOutput(ctx, "cflags", filename)
- rule.Command().
- Text("cat").
- Inputs(s.interOutputs[flag].Paths()).
- FlagWithOutput("> ", outputpath)
- rule.Build(filename, "gen "+filename)
- s.outputs = append(s.outputs, outputpath)
- }
-}
-
-func (s *cflagArtifactsText) GenerateBuildActions(ctx android.SingletonContext) {
- modulesWithCFlag := make(map[string][]string)
-
- // Scan through all modules, selecting the ones that are part of the filter,
- // and then storing into a map which tracks whether or not tracked C flag is
- // used or not.
- ctx.VisitAllModules(func(module android.Module) {
- if ccModule, ok := module.(*Module); ok {
- if allowedDir(ctx.ModuleDir(ccModule)) {
- cflags := ccModule.flags.Local.CFlags
- cppflags := ccModule.flags.Local.CppFlags
- module := fmt.Sprintf("%s:%s (%s)",
- ctx.BlueprintFile(ccModule),
- ctx.ModuleName(ccModule),
- ctx.ModuleSubDir(ccModule))
- for _, flag := range TrackedCFlags {
- if inList(flag, cflags) || inList(flag, cppflags) {
- modulesWithCFlag[flag] = append(modulesWithCFlag[flag], module)
- } else {
- modulesWithCFlag["!"+flag] = append(modulesWithCFlag["!"+flag], module)
- }
- }
- }
- }
- })
-
- // Traversing map and setting up rules to produce intermediary files which
- // contain parts of each expected C Flag artifact.
- for _, flag := range TrackedCFlags {
- sort.Strings(modulesWithCFlag[flag])
- part := s.GenCFlagArtifactParts(ctx, flag, true, modulesWithCFlag[flag], 0)
- sort.Strings(modulesWithCFlag["!"+flag])
- s.GenCFlagArtifactParts(ctx, flag, false, modulesWithCFlag["!"+flag], part)
- }
-
- // Combine intermediary files into a single C Flag artifact.
- s.GenCFlagArtifacts(ctx)
-}
-
-func cflagArtifactsTextFactory() android.Singleton {
- return &cflagArtifactsText{
- interOutputs: make(map[string]android.WritablePaths),
- }
-}
-
-func (s *cflagArtifactsText) MakeVars(ctx android.MakeVarsContext) {
- ctx.Strict("SOONG_MODULES_CFLAG_ARTIFACTS", strings.Join(s.outputs.Strings(), " "))
-}
diff --git a/cc/check.go b/cc/check.go
index a357a97..58ff5b2 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -57,6 +57,10 @@
} else if strings.HasPrefix("../", path) {
ctx.PropertyErrorf(prop, "Path must not start with `../`: `%s`. Use include_dirs to -include from a different directory", flag)
}
+ } else if args[0] == "-mllvm" {
+ if len(args) > 2 {
+ ctx.PropertyErrorf(prop, "`-mllvm` only takes one argument: `%s`", flag)
+ }
} else if strings.HasPrefix(flag, "-D") && strings.Contains(flag, "=") {
// Do nothing in this case.
// For now, we allow space characters in -DNAME=def form to allow use cases
@@ -87,6 +91,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 383eed0..88985b6 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -295,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)
}
@@ -412,11 +416,6 @@
if ctx.apexVariationName() != "" {
flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
- if ctx.Device() {
- flags.Global.CommonFlags = append(flags.Global.CommonFlags,
- fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
- ctx.apexSdkVersion().FinalOrFutureInt()))
- }
}
if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -437,12 +436,24 @@
// TODO: debug
flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Release.Cflags)...)
- CheckBadCompilerFlags(ctx, "clang_cflags", compiler.Properties.Clang_cflags)
- CheckBadCompilerFlags(ctx, "clang_asflags", compiler.Properties.Clang_asflags)
+ if !ctx.DeviceConfig().BuildBrokenClangCFlags() && len(compiler.Properties.Clang_cflags) != 0 {
+ ctx.PropertyErrorf("clang_cflags", "property is deprecated, see Changes.md file")
+ } else {
+ CheckBadCompilerFlags(ctx, "clang_cflags", compiler.Properties.Clang_cflags)
+ }
+ if !ctx.DeviceConfig().BuildBrokenClangAsFlags() && len(compiler.Properties.Clang_asflags) != 0 {
+ ctx.PropertyErrorf("clang_asflags", "property is deprecated, see Changes.md file")
+ } else {
+ CheckBadCompilerFlags(ctx, "clang_asflags", compiler.Properties.Clang_asflags)
+ }
flags.Local.CFlags = config.ClangFilterUnknownCflags(flags.Local.CFlags)
- flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Clang_cflags)...)
- flags.Local.AsFlags = append(flags.Local.AsFlags, esc(compiler.Properties.Clang_asflags)...)
+ if !ctx.DeviceConfig().BuildBrokenClangCFlags() {
+ flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Clang_cflags)...)
+ }
+ if !ctx.DeviceConfig().BuildBrokenClangAsFlags() {
+ flags.Local.AsFlags = append(flags.Local.AsFlags, esc(compiler.Properties.Clang_asflags)...)
+ }
flags.Local.CppFlags = config.ClangFilterUnknownCflags(flags.Local.CppFlags)
flags.Local.ConlyFlags = config.ClangFilterUnknownCflags(flags.Local.ConlyFlags)
flags.Local.LdFlags = config.ClangFilterUnknownCflags(flags.Local.LdFlags)
@@ -453,7 +464,8 @@
if version == "" || version == "current" {
target += strconv.Itoa(android.FutureApiLevelInt)
} else {
- target += version
+ apiLevel := nativeApiLevelOrPanic(ctx, version)
+ target += apiLevel.String()
}
}
@@ -489,11 +501,7 @@
}
}
- flags.Global.AsFlags = append(flags.Global.AsFlags, "-D__ASSEMBLY__")
-
- // TODO(b/235105792): override global -fdebug-default-version=5, it is causing $TMPDIR to
- // end up in the dwarf data for crtend_so.S.
- flags.Global.AsFlags = append(flags.Global.AsFlags, "-fdebug-default-version=4")
+ flags.Global.AsFlags = append(flags.Global.AsFlags, "${config.CommonGlobalAsflags}")
flags.Global.CppFlags = append(flags.Global.CppFlags, tc.Cppflags())
@@ -564,7 +572,7 @@
flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
}
- if Bool(compiler.Properties.Aidl.Generate_traces) {
+ if proptools.BoolDefault(compiler.Properties.Aidl.Generate_traces, true) {
flags.aidlFlags = append(flags.aidlFlags, "-t")
}
@@ -593,10 +601,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...)
}
}
}
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index de163d9..fdc94ad 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -25,6 +25,7 @@
"x86_device.go",
"x86_64_device.go",
+ "arm_linux_host.go",
"darwin_host.go",
"x86_linux_host.go",
"x86_linux_bionic_host.go",
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index d7f9618..ca2e05f 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -41,6 +41,11 @@
"armv8-2a-dotprod": []string{
"-march=armv8.2-a+dotprod",
},
+ "armv9-a": []string{
+ "-march=armv8.2-a+dotprod",
+ "-mbranch-protection=standard",
+ "-fno-stack-protector",
+ },
}
arm64Ldflags = []string{
@@ -48,8 +53,7 @@
"-Wl,-z,separate-code",
}
- arm64Lldflags = append(arm64Ldflags,
- "-Wl,-z,max-page-size=4096")
+ arm64Lldflags = arm64Ldflags
arm64Cppflags = []string{}
@@ -88,7 +92,13 @@
func init() {
exportedVars.ExportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
- exportedVars.ExportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
+
+ exportedVars.ExportStringList("Arm64Lldflags", arm64Lldflags)
+ pctx.VariableFunc("Arm64Lldflags", func(ctx android.PackageVarContext) string {
+ maxPageSizeFlag := "-Wl,-z,max-page-size=" + ctx.Config().MaxPageSizeSupported()
+ flags := append(arm64Lldflags, maxPageSizeFlag)
+ return strings.Join(flags, " ")
+ })
exportedVars.ExportStringListStaticVariable("Arm64Cflags", arm64Cflags)
exportedVars.ExportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
@@ -101,6 +111,7 @@
exportedVars.ExportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"])
exportedVars.ExportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"])
exportedVars.ExportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"])
+ exportedVars.ExportStringListStaticVariable("Arm64Armv9ACflags", arm64ArchVariantCflags["armv9-a"])
exportedVars.ExportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"])
exportedVars.ExportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"])
@@ -117,6 +128,7 @@
"armv8-a-branchprot": "${config.Arm64Armv8ABranchProtCflags}",
"armv8-2a": "${config.Arm64Armv82ACflags}",
"armv8-2a-dotprod": "${config.Arm64Armv82ADotprodCflags}",
+ "armv9-a": "${config.Arm64Armv9ACflags}",
}
arm64CpuVariantCflagsVar = map[string]string{
@@ -193,6 +205,7 @@
case "armv8-a-branchprot":
case "armv8-2a":
case "armv8-2a-dotprod":
+ case "armv9-a":
// Nothing extra for armv8-a/armv8-2a
default:
panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 5c7f926..9f5124b 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -58,31 +58,31 @@
)
func init() {
- pctx.StaticVariable("LinuxBionicArm64Cflags", strings.Join(linuxCrossCflags, " "))
- pctx.StaticVariable("LinuxBionicArm64Ldflags", strings.Join(linuxCrossLdflags, " "))
+ exportedVars.ExportStringListStaticVariable("LinuxBionicArm64Cflags", linuxCrossCflags)
+ exportedVars.ExportStringListStaticVariable("LinuxBionicArm64Ldflags", linuxCrossLdflags)
}
// toolchain config for ARM64 Linux CrossHost. Almost everything is the same as the ARM64 Android
// target. The overridden methods below show the differences.
-type toolchainLinuxArm64 struct {
+type toolchainLinuxBionicArm64 struct {
toolchainArm64
}
-func (toolchainLinuxArm64) ClangTriple() string {
+func (toolchainLinuxBionicArm64) ClangTriple() string {
// Note the absence of "-android" suffix. The compiler won't define __ANDROID__
return "aarch64-linux"
}
-func (toolchainLinuxArm64) Cflags() string {
+func (toolchainLinuxBionicArm64) Cflags() string {
// The inherited flags + extra flags
return "${config.Arm64Cflags} ${config.LinuxBionicArm64Cflags}"
}
-func (toolchainLinuxArm64) CrtBeginSharedBinary() []string {
+func (toolchainLinuxBionicArm64) CrtBeginSharedBinary() []string {
return linuxArm64CrtBeginSharedBinary
}
-func linuxArm64ToolchainFactory(arch android.Arch) Toolchain {
+func linuxBionicArm64ToolchainFactory(arch android.Arch) Toolchain {
archVariant := "armv8-a" // for host, default to armv8-a
toolchainCflags := []string{arm64ArchVariantCflagsVar[archVariant]}
@@ -90,7 +90,7 @@
// the host CPU needs the fix
extraLdflags := "-Wl,--fix-cortex-a53-843419"
- ret := toolchainLinuxArm64{}
+ ret := toolchainLinuxBionicArm64{}
// add the extra ld and lld flags
ret.toolchainArm64.ldflags = strings.Join([]string{
@@ -108,5 +108,5 @@
}
func init() {
- registerToolchainFactory(android.LinuxBionic, android.Arm64, linuxArm64ToolchainFactory)
+ registerToolchainFactory(android.LinuxBionic, android.Arm64, linuxBionicArm64ToolchainFactory)
}
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index b53a097..dec2b45 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -97,6 +97,15 @@
// better solution comes around. See Bug 27340895
"-D__ARM_FEATURE_LPAE=1",
},
+ "cortex-a32": []string{
+ "-mcpu=cortex-a32",
+ "-mfpu=neon-vfpv4",
+ // Fake an ARM compiler flag as these processors support LPAE which clang
+ // don't advertise.
+ // TODO This is a hack and we need to add it for each processor that supports LPAE until some
+ // better solution comes around. See Bug 27340895
+ "-D__ARM_FEATURE_LPAE=1",
+ },
"cortex-a53": []string{
"-mcpu=cortex-a53",
"-mfpu=neon-fp-armv8",
@@ -176,7 +185,12 @@
exportedVars.ExportString("ArmClangTriple", clangTriple)
exportedVars.ExportStringListStaticVariable("ArmLdflags", armLdflags)
- exportedVars.ExportStringListStaticVariable("ArmLldflags", armLldflags)
+ exportedVars.ExportStringList("ArmLldflags", armLldflags)
+ pctx.VariableFunc("ArmLldflags", func(ctx android.PackageVarContext) string {
+ maxPageSizeFlag := "-Wl,-z,max-page-size=" + ctx.Config().MaxPageSizeSupported()
+ flags := append(armLldflags, maxPageSizeFlag)
+ return strings.Join(flags, " ")
+ })
exportedVars.ExportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags)
exportedVars.ExportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags)
@@ -204,6 +218,7 @@
exportedVars.ExportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"])
exportedVars.ExportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"])
exportedVars.ExportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"])
+ exportedVars.ExportStringListStaticVariable("ArmCortexA32Cflags", armCpuVariantCflags["cortex-a32"])
exportedVars.ExportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"])
exportedVars.ExportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"])
exportedVars.ExportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"])
@@ -222,7 +237,9 @@
"": "${config.ArmGenericCflags}",
"cortex-a7": "${config.ArmCortexA7Cflags}",
"cortex-a8": "${config.ArmCortexA8Cflags}",
+ "cortex-a9": "${config.ArmGenericCflags}",
"cortex-a15": "${config.ArmCortexA15Cflags}",
+ "cortex-a32": "${config.ArmCortexA32Cflags}",
"cortex-a53": "${config.ArmCortexA53Cflags}",
"cortex-a53.a57": "${config.ArmCortexA53Cflags}",
"cortex-a55": "${config.ArmCortexA55Cflags}",
diff --git a/cc/config/arm_linux_host.go b/cc/config/arm_linux_host.go
new file mode 100644
index 0000000..525fb5d
--- /dev/null
+++ b/cc/config/arm_linux_host.go
@@ -0,0 +1,174 @@
+// 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 config
+
+import "android/soong/android"
+
+var (
+ linuxArmCflags = []string{
+ "-march=armv7a",
+ }
+
+ linuxArm64Cflags = []string{}
+
+ linuxArmLdflags = []string{
+ "-march=armv7a",
+ }
+
+ linuxArm64Ldflags = []string{}
+)
+
+func init() {
+ exportedVars.ExportStringListStaticVariable("LinuxArmCflags", linuxArmCflags)
+ exportedVars.ExportStringListStaticVariable("LinuxArm64Cflags", linuxArm64Cflags)
+ exportedVars.ExportStringListStaticVariable("LinuxArmLdflags", linuxArmLdflags)
+ exportedVars.ExportStringListStaticVariable("LinuxArmLldflags", linuxArmLdflags)
+ exportedVars.ExportStringListStaticVariable("LinuxArm64Ldflags", linuxArm64Ldflags)
+ exportedVars.ExportStringListStaticVariable("LinuxArm64Lldflags", linuxArm64Ldflags)
+
+ exportedVars.ExportStringListStaticVariable("LinuxArmYasmFlags", []string{"-f elf32 -m arm"})
+ exportedVars.ExportStringListStaticVariable("LinuxArm64YasmFlags", []string{"-f elf64 -m aarch64"})
+
+}
+
+// Musl arm+arm64
+type toolchainLinuxArm struct {
+ toolchain32Bit
+ toolchainLinux
+}
+
+type toolchainLinuxArm64 struct {
+ toolchain64Bit
+ toolchainLinux
+}
+
+func (t *toolchainLinuxArm) Name() string {
+ return "arm"
+}
+
+func (t *toolchainLinuxArm64) Name() string {
+ return "arm64"
+}
+
+func (t *toolchainLinuxArm) Cflags() string {
+ return "${config.LinuxCflags} ${config.LinuxArmCflags}"
+}
+
+func (t *toolchainLinuxArm) Cppflags() string {
+ return ""
+}
+
+func (t *toolchainLinuxArm64) Cflags() string {
+ return "${config.LinuxCflags} ${config.LinuxArm64Cflags}"
+}
+
+func (t *toolchainLinuxArm64) Cppflags() string {
+ return ""
+}
+
+func (t *toolchainLinuxArm) Ldflags() string {
+ return "${config.LinuxLdflags} ${config.LinuxArmLdflags}"
+}
+
+func (t *toolchainLinuxArm) Lldflags() string {
+ return "${config.LinuxLldflags} ${config.LinuxArmLldflags}"
+}
+
+func (t *toolchainLinuxArm64) Ldflags() string {
+ return "${config.LinuxLdflags} ${config.LinuxArm64Ldflags}"
+}
+
+func (t *toolchainLinuxArm64) Lldflags() string {
+ return "${config.LinuxLldflags} ${config.LinuxArm64Lldflags}"
+}
+
+func (t *toolchainLinuxArm) YasmFlags() string {
+ return "${config.LinuxArmYasmFlags}"
+}
+
+func (t *toolchainLinuxArm64) YasmFlags() string {
+ return "${config.LinuxArm64YasmFlags}"
+}
+
+func (toolchainLinuxArm) LibclangRuntimeLibraryArch() string {
+ return "arm"
+}
+
+func (toolchainLinuxArm64) LibclangRuntimeLibraryArch() string {
+ return "arm64"
+}
+
+func (t *toolchainLinuxArm) InstructionSetFlags(isa string) (string, error) {
+ // TODO: Is no thumb OK?
+ return t.toolchainBase.InstructionSetFlags("")
+}
+
+type toolchainLinuxMuslArm struct {
+ toolchainLinuxArm
+ toolchainMusl
+}
+
+type toolchainLinuxMuslArm64 struct {
+ toolchainLinuxArm64
+ toolchainMusl
+}
+
+func (t *toolchainLinuxMuslArm) ClangTriple() string {
+ return "arm-linux-musleabihf"
+}
+
+func (t *toolchainLinuxMuslArm) Cflags() string {
+ return t.toolchainLinuxArm.Cflags() + " " + t.toolchainMusl.Cflags()
+}
+
+func (t *toolchainLinuxMuslArm) Ldflags() string {
+ return t.toolchainLinuxArm.Ldflags() + " " + t.toolchainMusl.Ldflags()
+}
+
+func (t *toolchainLinuxMuslArm) Lldflags() string {
+ return t.toolchainLinuxArm.Lldflags() + " " + t.toolchainMusl.Lldflags()
+}
+
+func (t *toolchainLinuxMuslArm64) ClangTriple() string {
+ return "aarch64-linux-musl"
+}
+
+func (t *toolchainLinuxMuslArm64) Cflags() string {
+ return t.toolchainLinuxArm64.Cflags() + " " + t.toolchainMusl.Cflags()
+}
+
+func (t *toolchainLinuxMuslArm64) Ldflags() string {
+ return t.toolchainLinuxArm64.Ldflags() + " " + t.toolchainMusl.Ldflags()
+}
+
+func (t *toolchainLinuxMuslArm64) Lldflags() string {
+ return t.toolchainLinuxArm64.Lldflags() + " " + t.toolchainMusl.Lldflags()
+}
+
+var toolchainLinuxMuslArmSingleton Toolchain = &toolchainLinuxMuslArm{}
+var toolchainLinuxMuslArm64Singleton Toolchain = &toolchainLinuxMuslArm64{}
+
+func linuxMuslArmToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxMuslArmSingleton
+}
+
+func linuxMuslArm64ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxMuslArm64Singleton
+}
+
+func init() {
+ registerToolchainFactory(android.LinuxMusl, android.Arm, linuxMuslArmToolchainFactory)
+ registerToolchainFactory(android.LinuxMusl, android.Arm64, linuxMuslArm64ToolchainFactory)
+}
diff --git a/cc/config/bionic.go b/cc/config/bionic.go
index e87f571..a1e3851 100644
--- a/cc/config/bionic.go
+++ b/cc/config/bionic.go
@@ -15,6 +15,7 @@
package config
type toolchainBionic struct {
+ toolchainBase
}
var (
@@ -29,6 +30,12 @@
func (toolchainBionic) DefaultSharedLibraries() []string { return bionicDefaultSharedLibraries }
+func (toolchainBionic) ShlibSuffix() string { return ".so" }
+
+func (toolchainBionic) ExecutableSuffix() string { return "" }
+
+func (toolchainBionic) AvailableLibraries() []string { return nil }
+
func (toolchainBionic) CrtBeginStaticBinary() []string { return bionicCrtBeginStaticBinary }
func (toolchainBionic) CrtBeginSharedBinary() []string { return bionicCrtBeginSharedBinary }
func (toolchainBionic) CrtBeginSharedLibrary() []string { return bionicCrtBeginSharedLibrary }
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 1667ad7..2cabdc8 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -166,6 +166,8 @@
type toolchainDarwin struct {
cFlags, ldFlags string
toolchain64Bit
+ toolchainNoCrt
+ toolchainBase
}
type toolchainDarwinX86 struct {
@@ -220,6 +222,10 @@
return ".dylib"
}
+func (t *toolchainDarwin) ExecutableSuffix() string {
+ return ""
+}
+
func (t *toolchainDarwin) AvailableLibraries() []string {
return darwinAvailableLibraries
}
diff --git a/cc/config/global.go b/cc/config/global.go
index 3caf327..a0fe4b7 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -59,6 +59,10 @@
"-Werror=string-plus-int",
"-Werror=unreachable-code-loop-increment",
+ // Force deprecation warnings to be warnings for code that compiles with -Werror.
+ // Making deprecated usages an error causes extreme pain when trying to deprecate anything.
+ "-Wno-error=deprecated-declarations",
+
"-D__compiler_offsetof=__builtin_offsetof",
// Emit address-significance table which allows linker to perform safe ICF. Clang does
@@ -72,9 +76,6 @@
// Help catch common 32/64-bit errors.
"-Werror=int-conversion",
- // Enable the new pass manager.
- "-fexperimental-new-pass-manager",
-
// Disable overly aggressive warning for macros defined with a leading underscore
// This happens in AndroidConfig.h, which is included nearly everywhere.
// TODO: can we remove this now?
@@ -87,9 +88,6 @@
// Warnings from clang-7.0
"-Wno-sign-compare",
- // Warnings from clang-8.0
- "-Wno-defaulted-function-deleted",
-
// Disable -Winconsistent-missing-override until we can clean up the existing
// codebase for it.
"-Wno-inconsistent-missing-override",
@@ -117,6 +115,13 @@
commonGlobalConlyflags = []string{}
+ commonGlobalAsflags = []string{
+ "-D__ASSEMBLY__",
+ // TODO(b/235105792): override global -fdebug-default-version=5, it is causing $TMPDIR to
+ // end up in the dwarf data for crtend_so.S.
+ "-fdebug-default-version=4",
+ }
+
deviceGlobalCflags = []string{
"-ffunction-sections",
"-fdata-sections",
@@ -139,6 +144,11 @@
"-fdebug-info-for-profiling",
}
+ commonGlobalLldflags = []string{
+ "-fuse-ld=lld",
+ "-Wl,--icf=safe",
+ }
+
deviceGlobalCppflags = []string{
"-fvisibility-inlines-hidden",
}
@@ -156,13 +166,9 @@
"-Wl,--exclude-libs,libgcc_stripped.a",
"-Wl,--exclude-libs,libunwind_llvm.a",
"-Wl,--exclude-libs,libunwind.a",
- "-Wl,--icf=safe",
}
- deviceGlobalLldflags = append(deviceGlobalLdflags,
- []string{
- "-fuse-ld=lld",
- }...)
+ deviceGlobalLldflags = append(deviceGlobalLdflags, commonGlobalLldflags...)
hostGlobalCflags = []string{}
@@ -170,7 +176,7 @@
hostGlobalLdflags = []string{}
- hostGlobalLldflags = []string{"-fuse-ld=lld"}
+ hostGlobalLldflags = commonGlobalLldflags
commonGlobalCppflags = []string{
"-Wsign-promo",
@@ -187,11 +193,11 @@
noOverrideGlobalCflags = []string{
"-Werror=bool-operation",
+ "-Werror=format-insufficient-args",
"-Werror=implicit-int-float-conversion",
"-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",
@@ -202,10 +208,7 @@
"-Werror=fortify-source",
"-Werror=address-of-temporary",
- // Bug: http://b/29823425 Disable -Wnull-dereference until the
- // new cases detected by this warning in Clang r271374 are
- // fixed.
- //"-Werror=null-dereference",
+ "-Werror=null-dereference",
"-Werror=return-type",
// http://b/72331526 Disable -Wtautological-* until the instances detected by these
@@ -217,15 +220,12 @@
// http://b/145211066
"-Wno-implicit-int-float-conversion",
// New warnings to be fixed after clang-r377782.
- "-Wno-sizeof-array-div", // http://b/148815709
"-Wno-tautological-overlap-compare", // http://b/148815696
// New warnings to be fixed after clang-r383902.
"-Wno-deprecated-copy", // http://b/153746672
"-Wno-range-loop-construct", // http://b/153747076
- "-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-string-compare", // http://b/153764102
"-Wno-pessimizing-move", // http://b/154270751
// New warnings to be fixed after clang-r399163
"-Wno-non-c-typedef-for-linkage", // http://b/161304145
@@ -234,14 +234,28 @@
// New warnings to be fixed after clang-r433403
"-Wno-error=unused-but-set-variable", // http://b/197240255
"-Wno-error=unused-but-set-parameter", // http://b/197240255
+ // New warnings to be fixed after clang-r468909
+ "-Wno-error=deprecated-builtins", // http://b/241601211
+ "-Wno-error=deprecated", // in external/googletest/googletest
+ // New warnings to be fixed after clang-r475365
+ "-Wno-error=single-bit-bitfield-constant-conversion", // http://b/243965903
+ "-Wno-error=enum-constexpr-conversion", // http://b/243964282
}
+ noOverride64GlobalCflags = []string{}
+
noOverrideExternalGlobalCflags = []string{
- // http://b/197240255
+ // http://b/191699019
+ "-Wno-format-insufficient-args",
+ "-Wno-sizeof-array-div",
+ "-Wno-incompatible-function-pointer-types",
"-Wno-unused-but-set-variable",
"-Wno-unused-but-set-parameter",
- // http://b/215753485
+ "-Wno-unqualified-std-cast-call",
"-Wno-bitwise-instead-of-logical",
+ "-Wno-misleading-indentation",
+ "-Wno-array-parameter",
+ "-Wno-gnu-offsetof-extensions",
}
// Extra cflags for external third-party projects to disable warnings that
@@ -260,9 +274,6 @@
// http://b/145211477
"-Wno-pointer-compare",
- // http://b/145211022
- "-Wno-xor-used-as-pow",
- // http://b/145211022
"-Wno-final-dtor-non-final-class",
// http://b/165945989
@@ -273,21 +284,29 @@
// http://b/175068488
"-Wno-string-concatenation",
+
+ // http://b/239661264
+ "-Wno-deprecated-non-prototype",
+ }
+
+ llvmNextExtraCommonGlobalCflags = []string{
+ // Do not report warnings when testing with the top of trunk LLVM.
+ "-Wno-error",
}
IllegalFlags = []string{
"-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-r450784d"
- ClangDefaultShortVersion = "14.0.6"
+ ClangDefaultVersion = "clang-r487747c"
+ ClangDefaultShortVersion = "17"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
@@ -295,8 +314,10 @@
"vendor/",
}
- // Directories with warnings from Android.mk files.
- WarningAllowedOldProjects = []string{}
+ VersionScriptFlagPrefix = "-Wl,--version-script,"
+
+ VisibilityHiddenFlag = "-fvisibility=hidden"
+ VisibilityDefaultFlag = "-fvisibility=default"
)
// BazelCcToolchainVars generates bzl file content containing variables for
@@ -315,6 +336,7 @@
}
exportedVars.ExportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags)
+ exportedVars.ExportStringListStaticVariable("CommonGlobalAsflags", commonGlobalAsflags)
exportedVars.ExportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags)
exportedVars.ExportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags)
exportedVars.ExportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags)
@@ -323,15 +345,7 @@
exportedVars.ExportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags)
// Export the static default CommonGlobalCflags to Bazel.
- // TODO(187086342): handle cflags that are set in VariableFuncs.
- bazelCommonGlobalCflags := append(
- commonGlobalCflags,
- []string{
- // Default to zero initialization.
- "-ftrivial-auto-var-init=zero",
- "-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang",
- }...)
- exportedVars.ExportStringList("CommonGlobalCflags", bazelCommonGlobalCflags)
+ exportedVars.ExportStringList("CommonGlobalCflags", commonGlobalCflags)
pctx.VariableFunc("CommonGlobalCflags", func(ctx android.PackageVarContext) string {
flags := commonGlobalCflags
@@ -340,14 +354,14 @@
// Automatically initialize any uninitialized stack variables.
// Prefer zero-init if multiple options are set.
if ctx.Config().IsEnvTrue("AUTO_ZERO_INITIALIZE") {
- flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
+ flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang -Wno-unused-command-line-argument")
} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
flags = append(flags, "-ftrivial-auto-var-init=pattern")
} else if ctx.Config().IsEnvTrue("AUTO_UNINITIALIZE") {
flags = append(flags, "-ftrivial-auto-var-init=uninitialized")
} else {
// Default to zero initialization.
- flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
+ flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang -Wno-unused-command-line-argument")
}
// Workaround for ccache with clang.
@@ -355,6 +369,11 @@
if ctx.Config().IsEnvTrue("USE_CCACHE") {
flags = append(flags, "-Wno-unused-command-line-argument")
}
+
+ if ctx.Config().IsEnvTrue("ALLOW_UNKNOWN_WARNING_OPTION") {
+ flags = append(flags, "-Wno-error=unknown-warning-option")
+ }
+
return strings.Join(flags, " ")
})
@@ -366,12 +385,32 @@
return strings.Join(deviceGlobalCflags, " ")
})
+ // Export the static default NoOverrideGlobalCflags to Bazel.
+ exportedVars.ExportStringList("NoOverrideGlobalCflags", noOverrideGlobalCflags)
+ pctx.VariableFunc("NoOverrideGlobalCflags", func(ctx android.PackageVarContext) string {
+ flags := noOverrideGlobalCflags
+ if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+ flags = append(noOverrideGlobalCflags, llvmNextExtraCommonGlobalCflags...)
+ }
+ return strings.Join(flags, " ")
+ })
+
+ exportedVars.ExportStringListStaticVariable("NoOverride64GlobalCflags", noOverride64GlobalCflags)
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)
+
+ exportedVars.ExportString("VersionScriptFlagPrefix", VersionScriptFlagPrefix)
+
+ exportedVars.ExportString("VisibilityHiddenFlag", VisibilityHiddenFlag)
+ exportedVars.ExportString("VisibilityDefaultFlag", VisibilityDefaultFlag)
+
// Everything in these lists is a crime against abstraction and dependency tracking.
// Do not add anything to this list.
commonGlobalIncludes := []string{
@@ -388,16 +427,13 @@
exportedVars.ExportStringList("CommonGlobalIncludes", commonGlobalIncludes)
pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes)
- 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)
+ exportedVars.ExportStringStaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion)
pctx.StaticVariable("ClangPath", "${ClangBase}/${HostPrebuiltTag}/${ClangVersion}")
pctx.StaticVariable("ClangBin", "${ClangPath}/bin")
- pctx.StaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
- pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib64/clang/${ClangShortVersion}/lib/linux")
+ exportedVars.ExportStringStaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
+ pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib/clang/${ClangShortVersion}/lib/linux")
// These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts
// being used for the rest of the build process.
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index d8918f1..a63d5c2 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -25,17 +25,22 @@
riscv64Cflags = []string{
// Help catch common 32/64-bit errors.
"-Werror=implicit-function-declaration",
+ "-fno-emulated-tls",
+ // A temporary fix for SExtWRemoval miscompilation bug.
+ "-mllvm",
+ "-riscv-disable-sextw-removal=true",
}
riscv64ArchVariantCflags = map[string][]string{}
riscv64Ldflags = []string{
"-Wl,--hash-style=gnu",
- "-Wl,-z,separate-code",
}
riscv64Lldflags = append(riscv64Ldflags,
- "-Wl,-z,max-page-size=4096")
+ "-Wl,-z,max-page-size=4096",
+ "-Wl,-plugin-opt,-emulated-tls=0",
+ )
riscv64Cppflags = []string{}
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index ba1043b..d55a13d 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -16,37 +16,100 @@
import (
"android/soong/android"
+ "regexp"
"strings"
)
+var (
+ // Some clang-tidy checks have bugs or don't work for Android.
+ // They are disabled here, overriding any locally selected checks.
+ globalNoCheckList = []string{
+ // https://b.corp.google.com/issues/153464409
+ // many local projects enable cert-* checks, which
+ // trigger bugprone-reserved-identifier.
+ "-bugprone-reserved-identifier*,-cert-dcl51-cpp,-cert-dcl37-c",
+ // http://b/153757728
+ "-readability-qualified-auto",
+ // http://b/193716442
+ "-bugprone-implicit-widening-of-multiplication-result",
+ // Too many existing functions trigger this rule, and fixing it requires large code
+ // refactoring. The cost of maintaining this tidy rule outweighs the benefit it brings.
+ "-bugprone-easily-swappable-parameters",
+ // http://b/216364337 - TODO: Follow-up after compiler update to
+ // disable or fix individual instances.
+ "-cert-err33-c",
+ // http://b/241125373
+ "-bugprone-unchecked-optional-access",
+ // http://b/265438407
+ "-misc-use-anonymous-namespace",
+ }
+
+ // Some clang-tidy checks are included in some tidy_checks_as_errors lists,
+ // but not all warnings are fixed/suppressed yet. These checks are not
+ // disabled in the TidyGlobalNoChecks list, so we can see them and fix/suppress them.
+ globalNoErrorCheckList = []string{
+ // http://b/241997913
+ "-bugprone-assignment-in-if-condition",
+ // http://b/155034972
+ "-bugprone-branch-clone",
+ // http://b/155034563
+ "-bugprone-signed-char-misuse",
+ // http://b/241819232
+ "-misc-const-correctness",
+ }
+
+ extraArgFlags = []string{
+ // We might be using the static analyzer through clang tidy.
+ // https://bugs.llvm.org/show_bug.cgi?id=32914
+ "-D__clang_analyzer__",
+
+ // A recent change in clang-tidy (r328258) enabled destructor inlining, which
+ // appears to cause a number of false positives. Until that's resolved, this turns
+ // off the effects of r328258.
+ // https://bugs.llvm.org/show_bug.cgi?id=37459
+ "-Xclang",
+ "-analyzer-config",
+ "-Xclang",
+ "c++-temp-dtor-inlining=false",
+ }
+)
+
func init() {
- // Many clang-tidy checks like altera-*, llvm-*, modernize-*
- // are not designed for Android source code or creating too
- // many (false-positive) warnings. The global default tidy checks
- // should include only tested groups and exclude known noisy checks.
+ // The global default tidy checks should include clang-tidy
+ // default checks and tested groups, but exclude known noisy checks.
// See https://clang.llvm.org/extra/clang-tidy/checks/list.html
- pctx.VariableFunc("TidyDefaultGlobalChecks", func(ctx android.PackageVarContext) string {
- if override := ctx.Config().Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
+ exportedVars.ExportVariableConfigMethod("TidyDefaultGlobalChecks", func(config android.Config) string {
+ if override := config.Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
return override
}
checks := strings.Join([]string{
- "-*",
"android-*",
"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-assignment-in-if-condition",
"-bugprone-easily-swappable-parameters",
"-bugprone-narrowing-conversions",
- "-google-readability*",
- "-google-runtime-references",
+ "-misc-const-correctness",
"-misc-no-recursion",
"-misc-non-private-member-variables-in-classes",
"-misc-unused-parameters",
- // the following groups are excluded by -*
+ "-performance-no-int-to-ptr",
+ // the following groups are not in clang-tidy default checks.
// -altera-*
// -cppcoreguidelines-*
// -darwin-*
@@ -60,44 +123,57 @@
// -readability-*
// -zircon-*
}, ",")
- // 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.
+ // clang-analyzer-* checks are slow for large files, but we have TIDY_TIMEOUT to
+ // limit clang-tidy runtime. We allow clang-tidy default clang-analyzer-* checks,
+ // and add it explicitly when CLANG_ANALYZER_CHECKS is set.
// The insecureAPI.DeprecatedOrUnsafeBufferHandling warning does not apply to Android.
- if ctx.Config().IsEnvTrue("CLANG_ANALYZER_CHECKS") {
+ if config.IsEnvTrue("CLANG_ANALYZER_CHECKS") {
checks += ",clang-analyzer-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling"
+ } else {
+ checks += ",-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling"
}
return checks
})
- // There are too many clang-tidy warnings in external and vendor projects.
- // Enable only some google checks for these projects.
- pctx.VariableFunc("TidyExternalVendorChecks", func(ctx android.PackageVarContext) string {
- if override := ctx.Config().Getenv("DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS"); override != "" {
+ // The external and vendor projects do not run clang-tidy unless TIDY_EXTERNAL_VENDOR is set.
+ // We do not add "-*" to the check list to avoid suppressing the check list in .clang-tidy config files.
+ // There are too many clang-tidy warnings in external and vendor projects, so we only
+ // enable some google checks for these projects. Users can add more checks locally with the
+ // "tidy_checks" list in .bp files, or the "Checks" list in .clang-tidy config files.
+ exportedVars.ExportVariableConfigMethod("TidyExternalVendorChecks", func(config android.Config) string {
+ if override := config.Getenv("DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS"); override != "" {
return override
}
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-*",
+ "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling",
}, ",")
})
+ exportedVars.ExportVariableFuncVariable("TidyGlobalNoChecks", func() string {
+ return strings.Join(globalNoCheckList, ",")
+ })
+
+ exportedVars.ExportVariableFuncVariable("TidyGlobalNoErrorChecks", func() string {
+ return strings.Join(globalNoErrorCheckList, ",")
+ })
+
+ exportedVars.ExportStringListStaticVariable("TidyExtraArgFlags", extraArgFlags)
+
// To reduce duplicate warnings from the same header files,
// header-filter will contain only the module directory and
// those specified by DEFAULT_TIDY_HEADER_DIRS.
- pctx.VariableFunc("TidyDefaultHeaderDirs", func(ctx android.PackageVarContext) string {
- return ctx.Config().Getenv("DEFAULT_TIDY_HEADER_DIRS")
+ exportedVars.ExportVariableConfigMethod("TidyDefaultHeaderDirs", func(config android.Config) string {
+ return config.Getenv("DEFAULT_TIDY_HEADER_DIRS")
})
// Use WTIH_TIDY_FLAGS to pass extra global default clang-tidy flags.
- pctx.VariableFunc("TidyWithTidyFlags", func(ctx android.PackageVarContext) string {
- return ctx.Config().Getenv("WITH_TIDY_FLAGS")
+ exportedVars.ExportVariableConfigMethod("TidyWithTidyFlags", func(config android.Config) string {
+ return config.Getenv("WITH_TIDY_FLAGS")
})
}
@@ -111,18 +187,21 @@
const tidyDefaultNoAnalyzer = "${config.TidyDefaultGlobalChecks},-clang-analyzer-*"
// This is a map of local path prefixes to the set of default clang-tidy checks
-// to be used.
+// to be used. This is like android.IsThirdPartyPath, but with more patterns.
// The last matched local_path_prefix should be the most specific to be used.
var DefaultLocalTidyChecks = []PathBasedTidyCheck{
{"external/", tidyExternalVendor},
- {"external/google", tidyDefault},
- {"external/webrtc", tidyDefault},
- {"external/googletest/", tidyExternalVendor},
{"frameworks/compile/mclinker/", tidyExternalVendor},
- {"hardware/qcom", tidyExternalVendor},
+ {"hardware/", tidyExternalVendor},
+ {"hardware/google/", tidyDefault},
+ {"hardware/interfaces/", tidyDefault},
+ {"hardware/ril/", tidyDefault},
+ {"hardware/libhardware", tidyDefault}, // all 'hardware/libhardware*'
{"vendor/", tidyExternalVendor},
- {"vendor/google", tidyDefault},
- {"vendor/google_devices", tidyExternalVendor},
+ {"vendor/google", tidyDefault}, // all 'vendor/google*'
+ {"vendor/google/external/", tidyExternalVendor},
+ {"vendor/google_arc/libs/org.chromium.arc.mojom", tidyExternalVendor},
+ {"vendor/google_devices/", tidyExternalVendor}, // many have vendor code
}
var reversedDefaultLocalTidyChecks = reverseTidyChecks(DefaultLocalTidyChecks)
@@ -145,6 +224,40 @@
return tidyDefault
}
+func neverTidyForDir(dir string) bool {
+ // This function can be extended if tidy needs to be disabled for more directories.
+ return strings.HasPrefix(dir, "external/grpc-grpc")
+}
+
+func NoClangTidyForDir(allowExternalVendor bool, dir string) bool {
+ // Tidy can be disable for a module in dir, if the dir is "neverTidyForDir",
+ // or if it belongs to external|vendor and !allowExternalVendor.
+ // This function depends on TidyChecksForDir, which selects tidyExternalVendor
+ // checks for external/vendor projects.
+ return neverTidyForDir(dir) ||
+ (!allowExternalVendor && TidyChecksForDir(dir) == tidyExternalVendor)
+}
+
+// Returns a globally disabled tidy checks, overriding locally selected checks.
+func TidyGlobalNoChecks() string {
+ if len(globalNoCheckList) > 0 {
+ return ",${config.TidyGlobalNoChecks}"
+ }
+ return ""
+}
+
+// Returns a globally allowed/no-error tidy checks, appended to -warnings-as-errors.
+func TidyGlobalNoErrorChecks() string {
+ if len(globalNoErrorCheckList) > 0 {
+ return ",${config.TidyGlobalNoErrorChecks}"
+ }
+ return ""
+}
+
+func TidyExtraArgFlags() []string {
+ return extraArgFlags
+}
+
func TidyFlagsForSrcFile(srcFile android.Path, flags string) string {
// Disable clang-analyzer-* checks globally for generated source files
// because some of them are too huge. Local .bp files can add wanted
@@ -158,3 +271,11 @@
}
return flags
}
+
+var (
+ removedCFlags = regexp.MustCompile(" -fsanitize=[^ ]*memtag-[^ ]* ")
+)
+
+func TidyReduceCFlags(flags string) string {
+ return removedCFlags.ReplaceAllString(flags, " ")
+}
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index e78e16a..6a10e14 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -36,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
}
@@ -144,14 +135,6 @@
return ""
}
-func (toolchainBase) ShlibSuffix() string {
- return ".so"
-}
-
-func (toolchainBase) ExecutableSuffix() string {
- return ""
-}
-
func (toolchainBase) Asflags() string {
return ""
}
@@ -164,16 +147,14 @@
return ""
}
-func (toolchainBase) AvailableLibraries() []string {
- return nil
-}
+type toolchainNoCrt struct{}
-func (toolchainBase) CrtBeginStaticBinary() []string { return nil }
-func (toolchainBase) CrtBeginSharedBinary() []string { return nil }
-func (toolchainBase) CrtBeginSharedLibrary() []string { return nil }
-func (toolchainBase) CrtEndStaticBinary() []string { return nil }
-func (toolchainBase) CrtEndSharedBinary() []string { return nil }
-func (toolchainBase) CrtEndSharedLibrary() []string { return nil }
+func (toolchainNoCrt) CrtBeginStaticBinary() []string { return nil }
+func (toolchainNoCrt) CrtBeginSharedBinary() []string { return nil }
+func (toolchainNoCrt) CrtBeginSharedLibrary() []string { return nil }
+func (toolchainNoCrt) CrtEndStaticBinary() []string { return nil }
+func (toolchainNoCrt) CrtEndSharedBinary() []string { return nil }
+func (toolchainNoCrt) CrtEndSharedLibrary() []string { return nil }
func (toolchainBase) DefaultSharedLibraries() []string {
return nil
@@ -192,7 +173,6 @@
}
type toolchain64Bit struct {
- toolchainBase
}
func (toolchain64Bit) Is64Bit() bool {
@@ -200,7 +180,6 @@
}
type toolchain32Bit struct {
- toolchainBase
}
func (toolchain32Bit) Is64Bit() bool {
@@ -233,6 +212,14 @@
return LibclangRuntimeLibrary(t, "asan")
}
+func AddressSanitizerStaticRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "asan.static")
+}
+
+func AddressSanitizerCXXStaticRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "asan_cxx.static")
+}
+
func HWAddressSanitizerRuntimeLibrary(t Toolchain) string {
return LibclangRuntimeLibrary(t, "hwasan")
}
@@ -265,4 +252,8 @@
return LibclangRuntimeLibrary(t, "fuzzer")
}
+func LibFuzzerRuntimeInterceptors(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "fuzzer_interceptors")
+}
+
var inList = android.InList
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index db69ce7..dd612ce 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -17,109 +17,9 @@
// List of VNDK libraries that have different core variant and vendor variant.
// For these libraries, the vendor variants must be installed even if the device
// has VndkUseCoreVariant set.
-// TODO(b/150578172): clean up unstable and non-versioned aidl module
+// Note that AIDL-generated modules must use vendor variants by default.
var VndkMustUseVendorVariantList = []string{
- "android.hardware.authsecret-V1-ndk",
- "android.hardware.authsecret-V1-ndk_platform",
- "android.hardware.authsecret-ndk_platform",
- "android.hardware.authsecret-unstable-ndk_platform",
- "android.hardware.automotive.occupant_awareness-V1-ndk",
- "android.hardware.automotive.occupant_awareness-V1-ndk_platform",
- "android.hardware.automotive.occupant_awareness-ndk_platform",
- "android.hardware.drm-V1-ndk",
- "android.hardware.dumpstate-V1-ndk",
- "android.hardware.gnss-V1-ndk",
- "android.hardware.gnss-V1-ndk_platform",
- "android.hardware.gnss-V2-ndk",
- "android.hardware.gnss-ndk_platform",
- "android.hardware.gnss-unstable-ndk_platform",
- "android.hardware.health-V1-ndk",
- "android.hardware.health-ndk",
- "android.hardware.health.storage-V1-ndk",
- "android.hardware.health.storage-V1-ndk_platform",
- "android.hardware.health.storage-ndk_platform",
- "android.hardware.health.storage-unstable-ndk_platform",
- "android.hardware.identity-V2-ndk_platform",
- "android.hardware.identity-V3-ndk",
- "android.hardware.identity-V3-ndk_platform",
- "android.hardware.identity-ndk_platform",
- "android.hardware.light-V1-ndk",
- "android.hardware.light-V1-ndk_platform",
- "android.hardware.light-ndk_platform",
- "android.hardware.memtrack-V1-ndk",
- "android.hardware.memtrack-V1-ndk_platform",
- "android.hardware.memtrack-ndk_platform",
- "android.hardware.memtrack-unstable-ndk_platform",
"android.hardware.nfc@1.2",
- "android.hardware.oemlock-V1-ndk",
- "android.hardware.oemlock-V1-ndk_platform",
- "android.hardware.oemlock-ndk_platform",
- "android.hardware.oemlock-unstable-ndk_platform",
- "android.hardware.power-V1-ndk_platform",
- "android.hardware.power-V2-ndk",
- "android.hardware.power-V2-ndk_platform",
- "android.hardware.power-ndk_platform",
- "android.hardware.power.stats-V1-ndk",
- "android.hardware.power.stats-V1-ndk_platform",
- "android.hardware.power.stats-ndk_platform",
- "android.hardware.power.stats-unstable-ndk_platform",
- "android.hardware.rebootescrow-V1-ndk",
- "android.hardware.rebootescrow-V1-ndk_platform",
- "android.hardware.rebootescrow-ndk_platform",
- "android.hardware.radio-V1-ndk",
- "android.hardware.radio-V1-ndk_platform",
- "android.hardware.radio.config-V1-ndk",
- "android.hardware.radio.config-V1-ndk_platform",
- "android.hardware.radio.data-V1-ndk",
- "android.hardware.radio.data-V1-ndk_platform",
- "android.hardware.radio.messaging-V1-ndk",
- "android.hardware.radio.messaging-V1-ndk_platform",
- "android.hardware.radio.modem-V1-ndk",
- "android.hardware.radio.modem-V1-ndk_platform",
- "android.hardware.radio.network-V1-ndk",
- "android.hardware.radio.network-V1-ndk_platform",
- "android.hardware.radio.sim-V1-ndk",
- "android.hardware.radio.sim-V1-ndk_platform",
- "android.hardware.radio.voice-V1-ndk",
- "android.hardware.radio.voice-V1-ndk_platform",
- "android.hardware.security.keymint-V1-ndk",
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.hardware.security.keymint-ndk_platform",
- "android.hardware.security.keymint-unstable-ndk_platform",
- "android.hardware.security.secureclock-V1-ndk",
- "android.hardware.security.secureclock-V1-ndk_platform",
- "android.hardware.security.secureclock-ndk_platform",
- "android.hardware.security.secureclock-unstable-ndk_platform",
- "android.hardware.security.sharedsecret-V1-ndk",
- "android.hardware.security.sharedsecret-V1-ndk_platform",
- "android.hardware.security.sharedsecret-ndk_platform",
- "android.hardware.security.sharedsecret-unstable-ndk_platform",
- "android.hardware.sensors-V1-ndk",
- "android.hardware.soundtrigger3-V1-ndk",
- "android.hardware.soundtrigger3-V1-ndk_platform",
- "android.hardware.uwb-V1-ndk",
- "android.hardware.uwb-V1-ndk_platform",
- "android.hardware.vibrator-V1-ndk_platform",
- "android.hardware.vibrator-V2-ndk",
- "android.hardware.vibrator-V2-ndk_platform",
- "android.hardware.vibrator-ndk_platform",
- "android.hardware.weaver-V1-ndk",
- "android.hardware.weaver-V1-ndk_platform",
- "android.hardware.weaver-ndk_platform",
- "android.hardware.weaver-unstable-ndk_platform",
- "android.system.suspend-V1-ndk",
- "android.system.keystore2-V1-ndk",
- "android.se.omapi-V1-ndk_platform",
- "android.se.omapi-ndk_platform",
- "android.se.omapi-unstable-ndk_platform",
- "android.hardware.wifi.hostapd-V1-ndk",
- "android.hardware.wifi.hostapd-V1-ndk_platform",
- "android.hardware.wifi.supplicant-V1-ndk",
- "android.system.keystore2-V1-ndk_platform",
- "android.system.keystore2-ndk_platform",
- "android.system.keystore2-unstable-ndk_platform",
- "android.system.suspend-V1-ndk",
- "android.system.suspend-V1-ndk_platform",
"libbinder",
"libcrypto",
"libexpat",
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index e2b0f06..9f093bb 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -41,6 +41,12 @@
"broadwell": []string{
"-march=broadwell",
},
+ "goldmont": []string{
+ "-march=goldmont",
+ },
+ "goldmont-plus": []string{
+ "-march=goldmont-plus",
+ },
"haswell": []string{
"-march=core-avx2",
},
@@ -59,6 +65,9 @@
"stoneyridge": []string{
"-march=bdver4",
},
+ "tremont": []string{
+ "-march=tremont",
+ },
}
x86_64ArchFeatureCflags = map[string][]string{
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 3001ab4..c826d3c 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -50,6 +50,12 @@
"broadwell": []string{
"-march=broadwell",
},
+ "goldmont": []string{
+ "-march=goldmont",
+ },
+ "goldmont-plus": []string{
+ "-march=goldmont-plus",
+ },
"haswell": []string{
"-march=core-avx2",
},
@@ -68,6 +74,9 @@
"stoneyridge": []string{
"-march=bdver4",
},
+ "tremont": []string{
+ "-march=tremont",
+ },
}
x86ArchFeatureCflags = map[string][]string{
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index 96a53bf..e006471 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -15,8 +15,6 @@
package config
import (
- "strings"
-
"android/soong/android"
)
@@ -71,14 +69,13 @@
)
func init() {
-
- pctx.StaticVariable("LinuxBionicCflags", strings.Join(linuxBionicCflags, " "))
- pctx.StaticVariable("LinuxBionicLdflags", strings.Join(linuxBionicLdflags, " "))
- pctx.StaticVariable("LinuxBionicLldflags", strings.Join(linuxBionicLdflags, " "))
+ exportedVars.ExportStringListStaticVariable("LinuxBionicCflags", linuxBionicCflags)
+ exportedVars.ExportStringListStaticVariable("LinuxBionicLdflags", linuxBionicLdflags)
+ exportedVars.ExportStringListStaticVariable("LinuxBionicLldflags", linuxBionicLdflags)
// Use the device gcc toolchain for now
- pctx.StaticVariable("LinuxBionicGccVersion", x86_64GccVersion)
- pctx.SourcePathVariable("LinuxBionicGccRoot",
+ exportedVars.ExportStringStaticVariable("LinuxBionicGccVersion", x86_64GccVersion)
+ exportedVars.ExportSourcePathVariable("LinuxBionicGccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${LinuxBionicGccVersion}")
}
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index d928838..93aa82e 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -109,10 +109,10 @@
}, "-l")
muslCrtBeginStaticBinary, muslCrtEndStaticBinary = []string{"libc_musl_crtbegin_static"}, []string{"libc_musl_crtend"}
- muslCrtBeginSharedBinary, muslCrtEndSharedBinary = []string{"libc_musl_crtbegin_dynamic", "musl_linker_script"}, []string{"libc_musl_crtend"}
+ muslCrtBeginSharedBinary, muslCrtEndSharedBinary = []string{"libc_musl_crtbegin_dynamic"}, []string{"libc_musl_crtend"}
muslCrtBeginSharedLibrary, muslCrtEndSharedLibrary = []string{"libc_musl_crtbegin_so"}, []string{"libc_musl_crtend_so"}
- muslDefaultSharedLibraries = []string{"libc_musl"}
+ MuslDefaultSharedLibraries = []string{"libc_musl"}
)
const (
@@ -158,6 +158,7 @@
}
type toolchainLinux struct {
+ toolchainBase
cFlags, ldFlags string
}
@@ -235,9 +236,18 @@
return linuxAvailableLibraries
}
+func (toolchainLinux) ShlibSuffix() string {
+ return ".so"
+}
+
+func (toolchainLinux) ExecutableSuffix() string {
+ return ""
+}
+
// glibc specialization of the linux toolchain
type toolchainGlibc struct {
+ toolchainNoCrt
}
func (toolchainGlibc) Glibc() bool { return true }
@@ -321,7 +331,7 @@
func (toolchainMusl) CrtEndSharedBinary() []string { return muslCrtEndSharedBinary }
func (toolchainMusl) CrtEndSharedLibrary() []string { return muslCrtEndSharedLibrary }
-func (toolchainMusl) DefaultSharedLibraries() []string { return muslDefaultSharedLibraries }
+func (toolchainMusl) DefaultSharedLibraries() []string { return MuslDefaultSharedLibraries }
func (toolchainMusl) Cflags() string {
return "${config.LinuxMuslCflags}"
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 5651280..561c500 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -153,6 +153,8 @@
type toolchainWindows struct {
cFlags, ldFlags string
+ toolchainBase
+ toolchainNoCrt
}
type toolchainWindowsX86 struct {
diff --git a/cc/coverage.go b/cc/coverage.go
index d0902ea..c0f6973 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -108,6 +108,12 @@
if EnableContinuousCoverage(ctx) {
flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-runtime-counter-relocation")
}
+
+ // http://b/248022906, http://b/247941801 enabling coverage and hwasan-globals
+ // instrumentation together causes duplicate-symbol errors for __llvm_profile_filename.
+ if c, ok := ctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(Hwasan) {
+ flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-hwasan-globals=0")
+ }
}
}
@@ -213,10 +219,14 @@
return properties
}
-// Coverage is an interface for non-CC modules to implement to be mutated for coverage
-type Coverage interface {
+type UseCoverage interface {
android.Module
IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool
+}
+
+// Coverage is an interface for non-CC modules to implement to be mutated for coverage
+type Coverage interface {
+ UseCoverage
SetPreventInstall()
HideFromMake()
MarkAsCoverageVariant(bool)
@@ -255,6 +265,11 @@
m[1].(Coverage).MarkAsCoverageVariant(true)
m[1].(Coverage).EnableCoverageIfNeeded()
+ } else if cov, ok := mctx.Module().(UseCoverage); ok && cov.IsNativeCoverageNeeded(mctx) {
+ // Module itself doesn't have to have "cov" variant, but it should use "cov" variants of
+ // deps.
+ mctx.CreateVariations("cov")
+ mctx.AliasVariation("cov")
}
}
diff --git a/cc/fdo_profile.go b/cc/fdo_profile.go
new file mode 100644
index 0000000..7fbe719
--- /dev/null
+++ b/cc/fdo_profile.go
@@ -0,0 +1,85 @@
+// Copyright 2023 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"
+
+ "github.com/google/blueprint"
+)
+
+func init() {
+ RegisterFdoProfileBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterFdoProfileBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("fdo_profile", fdoProfileFactory)
+}
+
+type fdoProfile struct {
+ android.ModuleBase
+
+ properties fdoProfileProperties
+}
+
+type fdoProfileProperties struct {
+ Profile *string `android:"arch_variant"`
+}
+
+// FdoProfileInfo is provided by FdoProfileProvider
+type FdoProfileInfo struct {
+ Path android.Path
+}
+
+// FdoProfileProvider is used to provide path to an fdo profile
+var FdoProfileProvider = blueprint.NewMutatorProvider(FdoProfileInfo{}, "fdo_profile")
+
+// FdoProfileMutatorInterface is the interface implemented by fdo_profile module type
+// module types that can depend on an fdo_profile module
+type FdoProfileMutatorInterface interface {
+ // FdoProfileMutator eithers set or get FdoProfileProvider
+ fdoProfileMutator(ctx android.BottomUpMutatorContext)
+}
+
+var _ FdoProfileMutatorInterface = (*fdoProfile)(nil)
+
+// GenerateAndroidBuildActions of fdo_profile does not have any build actions
+func (fp *fdoProfile) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+// FdoProfileMutator sets FdoProfileProvider to fdo_profile module
+// or sets afdo.Properties.FdoProfilePath to path in FdoProfileProvider of the depended fdo_profile
+func (fp *fdoProfile) fdoProfileMutator(ctx android.BottomUpMutatorContext) {
+ if fp.properties.Profile != nil {
+ path := android.PathForModuleSrc(ctx, *fp.properties.Profile)
+ ctx.SetProvider(FdoProfileProvider, FdoProfileInfo{
+ Path: path,
+ })
+ }
+}
+
+// fdoProfileMutator calls the generic fdoProfileMutator function of fdoProfileMutator
+// which is implemented by cc and cc.FdoProfile
+func fdoProfileMutator(ctx android.BottomUpMutatorContext) {
+ if f, ok := ctx.Module().(FdoProfileMutatorInterface); ok {
+ f.fdoProfileMutator(ctx)
+ }
+}
+
+func fdoProfileFactory() android.Module {
+ m := &fdoProfile{}
+ m.AddProperties(&m.properties)
+ android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibBoth)
+ return m
+}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 1cbd557..7aa8b91 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -27,29 +27,83 @@
)
func init() {
- android.RegisterModuleType("cc_fuzz", FuzzFactory)
+ android.RegisterModuleType("cc_fuzz", LibFuzzFactory)
android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
}
+type FuzzProperties struct {
+ FuzzFramework fuzz.Framework `blueprint:"mutated"`
+}
+
+type fuzzer struct {
+ Properties FuzzProperties
+}
+
+func (fuzzer *fuzzer) flags(ctx ModuleContext, flags Flags) Flags {
+ if fuzzer.Properties.FuzzFramework == fuzz.AFL {
+ flags.Local.CFlags = append(flags.Local.CFlags, []string{
+ "-fsanitize-coverage=trace-pc-guard",
+ "-Wno-unused-result",
+ "-Wno-unused-parameter",
+ "-Wno-unused-function",
+ }...)
+ }
+
+ return flags
+}
+
+func (fuzzer *fuzzer) props() []interface{} {
+ return []interface{}{&fuzzer.Properties}
+}
+
+func fuzzMutatorDeps(mctx android.TopDownMutatorContext) {
+ currentModule, ok := mctx.Module().(*Module)
+ if !ok {
+ return
+ }
+
+ if currentModule.fuzzer == nil {
+ return
+ }
+
+ mctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+ c, ok := child.(*Module)
+ if !ok {
+ return false
+ }
+
+ if c.sanitize == nil {
+ return false
+ }
+
+ isFuzzerPointer := c.sanitize.getSanitizerBoolPtr(Fuzzer)
+ if isFuzzerPointer == nil || !*isFuzzerPointer {
+ return false
+ }
+
+ if c.fuzzer == nil {
+ return false
+ }
+
+ c.fuzzer.Properties.FuzzFramework = currentModule.fuzzer.Properties.FuzzFramework
+ return true
+ })
+}
+
// cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at
// $ANDROID_HOST_OUT/fuzz/, and device binaries can be found at /data/fuzz on
// your device, or $ANDROID_PRODUCT_OUT/data/fuzz in your build tree.
-func FuzzFactory() android.Module {
- module := NewFuzz(android.HostAndDeviceSupported)
+func LibFuzzFactory() android.Module {
+ module := NewFuzzer(android.HostAndDeviceSupported)
return module.Init()
}
-func NewFuzzInstaller() *baseInstaller {
- return NewBaseInstaller("fuzz", "fuzz", InstallInData)
-}
-
type fuzzBinary struct {
*binaryDecorator
*baseCompiler
-
- fuzzPackagedModule fuzz.FuzzPackagedModule
-
+ fuzzPackagedModule fuzz.FuzzPackagedModule
installedSharedDeps []string
+ sharedLibraries android.Paths
}
func (fuzz *fuzzBinary) fuzzBinary() bool {
@@ -59,6 +113,7 @@
func (fuzz *fuzzBinary) linkerProps() []interface{} {
props := fuzz.binaryDecorator.linkerProps()
props = append(props, &fuzz.fuzzPackagedModule.FuzzProperties)
+
return props
}
@@ -66,10 +121,22 @@
fuzz.binaryDecorator.linkerInit(ctx)
}
-func (fuzz *fuzzBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
- deps.StaticLibs = append(deps.StaticLibs,
- config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
- deps = fuzz.binaryDecorator.linkerDeps(ctx, deps)
+func (fuzzBin *fuzzBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
+ if ctx.Config().Getenv("FUZZ_FRAMEWORK") == "AFL" {
+ deps.HeaderLibs = append(deps.HeaderLibs, "libafl_headers")
+ } else {
+ deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
+ // Fuzzers built with HWASAN should use the interceptors for better
+ // mutation based on signals in strcmp, memcpy, etc. This is only needed for
+ // fuzz targets, not generic HWASAN-ified binaries or libraries.
+ if module, ok := ctx.Module().(*Module); ok {
+ if module.IsSanitizerEnabled(Hwasan) {
+ deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeInterceptors(ctx.toolchain()))
+ }
+ }
+ }
+
+ deps = fuzzBin.binaryDecorator.linkerDeps(ctx, deps)
return deps
}
@@ -78,16 +145,19 @@
// RunPaths on devices isn't instantiated by the base linker. `../lib` for
// installed fuzz targets (both host and device), and `./lib` for fuzz
// target packages.
- flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
- return flags
-}
-func UnstrippedOutputFile(module android.Module) android.Path {
- if mod, ok := module.(LinkableInterface); ok {
- return mod.UnstrippedOutputFile()
+ // When running on device, fuzz targets with vendor: true set will be in
+ // fuzzer_name/vendor/fuzzer_name (note the extra 'vendor' and thus need to
+ // link with libraries in ../../lib/. Non-vendor binaries only need to look
+ // one level up, in ../lib/.
+ if ctx.inVendor() {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../../lib`)
+ } else {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
}
- panic("UnstrippedOutputFile called on non-LinkableInterface module: " + module.Name())
+
+ return flags
}
// IsValidSharedDependency takes a module and determines if it is a unique shared library
@@ -139,121 +209,115 @@
return false
}
- // Discard versioned members of SDK snapshots, because they will conflict with
- // unversioned ones.
- if sdkMember, ok := dependency.(android.SdkAware); ok && !sdkMember.ContainingSdk().Unversioned() {
- return false
- }
-
return true
}
-func sharedLibraryInstallLocation(
- libraryPath android.Path, isHost bool, archString string) string {
+func SharedLibraryInstallLocation(
+ libraryPath android.Path, isHost bool, fuzzDir string, archString string) string {
installLocation := "$(PRODUCT_OUT)/data"
if isHost {
installLocation = "$(HOST_OUT)"
}
installLocation = filepath.Join(
- installLocation, "fuzz", archString, "lib", libraryPath.Base())
+ installLocation, fuzzDir, archString, "lib", libraryPath.Base())
return installLocation
}
// Get the device-only shared library symbols install directory.
-func sharedLibrarySymbolsInstallLocation(libraryPath android.Path, archString string) string {
- return filepath.Join("$(PRODUCT_OUT)/symbols/data/fuzz/", archString, "/lib/", libraryPath.Base())
+func SharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string {
+ return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryPath.Base())
}
-func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) {
- fuzz.binaryDecorator.baseInstaller.dir = filepath.Join(
- "fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
- fuzz.binaryDecorator.baseInstaller.dir64 = filepath.Join(
- "fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
- fuzz.binaryDecorator.baseInstaller.install(ctx, file)
+func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
+ installBase := "fuzz"
- fuzz.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Corpus)
+ fuzzBin.binaryDecorator.baseInstaller.dir = filepath.Join(
+ installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+ fuzzBin.binaryDecorator.baseInstaller.dir64 = filepath.Join(
+ installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+ fuzzBin.binaryDecorator.baseInstaller.install(ctx, file)
+
+ fuzzBin.fuzzPackagedModule = PackageFuzzModule(ctx, fuzzBin.fuzzPackagedModule, pctx)
+
+ // Grab the list of required shared libraries.
+ fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
+
+ for _, lib := range fuzzBin.sharedLibraries {
+ fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
+ SharedLibraryInstallLocation(
+ lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+
+ // Also add the dependency on the shared library symbols dir.
+ if !ctx.Host() {
+ fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
+ SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+ }
+ }
+}
+
+func PackageFuzzModule(ctx android.ModuleContext, fuzzPackagedModule fuzz.FuzzPackagedModule, pctx android.PackageContext) fuzz.FuzzPackagedModule {
+ fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Corpus)
builder := android.NewRuleBuilder(pctx, ctx)
intermediateDir := android.PathForModuleOut(ctx, "corpus")
- for _, entry := range fuzz.fuzzPackagedModule.Corpus {
+ for _, entry := range fuzzPackagedModule.Corpus {
builder.Command().Text("cp").
Input(entry).
Output(intermediateDir.Join(ctx, entry.Base()))
}
builder.Build("copy_corpus", "copy corpus")
- fuzz.fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
+ fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
- fuzz.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Data)
+ fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Data)
builder = android.NewRuleBuilder(pctx, ctx)
intermediateDir = android.PathForModuleOut(ctx, "data")
- for _, entry := range fuzz.fuzzPackagedModule.Data {
+ for _, entry := range fuzzPackagedModule.Data {
builder.Command().Text("cp").
Input(entry).
Output(intermediateDir.Join(ctx, entry.Rel()))
}
builder.Build("copy_data", "copy data")
- fuzz.fuzzPackagedModule.DataIntermediateDir = intermediateDir
+ fuzzPackagedModule.DataIntermediateDir = intermediateDir
- if fuzz.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
- fuzz.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzz.fuzzPackagedModule.FuzzProperties.Dictionary)
- if fuzz.fuzzPackagedModule.Dictionary.Ext() != ".dict" {
+ if fuzzPackagedModule.FuzzProperties.Dictionary != nil {
+ fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzPackagedModule.FuzzProperties.Dictionary)
+ if fuzzPackagedModule.Dictionary.Ext() != ".dict" {
ctx.PropertyErrorf("dictionary",
"Fuzzer dictionary %q does not have '.dict' extension",
- fuzz.fuzzPackagedModule.Dictionary.String())
+ fuzzPackagedModule.Dictionary.String())
}
}
- if fuzz.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
+ if fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
- android.WriteFileRule(ctx, configPath, fuzz.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
- fuzz.fuzzPackagedModule.Config = configPath
+ android.WriteFileRule(ctx, configPath, fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
+ fuzzPackagedModule.Config = configPath
}
-
- // Grab the list of required shared libraries.
- seen := make(map[string]bool)
- var sharedLibraries android.Paths
- ctx.WalkDeps(func(child, parent android.Module) bool {
- if seen[child.Name()] {
- return false
- }
- seen[child.Name()] = true
-
- if IsValidSharedDependency(child) {
- sharedLibraries = append(sharedLibraries, child.(*Module).UnstrippedOutputFile())
- return true
- }
- return false
- })
-
- for _, lib := range sharedLibraries {
- fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
- sharedLibraryInstallLocation(
- lib, ctx.Host(), ctx.Arch().ArchType.String()))
-
- // Also add the dependency on the shared library symbols dir.
- if !ctx.Host() {
- fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
- sharedLibrarySymbolsInstallLocation(lib, ctx.Arch().ArchType.String()))
- }
- }
+ return fuzzPackagedModule
}
-func NewFuzz(hod android.HostOrDeviceSupported) *Module {
+func NewFuzzer(hod android.HostOrDeviceSupported) *Module {
module, binary := newBinary(hod, false)
+ baseInstallerPath := "fuzz"
- binary.baseInstaller = NewFuzzInstaller()
- module.sanitize.SetSanitizer(Fuzzer, true)
+ binary.baseInstaller = NewBaseInstaller(baseInstallerPath, baseInstallerPath, InstallInData)
- fuzz := &fuzzBinary{
+ fuzzBin := &fuzzBinary{
binaryDecorator: binary,
baseCompiler: NewBaseCompiler(),
}
- module.compiler = fuzz
- module.linker = fuzz
- module.installer = fuzz
+ module.compiler = fuzzBin
+ module.linker = fuzzBin
+ module.installer = fuzzBin
+
+ module.fuzzer.Properties.FuzzFramework = fuzz.LibFuzzer
// The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin.
android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- disableDarwinAndLinuxBionic := struct {
+
+ extraProps := struct {
+ Sanitize struct {
+ Fuzzer *bool
+ }
Target struct {
Darwin struct {
Enabled *bool
@@ -263,9 +327,21 @@
}
}
}{}
- disableDarwinAndLinuxBionic.Target.Darwin.Enabled = BoolPtr(false)
- disableDarwinAndLinuxBionic.Target.Linux_bionic.Enabled = BoolPtr(false)
- ctx.AppendProperties(&disableDarwinAndLinuxBionic)
+ extraProps.Sanitize.Fuzzer = BoolPtr(true)
+ extraProps.Target.Darwin.Enabled = BoolPtr(false)
+ extraProps.Target.Linux_bionic.Enabled = BoolPtr(false)
+ ctx.AppendProperties(&extraProps)
+
+ targetFramework := fuzz.GetFramework(ctx, fuzz.Cc)
+ if !fuzz.IsValidFrameworkForModule(targetFramework, fuzz.Cc, fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzzing_frameworks) {
+ ctx.Module().Disable()
+ return
+ }
+
+ if targetFramework == fuzz.AFL {
+ fuzzBin.baseCompiler.Properties.Srcs = append(fuzzBin.baseCompiler.Properties.Srcs, ":aflpp_driver", ":afl-compiler-rt")
+ module.fuzzer.Properties.FuzzFramework = fuzz.AFL
+ }
})
return module
@@ -273,15 +349,24 @@
// Responsible for generating GNU Make rules that package fuzz targets into
// their architecture & target/host specific zip file.
-type ccFuzzPackager struct {
+type ccRustFuzzPackager struct {
fuzz.FuzzPackager
+ fuzzPackagingArchModules string
+ fuzzTargetSharedDepsInstallPairs string
+ allFuzzTargetsName string
}
func fuzzPackagingFactory() android.Singleton {
- return &ccFuzzPackager{}
+
+ fuzzPackager := &ccRustFuzzPackager{
+ fuzzPackagingArchModules: "SOONG_FUZZ_PACKAGING_ARCH_MODULES",
+ fuzzTargetSharedDepsInstallPairs: "FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
+ allFuzzTargetsName: "ALL_FUZZ_TARGETS",
+ }
+ return fuzzPackager
}
-func (s *ccFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+func (s *ccRustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
// Map between each architecture + host/device combination, and the files that
// need to be packaged (in the tuple of {source file, destination folder in
// archive}).
@@ -296,18 +381,18 @@
sharedLibraryInstalled := make(map[string]bool)
ctx.VisitAllModules(func(module android.Module) {
- ccModule, ok := module.(*Module)
- if !ok || ccModule.Properties.PreventInstall {
+ ccModule, ok := module.(LinkableInterface)
+ if !ok || ccModule.PreventInstall() {
return
}
// Discard non-fuzz targets.
- if ok := fuzz.IsValid(ccModule.FuzzModule); !ok {
+ if ok := fuzz.IsValid(ccModule.FuzzModuleStruct()); !ok {
return
}
- fuzzModule, ok := ccModule.compiler.(*fuzzBinary)
- if !ok {
+ sharedLibsInstallDirPrefix := "lib"
+ if !ccModule.IsFuzzModule() {
return
}
@@ -316,36 +401,39 @@
hostOrTargetString = "host"
}
- archString := ccModule.Arch().ArchType.String()
- archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
- archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
+ fpm := fuzz.FuzzPackagedModule{}
+ if ok {
+ fpm = ccModule.FuzzPackagedModule()
+ }
- // Grab the list of required shared libraries.
- sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, UnstrippedOutputFile, IsValidSharedDependency)
+ intermediatePath := "fuzz"
+
+ archString := ccModule.Target().Arch.ArchType.String()
+ archDir := android.PathForIntermediates(ctx, intermediatePath, hostOrTargetString, archString)
+ archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
var files []fuzz.FileToZip
builder := android.NewRuleBuilder(pctx, ctx)
// Package the corpus, data, dict and config into a zipfile.
- files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
+ files = s.PackageArtifacts(ctx, module, fpm, archDir, builder)
// Package shared libraries
- files = append(files, GetSharedLibsToZip(sharedLibraries, ccModule, &s.FuzzPackager, archString, &sharedLibraryInstalled)...)
+ files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries(), ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
// The executable.
- files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
+ files = append(files, fuzz.FileToZip{android.OutputFileForModule(ctx, ccModule, "unstripped"), ""})
- archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
+ archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
if !ok {
return
}
})
s.CreateFuzzPackage(ctx, archDirs, fuzz.Cc, pctx)
-
}
-func (s *ccFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+func (s *ccRustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
packages := s.Packages.Strings()
sort.Strings(packages)
sort.Strings(s.FuzzPackager.SharedLibInstallStrings)
@@ -353,27 +441,31 @@
// ready to handle phony targets created in Soong. In the meantime, this
// exports the phony 'fuzz' target and dependencies on packages to
// core/main.mk so that we can use dist-for-goals.
- ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
- ctx.Strict("FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
+
+ ctx.Strict(s.fuzzPackagingArchModules, strings.Join(packages, " "))
+
+ ctx.Strict(s.fuzzTargetSharedDepsInstallPairs,
strings.Join(s.FuzzPackager.SharedLibInstallStrings, " "))
// Preallocate the slice of fuzz targets to minimise memory allocations.
- s.PreallocateSlice(ctx, "ALL_FUZZ_TARGETS")
+ s.PreallocateSlice(ctx, s.allFuzzTargetsName)
}
// GetSharedLibsToZip finds and marks all the transiently-dependent shared libraries for
// packaging.
-func GetSharedLibsToZip(sharedLibraries android.Paths, module LinkableInterface, s *fuzz.FuzzPackager, archString string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
+func GetSharedLibsToZip(sharedLibraries android.Paths, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
var files []fuzz.FileToZip
+ fuzzDir := "fuzz"
+
for _, library := range sharedLibraries {
- files = append(files, fuzz.FileToZip{library, "lib"})
+ files = append(files, fuzz.FileToZip{library, destinationPathPrefix})
// For each architecture-specific shared library dependency, we need to
// install it to the output directory. Setup the install destination here,
// which will be used by $(copy-many-files) in the Make backend.
- installDestination := sharedLibraryInstallLocation(
- library, module.Host(), archString)
+ installDestination := SharedLibraryInstallLocation(
+ library, module.Host(), fuzzDir, archString)
if (*sharedLibraryInstalled)[installDestination] {
continue
}
@@ -391,7 +483,7 @@
// we want symbolization tools (like `stack`) to be able to find the symbols
// in $ANDROID_PRODUCT_OUT/symbols automagically.
if !module.Host() {
- symbolsInstallDestination := sharedLibrarySymbolsInstallLocation(library, archString)
+ symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(library, fuzzDir, archString)
symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
library.String()+":"+symbolsInstallDestination)
@@ -399,3 +491,49 @@
}
return files
}
+
+// CollectAllSharedDependencies search over the provided module's dependencies using
+// VisitDirectDeps and WalkDeps to enumerate all shared library dependencies.
+// VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer
+// runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies
+// have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
+func CollectAllSharedDependencies(ctx android.ModuleContext) (android.Paths, []android.Module) {
+ seen := make(map[string]bool)
+ recursed := make(map[string]bool)
+ deps := []android.Module{}
+
+ var sharedLibraries android.Paths
+
+ // Enumerate the first level of dependencies, as we discard all non-library
+ // modules in the BFS loop below.
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ if !IsValidSharedDependency(dep) {
+ return
+ }
+ if seen[ctx.OtherModuleName(dep)] {
+ return
+ }
+ seen[ctx.OtherModuleName(dep)] = true
+ deps = append(deps, dep)
+ sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, dep, "unstripped"))
+ })
+
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ if !IsValidSharedDependency(child) {
+ return false
+ }
+ if !seen[ctx.OtherModuleName(child)] {
+ seen[ctx.OtherModuleName(child)] = true
+ deps = append(deps, child)
+ sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, child, "unstripped"))
+ }
+
+ if recursed[ctx.OtherModuleName(child)] {
+ return false
+ }
+ recursed[ctx.OtherModuleName(child)] = true
+ return true
+ })
+
+ return sharedLibraries, deps
+}
diff --git a/cc/gen.go b/cc/gen.go
index 8f62363..dfbb177 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")
@@ -193,6 +229,34 @@
return cppFile, headers.Paths()
}
+func bp2buildCcSysprop(ctx android.Bp2buildMutatorContext, moduleName string, minSdkVersion *string, srcs bazel.LabelListAttribute) *bazel.LabelAttribute {
+ labels := SyspropLibraryLabels{
+ SyspropLibraryLabel: moduleName + "_sysprop_library",
+ StaticLibraryLabel: moduleName + "_cc_sysprop_library_static",
+ }
+ Bp2buildSysprop(ctx, labels, srcs, minSdkVersion)
+ return createLabelAttributeCorrespondingToSrcs(":"+labels.StaticLibraryLabel, srcs)
+}
+
+// Creates a LabelAttribute for a given label where the value is only set for
+// the same config values that have values in a given LabelListAttribute
+func createLabelAttributeCorrespondingToSrcs(baseLabelName string, srcs bazel.LabelListAttribute) *bazel.LabelAttribute {
+ baseLabel := bazel.Label{Label: baseLabelName}
+ label := bazel.LabelAttribute{}
+ if !srcs.Value.IsNil() && !srcs.Value.IsEmpty() {
+ label.Value = &baseLabel
+ return &label
+ }
+ for axis, configToSrcs := range srcs.ConfigurableValues {
+ for config, val := range configToSrcs {
+ if !val.IsNil() && !val.IsEmpty() {
+ label.SetSelectValue(axis, config, baseLabel)
+ }
+ }
+ }
+ return &label
+}
+
// Used to communicate information from the genSources method back to the library code that uses
// it.
type generatedSourceInfo struct {
diff --git a/cc/gen_test.go b/cc/gen_test.go
index 40a5716..85df333 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -74,4 +74,26 @@
})
+ t.Run("sysprop", func(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library {
+ name: "libsysprop",
+ srcs: [
+ "path/to/foo.sysprop",
+ ],
+ }`)
+
+ outDir := "out/soong/.intermediates/libsysprop/android_arm64_armv8-a_static/gen"
+ syspropBuildParams := ctx.ModuleForTests("libsysprop", "android_arm64_armv8-a_static").Rule("sysprop")
+
+ android.AssertStringEquals(t, "header output directory does not match", outDir+"/sysprop/include/path/to", syspropBuildParams.Args["headerOutDir"])
+ android.AssertStringEquals(t, "public output directory does not match", outDir+"/sysprop/public/include/path/to", syspropBuildParams.Args["publicOutDir"])
+ android.AssertStringEquals(t, "src output directory does not match", outDir+"/sysprop/path/to", syspropBuildParams.Args["srcOutDir"])
+ android.AssertStringEquals(t, "output include name does not match", "path/to/foo.sysprop.h", syspropBuildParams.Args["includeName"])
+ android.AssertStringEquals(t, "Input file does not match", "path/to/foo.sysprop", syspropBuildParams.Input.String())
+ android.AssertStringEquals(t, "Output file does not match", outDir+"/sysprop/path/to/foo.sysprop.cpp", syspropBuildParams.Output.String())
+ android.AssertStringListContains(t, "Implicit outputs does not contain header file", syspropBuildParams.ImplicitOutputs.Strings(), outDir+"/sysprop/include/path/to/foo.sysprop.h")
+ android.AssertStringListContains(t, "Implicit outputs does not contain public header file", syspropBuildParams.ImplicitOutputs.Strings(), outDir+"/sysprop/public/include/path/to/foo.sysprop.h")
+ android.AssertIntEquals(t, "Implicit outputs contains the incorrect number of elements", 2, len(syspropBuildParams.ImplicitOutputs.Strings()))
+ })
}
diff --git a/cc/genrule.go b/cc/genrule.go
index 4ef990c..d1c4c2a 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -48,6 +48,8 @@
//
// CC_NATIVE_BRIDGE the name of the subdirectory that native bridge libraries are stored in if
// the architecture has native bridge enabled, empty if it is disabled.
+//
+// CC_OS the name of the OS the command is being executed for.
func GenRuleFactory() android.Module {
module := genrule.NewGenRule()
@@ -68,8 +70,9 @@
func genruleCmdModifier(ctx android.ModuleContext, cmd string) string {
target := ctx.Target()
arch := target.Arch.ArchType
- return fmt.Sprintf("CC_ARCH=%s CC_NATIVE_BRIDGE=%s CC_MULTILIB=%s && %s",
- arch.Name, target.NativeBridgeRelativePath, arch.Multilib, cmd)
+ osName := target.Os.Name
+ return fmt.Sprintf("CC_ARCH=%s CC_NATIVE_BRIDGE=%s CC_MULTILIB=%s CC_OS=%s && %s",
+ arch.Name, target.NativeBridgeRelativePath, arch.Multilib, osName, cmd)
}
var _ android.ImageInterface = (*GenruleExtraProperties)(nil)
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index f25f704..0d16e62 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -40,14 +40,13 @@
name: "gen",
tool_files: ["tool"],
cmd: "$(location tool) $(in) $(out)",
+ out: ["out_arm"],
arch: {
arm: {
srcs: ["foo"],
- out: ["out_arm"],
},
arm64: {
srcs: ["bar"],
- out: ["out_arm64"],
},
},
}
@@ -70,7 +69,7 @@
t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings())
}
- gen = ctx.ModuleForTests("gen", "android_arm64_armv8-a").Output("out_arm64")
+ gen = ctx.ModuleForTests("gen", "android_arm64_armv8-a").Output("out_arm")
expected = []string{"bar"}
if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Implicits.Strings())
diff --git a/cc/image.go b/cc/image.go
index cb7f3c9..e65a9aa 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -533,7 +533,7 @@
}
} else {
// This is either in /system (or similar: /data), or is a
- // modules built with the NDK. Modules built with the NDK
+ // module built with the NDK. Modules built with the NDK
// will be restricted using the existing link type checks.
coreVariantNeeded = true
}
diff --git a/cc/installer.go b/cc/installer.go
index 2522610..716a0df 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
@@ -100,6 +100,10 @@
installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file)
}
+func (installer *baseInstaller) installExecutable(ctx ModuleContext, file android.Path) {
+ installer.path = ctx.InstallExecutable(installer.installDir(ctx), file.Base(), file)
+}
+
func (installer *baseInstaller) everInstallable() bool {
// Most cc modules are installable.
return true
diff --git a/cc/library.go b/cc/library.go
index f9bef6c..7051f72 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -26,10 +26,10 @@
"android/soong/android"
"android/soong/bazel"
"android/soong/bazel/cquery"
- "android/soong/cc/config"
"github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/proptools"
)
// LibraryProperties is a collection of properties shared by cc library rules/cc.
@@ -70,6 +70,12 @@
// List versions to generate stubs libs for. The version name "current" is always
// implicitly added.
Versions []string
+
+ // Whether to not require the implementation of the library to be installed if a
+ // client of the stubs is installed. Defaults to true; set to false if the
+ // implementation is made available by some other means, e.g. in a Microdroid
+ // virtual machine.
+ Implementation_installable *bool
}
// set the name of the output
@@ -78,10 +84,19 @@
// set suffix of the name of the output
Suffix *string `android:"arch_variant"`
+ // Properties for ABI compatibility checker.
+ Header_abi_checker headerAbiCheckerProperties
+
Target struct {
Vendor, Product struct {
// set suffix of the name of the output
Suffix *string `android:"arch_variant"`
+
+ Header_abi_checker headerAbiCheckerProperties
+ }
+
+ Platform struct {
+ Header_abi_checker headerAbiCheckerProperties
}
}
@@ -92,29 +107,6 @@
// from PRODUCT_PACKAGES.
Overrides []string
- // Properties for ABI compatibility checker
- Header_abi_checker struct {
- // Enable ABI checks (even if this is not an LLNDK/VNDK lib)
- Enabled *bool
-
- // Path to a symbol file that specifies the symbols to be included in the generated
- // ABI dump file
- Symbol_file *string `android:"path"`
-
- // Symbol versions that should be ignored from the symbol file
- Exclude_symbol_versions []string
-
- // Symbol tags that should be ignored from the symbol file
- Exclude_symbol_tags []string
-
- // 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.
Inject_bssl_hash *bool `android:"arch_variant"`
@@ -250,7 +242,6 @@
Local_includes bazel.StringListAttribute
Absolute_includes bazel.StringListAttribute
Linkopts bazel.StringListAttribute
- Use_libcrt bazel.BoolAttribute
Rtti bazel.BoolAttribute
Stl *string
@@ -258,7 +249,6 @@
C_std *string
// This is shared only.
- Link_crt bazel.BoolAttribute
Additional_linker_inputs bazel.LabelListAttribute
// Common properties shared between both shared and static variants.
@@ -270,6 +260,20 @@
Features bazel.StringListAttribute
}
+type aidlLibraryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Include_dir *string
+ Tags bazel.StringListAttribute
+}
+
+type ccAidlLibraryAttributes struct {
+ Deps bazel.LabelListAttribute
+ Implementation_deps bazel.LabelListAttribute
+ Implementation_dynamic_deps bazel.LabelListAttribute
+ Tags bazel.StringListAttribute
+ sdkAttributes
+}
+
type stripAttributes struct {
Keep_symbols bazel.BoolAttribute
Keep_symbols_and_debug_frame bazel.BoolAttribute
@@ -278,21 +282,23 @@
None bazel.BoolAttribute
}
-func libraryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
- // For some cc_library modules, their static variants are ready to be
- // converted, but not their shared variants. For these modules, delegate to
- // the cc_library_static bp2build converter temporarily instead.
- if android.GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
- sharedOrStaticLibraryBp2Build(ctx, m, true)
- return
+func stripAttrsFromLinkerAttrs(la *linkerAttributes) stripAttributes {
+ return stripAttributes{
+ Keep_symbols: la.stripKeepSymbols,
+ Keep_symbols_and_debug_frame: la.stripKeepSymbolsAndDebugFrame,
+ Keep_symbols_list: la.stripKeepSymbolsList,
+ All: la.stripAll,
+ None: la.stripNone,
}
+}
+func libraryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
sharedAttrs := bp2BuildParseSharedProps(ctx, m)
staticAttrs := bp2BuildParseStaticProps(ctx, m)
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
@@ -319,7 +325,9 @@
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),
+ Runtime_deps: linkerAttrs.runtimeDeps,
sdkAttributes: bp2BuildParseSdkAttributes(m),
+ Native_coverage: baseAttributes.Native_coverage,
}
sharedCommonAttrs := staticOrSharedAttributes{
@@ -329,13 +337,16 @@
Copts: *compilerAttrs.copts.Clone().Append(sharedAttrs.Copts),
Hdrs: *compilerAttrs.hdrs.Clone().Append(sharedAttrs.Hdrs),
- Deps: *linkerAttrs.deps.Clone().Append(sharedAttrs.Deps),
- Implementation_deps: *linkerAttrs.implementationDeps.Clone().Append(sharedAttrs.Implementation_deps),
- Dynamic_deps: *linkerAttrs.dynamicDeps.Clone().Append(sharedAttrs.Dynamic_deps),
- 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),
+ Deps: *linkerAttrs.deps.Clone().Append(sharedAttrs.Deps),
+ Implementation_deps: *linkerAttrs.implementationDeps.Clone().Append(sharedAttrs.Implementation_deps),
+ Dynamic_deps: *linkerAttrs.dynamicDeps.Clone().Append(sharedAttrs.Dynamic_deps),
+ Implementation_dynamic_deps: *linkerAttrs.implementationDynamicDeps.Clone().Append(sharedAttrs.Implementation_dynamic_deps),
+ Whole_archive_deps: *linkerAttrs.wholeArchiveDeps.Clone().Append(sharedAttrs.Whole_archive_deps),
+ Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
+ System_dynamic_deps: *linkerAttrs.systemDynamicDeps.Clone().Append(sharedAttrs.System_dynamic_deps),
+ Runtime_deps: linkerAttrs.runtimeDeps,
+ sdkAttributes: bp2BuildParseSdkAttributes(m),
+ Native_coverage: baseAttributes.Native_coverage,
}
staticTargetAttrs := &bazelCcLibraryStaticAttributes{
@@ -350,14 +361,12 @@
Export_system_includes: exportedIncludes.SystemIncludes,
Local_includes: compilerAttrs.localIncludes,
Absolute_includes: compilerAttrs.absoluteIncludes,
- Use_libcrt: linkerAttrs.useLibcrt,
Rtti: compilerAttrs.rtti,
Stl: compilerAttrs.stl,
Cpp_std: compilerAttrs.cppStd,
C_std: compilerAttrs.cStd,
- Use_version_lib: linkerAttrs.useVersionLib,
- Features: linkerAttrs.features,
+ Features: baseAttributes.features,
}
sharedTargetAttrs := &bazelCcLibrarySharedAttributes{
@@ -372,8 +381,6 @@
Local_includes: compilerAttrs.localIncludes,
Absolute_includes: compilerAttrs.absoluteIncludes,
Linkopts: linkerAttrs.linkopts,
- Link_crt: linkerAttrs.linkCrt,
- Use_libcrt: linkerAttrs.useLibcrt,
Rtti: compilerAttrs.rtti,
Stl: compilerAttrs.stl,
Cpp_std: compilerAttrs.cppStd,
@@ -382,26 +389,26 @@
Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
- Strip: stripAttributes{
- Keep_symbols: linkerAttrs.stripKeepSymbols,
- Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
- Keep_symbols_list: linkerAttrs.stripKeepSymbolsList,
- All: linkerAttrs.stripAll,
- None: linkerAttrs.stripNone,
- },
- Features: linkerAttrs.features,
+ Strip: stripAttrsFromLinkerAttrs(&linkerAttrs),
+ Features: baseAttributes.features,
+ bazelCcHeaderAbiCheckerAttributes: bp2buildParseAbiCheckerProps(ctx, m),
- Stubs_symbol_file: compilerAttrs.stubsSymbolFile,
- Stubs_versions: compilerAttrs.stubsVersions,
+ Fdo_profile: compilerAttrs.fdoProfile,
}
+ if compilerAttrs.stubsSymbolFile != nil && len(compilerAttrs.stubsVersions.Value) > 0 {
+ sharedTargetAttrs.Stubs_symbol_file = compilerAttrs.stubsSymbolFile
+ }
+
+ sharedTargetAttrs.Suffix = compilerAttrs.suffix
+
for axis, configToProps := range m.GetArchVariantProperties(ctx, &LibraryProperties{}) {
- for config, props := range configToProps {
+ for cfg, 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)
+ if m.Name() == "libcrypto" || m.Name() == "libcrypto_for_testing" {
+ sharedTargetAttrs.Inject_bssl_hash.SetSelectValue(axis, cfg, props.Inject_bssl_hash)
} else {
ctx.PropertyErrorf("inject_bssl_hash", "only applies to libcrypto")
}
@@ -419,12 +426,207 @@
Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl",
}
+ var tagsForStaticVariant bazel.StringListAttribute
+ if compilerAttrs.stubsSymbolFile == nil && len(compilerAttrs.stubsVersions.Value) == 0 {
+ tagsForStaticVariant = android.ApexAvailableTags(m)
+ }
+ tagsForStaticVariant.Append(bazel.StringListAttribute{Value: staticAttrs.Apex_available})
+
+ tagsForSharedVariant := android.ApexAvailableTags(m)
+ tagsForSharedVariant.Append(bazel.StringListAttribute{Value: sharedAttrs.Apex_available})
+
ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
- android.CommonAttributes{Name: m.Name() + "_bp2build_cc_library_static"},
+ android.CommonAttributes{
+ Name: m.Name() + "_bp2build_cc_library_static",
+ Tags: tagsForStaticVariant,
+ },
staticTargetAttrs, staticAttrs.Enabled)
ctx.CreateBazelTargetModuleWithRestrictions(sharedProps,
- android.CommonAttributes{Name: m.Name()},
+ android.CommonAttributes{
+ Name: m.Name(),
+ Tags: tagsForSharedVariant,
+ },
sharedTargetAttrs, sharedAttrs.Enabled)
+
+ createStubsBazelTargetIfNeeded(ctx, m, compilerAttrs, exportedIncludes, baseAttributes)
+}
+
+func createStubsBazelTargetIfNeeded(ctx android.TopDownMutatorContext, m *Module, compilerAttrs compilerAttributes, exportedIncludes BazelIncludes, baseAttributes baseAttributes) {
+ if compilerAttrs.stubsSymbolFile != nil && len(compilerAttrs.stubsVersions.Value) > 0 {
+ stubSuitesProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_stub_suite",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_stub_library.bzl",
+ }
+ soname := m.Name() + ".so"
+ stubSuitesAttrs := &bazelCcStubSuiteAttributes{
+ Symbol_file: compilerAttrs.stubsSymbolFile,
+ Versions: compilerAttrs.stubsVersions,
+ Export_includes: exportedIncludes.Includes,
+ Soname: &soname,
+ Source_library_label: proptools.StringPtr(m.GetBazelLabel(ctx, m)),
+ Deps: baseAttributes.deps,
+ }
+ ctx.CreateBazelTargetModule(stubSuitesProps,
+ android.CommonAttributes{Name: m.Name() + "_stub_libs"},
+ stubSuitesAttrs)
+
+ // Add alias for the stub shared_library in @api_surfaces repository
+ currentModuleLibApiDir := ctx.Config().ApiSurfacesDir(android.ModuleLibApi, "current")
+ actualLabelInMainWorkspace := bazel.Label{
+ Label: fmt.Sprintf("@//%s:%s%s", ctx.ModuleDir(), m.Name(), stubsSuffix),
+ }
+ ctx.CreateBazelTargetAliasInDir(currentModuleLibApiDir, m.Name(), actualLabelInMainWorkspace)
+
+ // Add alias for headers exported by the stub library
+ headerLabelInMainWorkspace := bazel.Label{
+ // This label is generated from cc_stub_suite macro
+ Label: fmt.Sprintf("@//%s:%s_stub_libs_%s_headers", ctx.ModuleDir(), m.Name(), android.ModuleLibApi.String()),
+ }
+ headerAlias := m.Name() + "_headers"
+ ctx.CreateBazelTargetAliasInDir(currentModuleLibApiDir, headerAlias, headerLabelInMainWorkspace)
+ }
+}
+
+func apiContributionBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+ apiSurfaces := make([]string, 0)
+ apiHeaders := make([]string, 0)
+ // module-libapi for apexes (non-null `stubs` property)
+ if module.HasStubsVariants() {
+ apiSurfaces = append(apiSurfaces, android.ModuleLibApi.String())
+ apiIncludes := getModuleLibApiIncludes(ctx, module)
+ if !apiIncludes.isEmpty() {
+ createApiHeaderTarget(ctx, apiIncludes)
+ apiHeaders = append(apiHeaders, apiIncludes.name)
+ }
+ }
+ // vendorapi (non-null `llndk` property)
+ if module.HasLlndkStubs() {
+ apiSurfaces = append(apiSurfaces, android.VendorApi.String())
+ apiIncludes := getVendorApiIncludes(ctx, module)
+ if !apiIncludes.isEmpty() {
+ createApiHeaderTarget(ctx, apiIncludes)
+ apiHeaders = append(apiHeaders, apiIncludes.name)
+ }
+ }
+ // create a target only if this module contributes to an api surface
+ // TODO: Currently this does not distinguish modulelibapi-only headers and vendrorapi-only headers
+ // TODO: Update so that modulelibapi-only headers do not get exported to vendorapi (and vice-versa)
+ if len(apiSurfaces) > 0 {
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_api_contribution",
+ Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+ }
+ attrs := &bazelCcApiContributionAttributes{
+ Library_name: module.Name(),
+ Api_surfaces: bazel.MakeStringListAttribute(apiSurfaces),
+ Api: apiLabelAttribute(ctx, module),
+ Hdrs: bazel.MakeLabelListAttribute(
+ bazel.MakeLabelListFromTargetNames(apiHeaders),
+ ),
+ }
+ ctx.CreateBazelTargetModule(
+ props,
+ android.CommonAttributes{
+ Name: android.ApiContributionTargetName(module.Name()),
+ SkipData: proptools.BoolPtr(true),
+ },
+ attrs,
+ )
+ }
+}
+
+// Native apis are versioned in a single .map.txt for all api surfaces
+// Pick any one of the .map.txt files
+func apiLabelAttribute(ctx android.TopDownMutatorContext, module *Module) bazel.LabelAttribute {
+ var apiFile *string
+ linker := module.linker.(*libraryDecorator)
+ if llndkApi := linker.Properties.Llndk.Symbol_file; llndkApi != nil {
+ apiFile = llndkApi
+ } else if moduleLibApi := linker.Properties.Stubs.Symbol_file; moduleLibApi != nil {
+ apiFile = moduleLibApi
+ } else {
+ ctx.ModuleErrorf("API surface library does not have any API file")
+ }
+ apiLabel := android.BazelLabelForModuleSrcSingle(ctx, proptools.String(apiFile)).Label
+ return *bazel.MakeLabelAttribute(apiLabel)
+}
+
+// wrapper struct to flatten the arch and os specific export_include_dirs
+// flattening is necessary since we want to export apis of all arches even when we build for x86 (e.g.)
+type bazelCcApiLibraryHeadersAttributes struct {
+ bazelCcLibraryHeadersAttributes
+
+ Arch *string
+}
+
+func (a *bazelCcApiLibraryHeadersAttributes) isEmpty() bool {
+ return a.Export_includes.IsEmpty() &&
+ a.Export_system_includes.IsEmpty() &&
+ a.Deps.IsEmpty()
+}
+
+type apiIncludes struct {
+ name string // name of the Bazel target in the generated bp2build workspace
+ attrs bazelCcApiLibraryHeadersAttributes
+}
+
+func (includes *apiIncludes) isEmpty() bool {
+ return includes.attrs.isEmpty()
+}
+
+func (includes *apiIncludes) addDep(name string) {
+ l := bazel.Label{Label: ":" + name}
+ ll := bazel.MakeLabelList([]bazel.Label{l})
+ lla := bazel.MakeLabelListAttribute(ll)
+ includes.attrs.Deps.Append(lla)
+}
+
+// includes provided to the module-lib API surface. This API surface is used by apexes.
+func getModuleLibApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
+ flagProps := c.library.(*libraryDecorator).flagExporter.Properties
+ linkProps := c.library.(*libraryDecorator).baseLinker.Properties
+ includes := android.FirstUniqueStrings(flagProps.Export_include_dirs)
+ systemIncludes := android.FirstUniqueStrings(flagProps.Export_system_include_dirs)
+ headerLibs := android.FirstUniqueStrings(linkProps.Export_header_lib_headers)
+ attrs := bazelCcLibraryHeadersAttributes{
+ Export_includes: bazel.MakeStringListAttribute(includes),
+ Export_system_includes: bazel.MakeStringListAttribute(systemIncludes),
+ Deps: bazel.MakeLabelListAttribute(apiHeaderLabels(ctx, headerLibs)),
+ }
+
+ return apiIncludes{
+ name: c.Name() + ".module-libapi.headers",
+ attrs: bazelCcApiLibraryHeadersAttributes{
+ bazelCcLibraryHeadersAttributes: attrs,
+ },
+ }
+}
+
+func getVendorApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
+ baseProps := c.library.(*libraryDecorator).flagExporter.Properties
+ llndkProps := c.library.(*libraryDecorator).Properties.Llndk
+ includes := baseProps.Export_include_dirs
+ systemIncludes := baseProps.Export_system_include_dirs
+ // LLNDK can override the base includes
+ if llndkIncludes := llndkProps.Override_export_include_dirs; llndkIncludes != nil {
+ includes = llndkIncludes
+ }
+ if proptools.Bool(llndkProps.Export_headers_as_system) {
+ systemIncludes = append(systemIncludes, includes...)
+ includes = nil
+ }
+
+ attrs := bazelCcLibraryHeadersAttributes{
+ Export_includes: bazel.MakeStringListAttribute(includes),
+ Export_system_includes: bazel.MakeStringListAttribute(systemIncludes),
+ Deps: bazel.MakeLabelListAttribute(apiHeaderLabels(ctx, llndkProps.Export_llndk_headers)),
+ }
+ return apiIncludes{
+ name: c.Name() + ".vendorapi.headers",
+ attrs: bazelCcApiLibraryHeadersAttributes{
+ bazelCcLibraryHeadersAttributes: attrs,
+ },
+ }
}
// cc_library creates both static and/or shared libraries for a device and/or
@@ -611,7 +813,7 @@
sAbiOutputFile android.OptionalPath
// Source Abi Diff
- sAbiDiff android.OptionalPath
+ sAbiDiff android.Paths
// Location of the static library in the sysroot. Empty if the library is
// not included in the NDK.
@@ -645,20 +847,24 @@
}
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])
+ var outputFilePath android.Path = android.PathForBazelOut(ctx, rootStaticArchives[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ handler.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+ }
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
objPaths := ccInfo.CcObjectFiles
@@ -682,22 +888,26 @@
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)
+ var outputFilePath android.Path = android.PathForBazelOut(ctx, rootDynamicLibraries[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ handler.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+ }
- handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath
+ handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+ handler.module.linker.(*libraryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, ccInfo.UnstrippedOutput)
var tocFile android.OptionalPath
if len(ccInfo.TocFile) > 0 {
@@ -705,6 +915,10 @@
}
handler.module.linker.(*libraryDecorator).tocFile = tocFile
+ if len(ccInfo.AbiDiffFiles) > 0 {
+ handler.module.linker.(*libraryDecorator).sAbiDiff = android.PathsForBazelOut(ctx, ccInfo.AbiDiffFiles)
+ }
+
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
TableOfContents: tocFile,
SharedLibrary: outputFilePath,
@@ -712,30 +926,35 @@
// 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.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
+ if v := handler.module.library.stubsVersion(); v != "" {
+ stubsLabel := label + "_stub_libs-" + v
+ bazelCtx.QueueBazelRequest(stubsLabel, cquery.GetCcInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
+ }
+}
+
+func (handler *ccLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ if v := handler.module.library.stubsVersion(); v != "" {
+ // if we are a stubs variant, just use the Bazel stubs target
+ label = label + "_stub_libs-" + v
+ }
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(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)
@@ -749,7 +968,11 @@
// implementation.
i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
}
- return ok
+
+ handler.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
+
+ cctx := moduleContextFromAndroidModuleContext(ctx, handler.module)
+ addStubDependencyProviders(cctx)
}
func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
@@ -772,9 +995,15 @@
for _, path := range paths {
dir := path.String()
// Skip if dir is for generated headers
- if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
+ if strings.HasPrefix(dir, ctx.Config().OutDir()) {
continue
}
+
+ // Filter out the generated headers from bazel.
+ if strings.HasPrefix(dir, android.PathForBazelOut(ctx, "bazel-out").String()) {
+ continue
+ }
+
// libeigen wrongly exports the root directory "external/eigen". But only two
// subdirectories "Eigen" and "unsupported" contain exported header files. Even worse
// some of them have no extension. So we need special treatment for libeigen in order
@@ -834,7 +1063,7 @@
return ret
}
-func GlobGeneratedHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
+func GlobGeneratedHeadersForSnapshot(_ android.ModuleContext, paths android.Paths) android.Paths {
ret := android.Paths{}
for _, header := range paths {
// TODO(b/148123511): remove exportedDeps after cleaning up genrule
@@ -929,7 +1158,6 @@
if ctx.Darwin() {
f = append(f,
"-dynamiclib",
- "-single_module",
"-install_name @rpath/"+libName+flags.Toolchain.ShlibSuffix(),
)
if ctx.Arch().ArchType == android.X86 {
@@ -992,12 +1220,20 @@
return flags
}
-func (library *libraryDecorator) headerAbiCheckerEnabled() bool {
- return Bool(library.Properties.Header_abi_checker.Enabled)
-}
-
-func (library *libraryDecorator) headerAbiCheckerExplicitlyDisabled() bool {
- return !BoolDefault(library.Properties.Header_abi_checker.Enabled, true)
+func (library *libraryDecorator) getHeaderAbiCheckerProperties(ctx android.BaseModuleContext) headerAbiCheckerProperties {
+ m := ctx.Module().(*Module)
+ variantProps := &library.Properties.Target.Platform.Header_abi_checker
+ if m.InVendor() {
+ variantProps = &library.Properties.Target.Vendor.Header_abi_checker
+ } else if m.InProduct() {
+ variantProps = &library.Properties.Target.Product.Header_abi_checker
+ }
+ props := library.Properties.Header_abi_checker
+ err := proptools.AppendProperties(&props, variantProps, nil)
+ if err != nil {
+ ctx.ModuleErrorf("Cannot merge headerAbiCheckerProperties: %s", err.Error())
+ }
+ return props
}
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
@@ -1037,9 +1273,27 @@
ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
return Objects{}
}
+ // b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
+ // systemapi, respectively. The former is for symbols defined in platform libraries
+ // and the latter is for symbols defined in APEXes.
+ // A single library can contain either # apex or # systemapi, but not both.
+ // The stub generator (ndkstubgen) is additive, so passing _both_ of these to it should be a no-op.
+ // However, having this distinction helps guard accidental
+ // promotion or demotion of API and also helps the API review process b/191371676
+ var flag string
+ if ctx.Module().(android.ApexModule).NotInPlatform() {
+ flag = "--apex"
+ } else {
+ flag = "--systemapi"
+ }
+ // b/184712170, unless the lib is an NDK library, exclude all public symbols from
+ // the stub so that it is mandated that all symbols are explicitly marked with
+ // either apex or systemapi.
+ if !ctx.Module().(*Module).IsNdk(ctx.Config()) {
+ flag = flag + " --no-ndk"
+ }
nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
- android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion),
- "--apex")
+ android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
library.versionScriptPath = android.OptionalPathForPath(
nativeAbiResult.versionScript)
@@ -1118,9 +1372,8 @@
setStatic()
setShared()
- // Check whether header_abi_checker is enabled or explicitly disabled.
- headerAbiCheckerEnabled() bool
- headerAbiCheckerExplicitlyDisabled() bool
+ // Gets the ABI properties for vendor, product, or platform variant
+ getHeaderAbiCheckerProperties(ctx android.BaseModuleContext) headerAbiCheckerProperties
// Write LOCAL_ADDITIONAL_DEPENDENCIES for ABI diff
androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer)
@@ -1136,6 +1389,7 @@
buildStubs() bool
setBuildStubs(isLatest bool)
hasStubsVariants() bool
+ isStubsImplementationRequired() bool
setStubsVersion(string)
stubsVersion() string
@@ -1272,7 +1526,7 @@
deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...)
deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
} else if library.shared() {
- if !Bool(library.baseLinker.Properties.Nocrt) {
+ if library.baseLinker.Properties.crt() {
deps.CrtBegin = append(deps.CrtBegin, ctx.toolchain().CrtBeginSharedLibrary()...)
deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtEndSharedLibrary()...)
}
@@ -1525,7 +1779,6 @@
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
-
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.StaticLibObjs.sAbiDumpFiles...)
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.WholeStaticLibObjs.sAbiDumpFiles...)
@@ -1545,6 +1798,12 @@
Target: ctx.Target(),
})
+ addStubDependencyProviders(ctx)
+
+ return unstrippedOutputFile
+}
+
+func addStubDependencyProviders(ctx ModuleContext) {
stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
if len(stubs) > 0 {
var stubsInfo []SharedStubLibrary
@@ -1559,12 +1818,9 @@
}
ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
SharedStubLibraries: stubsInfo,
-
- IsLLNDK: ctx.IsLlndk(),
+ IsLLNDK: ctx.IsLlndk(),
})
}
-
- return unstrippedOutputFile
}
func (library *libraryDecorator) unstrippedOutputFilePath() android.Path {
@@ -1586,41 +1842,125 @@
return library.coverageOutputFile
}
-func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
- // The logic must be consistent with classifySourceAbiDump.
- isNdk := ctx.isNdk(ctx.Config())
- isLlndkOrVndk := ctx.IsLlndkPublic() || (ctx.useVndk() && ctx.isVndk())
+func getRefAbiDumpFile(ctx android.ModuleInstallPathContext,
+ versionedDumpDir, fileName string) android.OptionalPath {
- refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, false)
- refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, true)
+ currentArchType := ctx.Arch().ArchType
+ primaryArchType := ctx.Config().DevicePrimaryArchType()
+ archName := currentArchType.String()
+ if currentArchType != primaryArchType {
+ archName += "_" + primaryArchType.String()
+ }
- if refAbiDumpTextFile.Valid() {
- if refAbiDumpGzipFile.Valid() {
- ctx.ModuleErrorf(
- "Two reference ABI dump files are found: %q and %q. Please delete the stale one.",
- refAbiDumpTextFile, refAbiDumpGzipFile)
- return nil
+ return android.ExistentPathForSource(ctx, versionedDumpDir, archName, "source-based",
+ fileName+".lsdump")
+}
+
+func getRefAbiDumpDir(isNdk, isVndk bool) string {
+ var dirName string
+ if isNdk {
+ dirName = "ndk"
+ } else if isVndk {
+ dirName = "vndk"
+ } else {
+ dirName = "platform"
+ }
+ return filepath.Join("prebuilts", "abi-dumps", dirName)
+}
+
+func prevRefAbiDumpVersion(ctx ModuleContext, dumpDir string) int {
+ sdkVersionInt := ctx.Config().PlatformSdkVersion().FinalInt()
+ sdkVersionStr := ctx.Config().PlatformSdkVersion().String()
+
+ if ctx.Config().PlatformSdkFinal() {
+ return sdkVersionInt - 1
+ } else {
+ // The platform SDK version can be upgraded before finalization while the corresponding abi dumps hasn't
+ // been generated. Thus the Cross-Version Check chooses PLATFORM_SDK_VERION - 1 as previous version.
+ // This situation could be identified by checking the existence of the PLATFORM_SDK_VERION dump directory.
+ versionedDumpDir := android.ExistentPathForSource(ctx, dumpDir, sdkVersionStr)
+ if versionedDumpDir.Valid() {
+ return sdkVersionInt
+ } else {
+ return sdkVersionInt - 1
}
- return refAbiDumpTextFile.Path()
}
- if refAbiDumpGzipFile.Valid() {
- return unzipRefDump(ctx, refAbiDumpGzipFile.Path(), fileName)
+}
+
+func currRefAbiDumpVersion(ctx ModuleContext, isVndk bool) string {
+ if isVndk {
+ // Each version of VNDK is independent, so follow the VNDK version which is the codename or PLATFORM_SDK_VERSION.
+ return ctx.Module().(*Module).VndkVersion()
+ } else if ctx.Config().PlatformSdkFinal() {
+ // After sdk finalization, the ABI of the latest API level must be consistent with the source code,
+ // so choose PLATFORM_SDK_VERSION as the current version.
+ return ctx.Config().PlatformSdkVersion().String()
+ } else {
+ return "current"
}
- return nil
+}
+
+// sourceAbiDiff registers a build statement to compare linked sAbi dump files (.lsdump).
+func (library *libraryDecorator) sourceAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
+ baseName, nameExt string, isLlndkOrNdk, allowExtensions bool,
+ sourceVersion, errorMessage string) {
+
+ sourceDump := library.sAbiOutputFile.Path()
+
+ extraFlags := []string{"-target-version", sourceVersion}
+ headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
+ if Bool(headerAbiChecker.Check_all_apis) {
+ extraFlags = append(extraFlags, "-check-all-apis")
+ } else {
+ extraFlags = append(extraFlags,
+ "-allow-unreferenced-changes",
+ "-allow-unreferenced-elf-symbol-changes")
+ }
+ if isLlndkOrNdk {
+ extraFlags = append(extraFlags, "-consider-opaque-types-different")
+ }
+ if allowExtensions {
+ extraFlags = append(extraFlags, "-allow-extensions")
+ }
+ extraFlags = append(extraFlags, headerAbiChecker.Diff_flags...)
+
+ library.sAbiDiff = append(
+ library.sAbiDiff,
+ transformAbiDumpToAbiDiff(ctx, sourceDump, referenceDump,
+ baseName, nameExt, extraFlags, errorMessage))
+}
+
+func (library *libraryDecorator) crossVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
+ baseName string, isLlndkOrNdk bool, sourceVersion, prevVersion string) {
+
+ errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/master/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + prevVersion + "."
+
+ library.sourceAbiDiff(ctx, referenceDump, baseName, prevVersion,
+ isLlndkOrNdk, true /* allowExtensions */, sourceVersion, errorMessage)
+}
+
+func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
+ baseName string, isLlndkOrNdk, allowExtensions bool) {
+
+ libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
+ errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName
+
+ library.sourceAbiDiff(ctx, referenceDump, baseName, "",
+ isLlndkOrNdk, allowExtensions, "current", errorMessage)
+}
+
+func (library *libraryDecorator) optInAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
+ baseName, nameExt string, isLlndkOrNdk bool, refDumpDir string) {
+
+ libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
+ errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName + " -ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir
+
+ library.sourceAbiDiff(ctx, referenceDump, baseName, nameExt,
+ isLlndkOrNdk, false /* allowExtensions */, "current", errorMessage)
}
func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
if library.sabi.shouldCreateSourceAbiDump() {
- var vndkVersion string
-
- if ctx.useVndk() {
- // For modules linking against vndk, follow its vndk version
- vndkVersion = ctx.Module().(*Module).VndkVersion()
- } else {
- // Regard the other modules as PLATFORM_VNDK_VERSION
- vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
- }
-
exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
var SourceAbiFlags []string
for _, dir := range exportIncludeDirs.Strings() {
@@ -1630,20 +1970,52 @@
SourceAbiFlags = append(SourceAbiFlags, "-I"+reexportedInclude)
}
exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
+ headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
library.sAbiOutputFile = transformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags,
android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)),
- library.Properties.Header_abi_checker.Exclude_symbol_versions,
- library.Properties.Header_abi_checker.Exclude_symbol_tags)
+ headerAbiChecker.Exclude_symbol_versions,
+ headerAbiChecker.Exclude_symbol_tags)
addLsdumpPath(classifySourceAbiDump(ctx) + ":" + library.sAbiOutputFile.String())
- refAbiDumpFile := getRefAbiDumpFile(ctx, vndkVersion, fileName)
- 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())
+ // The logic must be consistent with classifySourceAbiDump.
+ isVndk := ctx.useVndk() && ctx.isVndk()
+ isNdk := ctx.isNdk(ctx.Config())
+ isLlndk := ctx.isImplementationForLLNDKPublic()
+ dumpDir := getRefAbiDumpDir(isNdk, isVndk)
+ binderBitness := ctx.DeviceConfig().BinderBitness()
+ // If NDK or PLATFORM library, check against previous version ABI.
+ if !isVndk {
+ prevVersionInt := prevRefAbiDumpVersion(ctx, dumpDir)
+ prevVersion := strconv.Itoa(prevVersionInt)
+ prevDumpDir := filepath.Join(dumpDir, prevVersion, binderBitness)
+ prevDumpFile := getRefAbiDumpFile(ctx, prevDumpDir, fileName)
+ if prevDumpFile.Valid() {
+ library.crossVersionAbiDiff(ctx, prevDumpFile.Path(),
+ fileName, isLlndk || isNdk,
+ strconv.Itoa(prevVersionInt+1), prevVersion)
+ }
+ }
+ // Check against the current version.
+ currVersion := currRefAbiDumpVersion(ctx, isVndk)
+ currDumpDir := filepath.Join(dumpDir, currVersion, binderBitness)
+ currDumpFile := getRefAbiDumpFile(ctx, currDumpDir, fileName)
+ if currDumpFile.Valid() {
+ library.sameVersionAbiDiff(ctx, currDumpFile.Path(),
+ fileName, isLlndk || isNdk, ctx.IsVndkExt())
+ }
+ // Check against the opt-in reference dumps.
+ for i, optInDumpDir := range headerAbiChecker.Ref_dump_dirs {
+ optInDumpDirPath := android.PathForModuleSrc(ctx, optInDumpDir)
+ // Ref_dump_dirs are not versioned.
+ // They do not contain subdir for binder bitness because 64-bit binder has been mandatory.
+ optInDumpFile := getRefAbiDumpFile(ctx, optInDumpDirPath.String(), fileName)
+ if !optInDumpFile.Valid() {
+ continue
+ }
+ library.optInAbiDiff(ctx, optInDumpFile.Path(),
+ fileName, "opt"+strconv.Itoa(i), isLlndk || isNdk,
+ optInDumpDirPath.String())
}
}
}
@@ -1835,7 +2207,16 @@
func (library *libraryDecorator) installSymlinkToRuntimeApex(ctx ModuleContext, file android.Path) {
dir := library.baseInstaller.installDir(ctx)
dirOnDevice := android.InstallPathToOnDevicePath(ctx, dir)
- target := "/" + filepath.Join("apex", "com.android.runtime", dir.Base(), "bionic", file.Base())
+ // libc_hwasan has relative_install_dir set, which would mess up the dir.Base() logic.
+ // hardcode here because it's the only target, if we have other targets that use this
+ // we can generalise this.
+ var target string
+ if ctx.baseModuleName() == "libc_hwasan" {
+ target = "/" + filepath.Join("apex", "com.android.runtime", "lib64", "bionic", "hwasan", file.Base())
+ } else {
+ base := dir.Base()
+ target = "/" + filepath.Join("apex", "com.android.runtime", base, "bionic", file.Base())
+ }
ctx.InstallAbsoluteSymlink(dir, file.Base(), target)
library.postInstallCmds = append(library.postInstallCmds, makeSymlinkCmd(dirOnDevice, file.Base(), target))
}
@@ -1902,8 +2283,7 @@
!ctx.useVndk() && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && ctx.Device() &&
library.baseLinker.sanitize.isUnsanitizedVariant() &&
ctx.isForPlatform() && !ctx.isPreventInstall() {
- installPath := getNdkSysrootBase(ctx).Join(
- ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
+ installPath := getUnversionedLibraryInstallPath(ctx).Join(ctx, file.Base())
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: android.Cp,
@@ -1992,8 +2372,8 @@
}
func (library *libraryDecorator) symbolFileForAbiCheck(ctx ModuleContext) *string {
- if library.Properties.Header_abi_checker.Symbol_file != nil {
- return library.Properties.Header_abi_checker.Symbol_file
+ if props := library.getHeaderAbiCheckerProperties(ctx); props.Symbol_file != nil {
+ return props.Symbol_file
}
if ctx.Module().(*Module).IsLlndk() {
return library.Properties.Llndk.Symbol_file
@@ -2011,6 +2391,10 @@
len(library.Properties.Stubs.Versions) > 0
}
+func (library *libraryDecorator) isStubsImplementationRequired() bool {
+ return BoolDefault(library.Properties.Stubs.Implementation_installable, true)
+}
+
func (library *libraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
if !library.hasStubsVariants() {
return nil
@@ -2022,7 +2406,10 @@
}
// Future API level is implicitly added if there isn't
- vers := library.Properties.Stubs.Versions
+ return addCurrentVersionIfNotPresent(library.Properties.Stubs.Versions)
+}
+
+func addCurrentVersionIfNotPresent(vers []string) []string {
if inList(android.FutureApiLevel.String(), vers) {
return vers
}
@@ -2093,10 +2480,20 @@
mod.ModuleBase.MakeUninstallable()
}
+func (library *libraryDecorator) getPartition() string {
+ return library.path.Partition()
+}
+
func (library *libraryDecorator) getAPIListCoverageXMLPath() android.ModuleOutPath {
return library.apiListCoverageXmlPath
}
+func (library *libraryDecorator) overriddenModules() []string {
+ return library.Properties.Overrides
+}
+
+var _ overridable = (*libraryDecorator)(nil)
+
var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList")
// versioningMacroNamesList returns a singleton map, where keys are "version macro names",
@@ -2277,7 +2674,7 @@
// normalizeVersions modifies `versions` in place, so that each raw version
// string becomes its normalized canonical form.
// Validates that the versions in `versions` are specified in least to greatest order.
-func normalizeVersions(ctx android.BaseModuleContext, versions []string) {
+func normalizeVersions(ctx android.BazelConversionPathContext, versions []string) {
var previous android.ApiLevel
for i, v := range versions {
ver, err := android.ApiLevelFromUser(ctx, v)
@@ -2301,11 +2698,12 @@
m := mctx.Module().(*Module)
isLLNDK := m.IsLlndk()
isVendorPublicLibrary := m.IsVendorPublicLibrary()
+ isImportedApiLibrary := m.isImportedApiLibrary()
modules := mctx.CreateLocalVariations(variants...)
for i, m := range modules {
- if variants[i] != "" || isLLNDK || isVendorPublicLibrary {
+ if variants[i] != "" || isLLNDK || isVendorPublicLibrary || isImportedApiLibrary {
// A stubs or LLNDK stubs variant.
c := m.(*Module)
c.sanitize = nil
@@ -2348,7 +2746,7 @@
}
}
-func CanBeOrLinkAgainstVersionVariants(module interface {
+func canBeOrLinkAgainstVersionVariants(module interface {
Host() bool
InRamdisk() bool
InVendorRamdisk() bool
@@ -2356,15 +2754,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()
}
@@ -2375,37 +2772,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())
}
}
@@ -2445,12 +2846,35 @@
return outputFile
}
+func bp2buildParseAbiCheckerProps(ctx android.TopDownMutatorContext, module *Module) bazelCcHeaderAbiCheckerAttributes {
+ lib, ok := module.linker.(*libraryDecorator)
+ if !ok {
+ return bazelCcHeaderAbiCheckerAttributes{}
+ }
+
+ abiChecker := lib.getHeaderAbiCheckerProperties(ctx)
+
+ abiCheckerAttrs := bazelCcHeaderAbiCheckerAttributes{
+ Abi_checker_enabled: abiChecker.Enabled,
+ Abi_checker_exclude_symbol_versions: abiChecker.Exclude_symbol_versions,
+ Abi_checker_exclude_symbol_tags: abiChecker.Exclude_symbol_tags,
+ Abi_checker_check_all_apis: abiChecker.Check_all_apis,
+ Abi_checker_diff_flags: abiChecker.Diff_flags,
+ }
+ if abiChecker.Symbol_file != nil {
+ symbolFile := android.BazelLabelForModuleSrcSingle(ctx, *abiChecker.Symbol_file)
+ abiCheckerAttrs.Abi_checker_symbol_file = &symbolFile
+ }
+
+ return abiCheckerAttrs
+}
+
func sharedOrStaticLibraryBp2Build(ctx android.TopDownMutatorContext, module *Module, isStatic bool) {
baseAttributes := bp2BuildParseBaseProps(ctx, module)
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
@@ -2489,21 +2913,21 @@
Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
System_dynamic_deps: linkerAttrs.systemDynamicDeps,
sdkAttributes: bp2BuildParseSdkAttributes(module),
+ Runtime_deps: linkerAttrs.runtimeDeps,
+ Native_coverage: baseAttributes.Native_coverage,
}
+ module.convertTidyAttributes(ctx, &commonAttrs.tidyAttributes)
+
var attrs interface{}
if isStatic {
commonAttrs.Deps.Add(baseAttributes.protoDependency)
attrs = &bazelCcLibraryStaticAttributes{
staticOrSharedAttributes: commonAttrs,
-
- Use_libcrt: linkerAttrs.useLibcrt,
- Use_version_lib: linkerAttrs.useVersionLib,
-
- Rtti: compilerAttrs.rtti,
- Stl: compilerAttrs.stl,
- Cpp_std: compilerAttrs.cppStd,
- C_std: compilerAttrs.cStd,
+ Rtti: compilerAttrs.rtti,
+ Stl: compilerAttrs.stl,
+ Cpp_std: compilerAttrs.cppStd,
+ C_std: compilerAttrs.cStd,
Export_includes: exportedIncludes.Includes,
Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
@@ -2515,12 +2939,12 @@
Conlyflags: compilerAttrs.conlyFlags,
Asflags: asFlags,
- Features: linkerAttrs.features,
+ Features: baseAttributes.features,
}
} else {
commonAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
- attrs = &bazelCcLibrarySharedAttributes{
+ sharedLibAttrs := &bazelCcLibrarySharedAttributes{
staticOrSharedAttributes: commonAttrs,
Cppflags: compilerAttrs.cppFlags,
@@ -2528,8 +2952,6 @@
Asflags: asFlags,
Linkopts: linkerAttrs.linkopts,
- Link_crt: linkerAttrs.linkCrt,
- Use_libcrt: linkerAttrs.useLibcrt,
Use_version_lib: linkerAttrs.useVersionLib,
Rtti: compilerAttrs.rtti,
@@ -2544,19 +2966,20 @@
Absolute_includes: compilerAttrs.absoluteIncludes,
Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
- Strip: stripAttributes{
- Keep_symbols: linkerAttrs.stripKeepSymbols,
- Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
- Keep_symbols_list: linkerAttrs.stripKeepSymbolsList,
- All: linkerAttrs.stripAll,
- None: linkerAttrs.stripNone,
- },
+ Strip: stripAttrsFromLinkerAttrs(&linkerAttrs),
- Features: linkerAttrs.features,
+ Features: baseAttributes.features,
- Stubs_symbol_file: compilerAttrs.stubsSymbolFile,
- Stubs_versions: compilerAttrs.stubsVersions,
+ Suffix: compilerAttrs.suffix,
+
+ bazelCcHeaderAbiCheckerAttributes: bp2buildParseAbiCheckerProps(ctx, module),
+
+ Fdo_profile: compilerAttrs.fdoProfile,
}
+ if compilerAttrs.stubsSymbolFile != nil && len(compilerAttrs.stubsVersions.Value) > 0 {
+ sharedLibAttrs.Stubs_symbol_file = compilerAttrs.stubsSymbolFile
+ }
+ attrs = sharedLibAttrs
}
var modType string
@@ -2564,26 +2987,27 @@
modType = "cc_library_static"
} else {
modType = "cc_library_shared"
+ createStubsBazelTargetIfNeeded(ctx, module, compilerAttrs, exportedIncludes, baseAttributes)
}
props := bazel.BazelTargetModuleProperties{
Rule_class: modType,
Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType),
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
+ tags := android.ApexAvailableTags(module)
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name(), Tags: tags}, attrs)
}
// TODO(b/199902614): Can this be factored to share with the other Attributes?
type bazelCcLibraryStaticAttributes struct {
staticOrSharedAttributes
- Use_libcrt bazel.BoolAttribute
Use_version_lib bazel.BoolAttribute
-
- Rtti bazel.BoolAttribute
- Stl *string
- Cpp_std *string
- C_std *string
+ Rtti bazel.BoolAttribute
+ Stl *string
+ Cpp_std *string
+ C_std *string
Export_includes bazel.StringListAttribute
Export_absolute_includes bazel.StringListAttribute
@@ -2603,10 +3027,7 @@
type bazelCcLibrarySharedAttributes struct {
staticOrSharedAttributes
- Linkopts bazel.StringListAttribute
- Link_crt bazel.BoolAttribute // Only for linking shared library (and cc_binary)
-
- Use_libcrt bazel.BoolAttribute
+ Linkopts bazel.StringListAttribute
Use_version_lib bazel.BoolAttribute
Rtti bazel.BoolAttribute
@@ -2631,6 +3052,30 @@
Features bazel.StringListAttribute
Stubs_symbol_file *string
- Stubs_versions bazel.StringListAttribute
- Inject_bssl_hash bazel.BoolAttribute
+
+ Inject_bssl_hash bazel.BoolAttribute
+
+ Suffix bazel.StringAttribute
+
+ bazelCcHeaderAbiCheckerAttributes
+
+ Fdo_profile bazel.LabelAttribute
+}
+
+type bazelCcStubSuiteAttributes struct {
+ Symbol_file *string
+ Versions bazel.StringListAttribute
+ Export_includes bazel.StringListAttribute
+ Source_library_label *string
+ Soname *string
+ Deps bazel.LabelListAttribute
+}
+
+type bazelCcHeaderAbiCheckerAttributes struct {
+ Abi_checker_enabled *bool
+ Abi_checker_symbol_file *bazel.Label
+ Abi_checker_exclude_symbol_versions []string
+ Abi_checker_exclude_symbol_tags []string
+ Abi_checker_check_all_apis *bool
+ Abi_checker_diff_flags []string
}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 41ebcc7..1dee726 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -15,8 +15,11 @@
package cc
import (
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
)
func init() {
@@ -47,31 +50,37 @@
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.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
+}
+
+func (h *libraryHeaderBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(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])
+ var outputPath android.Path = android.PathForBazelOut(ctx, outputPaths[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ h.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputPath = android.AttachValidationActions(ctx, outputPath, h.module.tidyFiles)
+ }
h.module.outputFile = android.OptionalPathForPath(outputPath)
// HeaderLibraryInfo is an empty struct to indicate to dependencies that this is a header library
@@ -84,7 +93,7 @@
// TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
h.library.collectedSnapshotHeaders = android.Paths{}
- return true
+ h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
}
// cc_library_headers contains a set of c/c++ headers which are imported by
@@ -96,7 +105,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()
}
@@ -122,14 +131,15 @@
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
+ (&linkerAttrs.deps).Append(linkerAttrs.dynamicDeps)
+ (&linkerAttrs.deps).Append(linkerAttrs.wholeArchiveDeps)
attrs := &bazelCcLibraryHeadersAttributes{
Export_includes: exportedIncludes.Includes,
Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
Export_system_includes: exportedIncludes.SystemIncludes,
- Implementation_deps: linkerAttrs.implementationDeps,
Deps: linkerAttrs.deps,
System_dynamic_deps: linkerAttrs.systemDynamicDeps,
Hdrs: baseAttributes.hdrs,
@@ -141,5 +151,125 @@
Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
+ tags := android.ApexAvailableTags(module)
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: module.Name(),
+ Tags: tags,
+ }, attrs)
+}
+
+// Append .contribution suffix to input labels
+func apiBazelTargets(ll bazel.LabelList) bazel.LabelList {
+ labels := make([]bazel.Label, 0)
+ for _, l := range ll.Includes {
+ labels = append(labels, bazel.Label{
+ Label: android.ApiContributionTargetName(l.Label),
+ })
+ }
+ return bazel.MakeLabelList(labels)
+}
+
+func apiLibraryHeadersBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+ // cc_api_library_headers have a 1:1 mapping to arch/no-arch
+ // For API export, create a top-level arch-agnostic target and list the arch-specific targets as its deps
+
+ // arch-agnostic includes
+ apiIncludes := getModuleLibApiIncludes(ctx, module)
+ // arch and os specific includes
+ archApiIncludes, androidOsIncludes := archOsSpecificApiIncludes(ctx, module)
+ for _, arch := range allArches { // sorted iteration
+ archApiInclude := archApiIncludes[arch]
+ if !archApiInclude.isEmpty() {
+ createApiHeaderTarget(ctx, archApiInclude)
+ apiIncludes.addDep(archApiInclude.name)
+ }
+ }
+ // os==android includes
+ if !androidOsIncludes.isEmpty() {
+ createApiHeaderTarget(ctx, androidOsIncludes)
+ apiIncludes.addDep(androidOsIncludes.name)
+ }
+
+ if !apiIncludes.isEmpty() {
+ // override the name from <mod>.module-libapi.headers --> <mod>.contribution
+ apiIncludes.name = android.ApiContributionTargetName(module.Name())
+ createApiHeaderTarget(ctx, apiIncludes)
+ }
+}
+
+func createApiHeaderTarget(ctx android.TopDownMutatorContext, includes apiIncludes) {
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_api_library_headers",
+ Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+ }
+ ctx.CreateBazelTargetModule(
+ props,
+ android.CommonAttributes{
+ Name: includes.name,
+ SkipData: proptools.BoolPtr(true),
+ },
+ &includes.attrs,
+ )
+}
+
+var (
+ allArches = []string{"arm", "arm64", "x86", "x86_64"}
+)
+
+type archApiIncludes map[string]apiIncludes
+
+func archOsSpecificApiIncludes(ctx android.TopDownMutatorContext, module *Module) (archApiIncludes, apiIncludes) {
+ baseProps := bp2BuildParseBaseProps(ctx, module)
+ i := bp2BuildParseExportedIncludes(ctx, module, &baseProps.includes)
+ archRet := archApiIncludes{}
+ for _, arch := range allArches {
+ includes := i.Includes.SelectValue(
+ bazel.ArchConfigurationAxis,
+ arch)
+ systemIncludes := i.SystemIncludes.SelectValue(
+ bazel.ArchConfigurationAxis,
+ arch)
+ deps := baseProps.deps.SelectValue(
+ bazel.ArchConfigurationAxis,
+ arch)
+ attrs := bazelCcLibraryHeadersAttributes{
+ Export_includes: bazel.MakeStringListAttribute(includes),
+ Export_system_includes: bazel.MakeStringListAttribute(systemIncludes),
+ }
+ apiDeps := apiBazelTargets(deps)
+ if !apiDeps.IsEmpty() {
+ attrs.Deps = bazel.MakeLabelListAttribute(apiDeps)
+ }
+ apiIncludes := apiIncludes{
+ name: android.ApiContributionTargetName(module.Name()) + "." + arch,
+ attrs: bazelCcApiLibraryHeadersAttributes{
+ bazelCcLibraryHeadersAttributes: attrs,
+ Arch: proptools.StringPtr(arch),
+ },
+ }
+ archRet[arch] = apiIncludes
+ }
+
+ // apiIncludes for os == Android
+ androidOsDeps := baseProps.deps.SelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid)
+ androidOsAttrs := bazelCcLibraryHeadersAttributes{
+ Export_includes: bazel.MakeStringListAttribute(
+ i.Includes.SelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid),
+ ),
+ Export_system_includes: bazel.MakeStringListAttribute(
+ i.SystemIncludes.SelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid),
+ ),
+ }
+ androidOsApiDeps := apiBazelTargets(androidOsDeps)
+ if !androidOsApiDeps.IsEmpty() {
+ androidOsAttrs.Deps = bazel.MakeLabelListAttribute(androidOsApiDeps)
+ }
+ osRet := apiIncludes{
+ name: android.ApiContributionTargetName(module.Name()) + ".androidos",
+ attrs: bazelCcApiLibraryHeadersAttributes{
+ bazelCcLibraryHeadersAttributes: androidOsAttrs,
+ },
+ }
+ return archRet, osRet
}
diff --git a/cc/library_headers_test.go b/cc/library_headers_test.go
index 3e448ba..1924b2f 100644
--- a/cc/library_headers_test.go
+++ b/cc/library_headers_test.go
@@ -59,6 +59,7 @@
}
func TestPrebuiltLibraryHeadersPreferred(t *testing.T) {
+ t.Parallel()
bp := `
cc_library_headers {
name: "headers",
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 1bcbdc5..e743bb6 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -405,7 +405,7 @@
}
// Add the collated include dir properties to the output.
- for _, property := range android.SortedStringKeys(includeDirs) {
+ for _, property := range android.SortedKeys(includeDirs) {
outputProperties.AddProperty(property, includeDirs[property])
}
diff --git a/cc/library_stub.go b/cc/library_stub.go
new file mode 100644
index 0000000..18d3f21
--- /dev/null
+++ b/cc/library_stub.go
@@ -0,0 +1,509 @@
+// 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 (
+ "regexp"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/multitree"
+)
+
+var (
+ ndkVariantRegex = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
+ stubVariantRegex = regexp.MustCompile("apex\\.([a-zA-Z0-9]+)")
+)
+
+func init() {
+ RegisterLibraryStubBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("cc_api_library", CcApiLibraryFactory)
+ ctx.RegisterModuleType("cc_api_headers", CcApiHeadersFactory)
+ ctx.RegisterModuleType("cc_api_variant", CcApiVariantFactory)
+}
+
+func updateImportedLibraryDependency(ctx android.BottomUpMutatorContext) {
+ m, ok := ctx.Module().(*Module)
+ if !ok {
+ return
+ }
+
+ apiLibrary, ok := m.linker.(*apiLibraryDecorator)
+ if !ok {
+ return
+ }
+
+ if m.UseVndk() && apiLibrary.hasLLNDKStubs() {
+ // Add LLNDK variant dependency
+ if inList("llndk", apiLibrary.properties.Variants) {
+ variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
+ ctx.AddDependency(m, nil, variantName)
+ }
+ } else if m.IsSdkVariant() {
+ // Add NDK variant dependencies
+ targetVariant := "ndk." + m.StubsVersion()
+ if inList(targetVariant, apiLibrary.properties.Variants) {
+ variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
+ ctx.AddDependency(m, nil, variantName)
+ }
+ } else if m.IsStubs() {
+ targetVariant := "apex." + m.StubsVersion()
+ if inList(targetVariant, apiLibrary.properties.Variants) {
+ variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
+ ctx.AddDependency(m, nil, variantName)
+ }
+ }
+}
+
+// 'cc_api_library' is a module type which is from the exported API surface
+// with C shared library type. The module will replace original module, and
+// offer a link to the module that generates shared library object from the
+// map file.
+type apiLibraryProperties struct {
+ Src *string `android:"arch_variant"`
+ Variants []string
+}
+
+type apiLibraryDecorator struct {
+ *libraryDecorator
+ properties apiLibraryProperties
+}
+
+func CcApiLibraryFactory() android.Module {
+ module, decorator := NewLibrary(android.DeviceSupported)
+ apiLibraryDecorator := &apiLibraryDecorator{
+ libraryDecorator: decorator,
+ }
+ apiLibraryDecorator.BuildOnlyShared()
+
+ module.stl = nil
+ module.sanitize = nil
+ decorator.disableStripping()
+
+ module.compiler = nil
+ module.linker = apiLibraryDecorator
+ module.installer = nil
+ module.library = apiLibraryDecorator
+ module.AddProperties(&module.Properties, &apiLibraryDecorator.properties)
+
+ // Prevent default system libs (libc, libm, and libdl) from being linked
+ if apiLibraryDecorator.baseLinker.Properties.System_shared_libs == nil {
+ apiLibraryDecorator.baseLinker.Properties.System_shared_libs = []string{}
+ }
+
+ apiLibraryDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
+ apiLibraryDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+ module.Init()
+
+ return module
+}
+
+func (d *apiLibraryDecorator) Name(basename string) string {
+ return basename + multitree.GetApiImportSuffix()
+}
+
+// Export include dirs without checking for existence.
+// The directories are not guaranteed to exist during Soong analysis.
+func (d *apiLibraryDecorator) exportIncludes(ctx ModuleContext) {
+ exporterProps := d.flagExporter.Properties
+ for _, dir := range exporterProps.Export_include_dirs {
+ d.dirs = append(d.dirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
+ }
+ // system headers
+ for _, dir := range exporterProps.Export_system_include_dirs {
+ d.systemDirs = append(d.systemDirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
+ }
+}
+
+func (d *apiLibraryDecorator) linkerInit(ctx BaseModuleContext) {
+ d.baseLinker.linkerInit(ctx)
+
+ if d.hasNDKStubs() {
+ // Set SDK version of module as current
+ ctx.Module().(*Module).Properties.Sdk_version = StringPtr("current")
+
+ // Add NDK stub as NDK known libs
+ name := ctx.ModuleName()
+
+ ndkKnownLibsLock.Lock()
+ ndkKnownLibs := getNDKKnownLibs(ctx.Config())
+ if !inList(name, *ndkKnownLibs) {
+ *ndkKnownLibs = append(*ndkKnownLibs, name)
+ }
+ ndkKnownLibsLock.Unlock()
+ }
+}
+
+func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
+ m, _ := ctx.Module().(*Module)
+
+ var in android.Path
+
+ // src might not exist during the beginning of soong analysis in Multi-tree
+ if src := String(d.properties.Src); src != "" {
+ in = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), src)
+ }
+
+ libName := m.BaseModuleName() + multitree.GetApiImportSuffix()
+
+ load_cc_variant := func(apiVariantModule string) {
+ var mod android.Module
+
+ ctx.VisitDirectDeps(func(depMod android.Module) {
+ if depMod.Name() == apiVariantModule {
+ mod = depMod
+ libName = apiVariantModule
+ }
+ })
+
+ if mod != nil {
+ variantMod, ok := mod.(*CcApiVariant)
+ if ok {
+ in = variantMod.Src()
+
+ // Copy LLDNK properties to cc_api_library module
+ d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
+ d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
+ variantMod.exportProperties.Export_include_dirs...)
+
+ // Export headers as system include dirs if specified. Mostly for libc
+ if Bool(variantMod.exportProperties.Export_headers_as_system) {
+ d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
+ d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
+ d.libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+ d.libraryDecorator.flagExporter.Properties.Export_include_dirs = nil
+ }
+ }
+ }
+ }
+
+ if m.UseVndk() && d.hasLLNDKStubs() {
+ // LLNDK variant
+ load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "llndk", ""))
+ } else if m.IsSdkVariant() {
+ // NDK Variant
+ load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion()))
+ } else if m.IsStubs() {
+ // APEX Variant
+ load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "apex", m.StubsVersion()))
+ }
+
+ // Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
+ d.exportIncludes(ctx)
+ d.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
+ d.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
+ d.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
+ d.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
+ d.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
+
+ if in == nil {
+ ctx.PropertyErrorf("src", "Unable to locate source property")
+ return nil
+ }
+
+ // Make the _compilation_ of rdeps have an order-only dep on cc_api_library.src (an .so file)
+ // The .so file itself has an order-only dependency on the headers contributed by this library.
+ // Creating this dependency ensures that the headers are assembled before compilation of rdeps begins.
+ d.libraryDecorator.reexportDeps(in)
+ d.libraryDecorator.flagExporter.setProvider(ctx)
+
+ d.unstrippedOutputFile = in
+ libName += flags.Toolchain.ShlibSuffix()
+
+ tocFile := android.PathForModuleOut(ctx, libName+".toc")
+ d.tocFile = android.OptionalPathForPath(tocFile)
+ TransformSharedObjectToToc(ctx, in, tocFile)
+
+ outputFile := android.PathForModuleOut(ctx, libName)
+
+ // TODO(b/270485584) This copies with a new name, just to avoid conflict with prebuilts.
+ // We can just use original input if there is any way to avoid name conflict without copy.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Description: "API surface imported library",
+ Input: in,
+ Output: outputFile,
+ Args: map[string]string{
+ "cpFlags": "-L",
+ },
+ })
+
+ ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+ SharedLibrary: outputFile,
+ Target: ctx.Target(),
+
+ TableOfContents: d.tocFile,
+ })
+
+ d.shareStubs(ctx)
+
+ return outputFile
+}
+
+// Share additional information about stub libraries with provider
+func (d *apiLibraryDecorator) shareStubs(ctx ModuleContext) {
+ stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
+ if len(stubs) > 0 {
+ var stubsInfo []SharedStubLibrary
+ for _, stub := range stubs {
+ stubInfo := ctx.OtherModuleProvider(stub, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ flagInfo := ctx.OtherModuleProvider(stub, FlagExporterInfoProvider).(FlagExporterInfo)
+ stubsInfo = append(stubsInfo, SharedStubLibrary{
+ Version: moduleLibraryInterface(stub).stubsVersion(),
+ SharedLibraryInfo: stubInfo,
+ FlagExporterInfo: flagInfo,
+ })
+ }
+ ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
+ SharedStubLibraries: stubsInfo,
+
+ IsLLNDK: ctx.IsLlndk(),
+ })
+ }
+}
+
+func (d *apiLibraryDecorator) availableFor(what string) bool {
+ // Stub from API surface should be available for any APEX.
+ return true
+}
+
+func (d *apiLibraryDecorator) hasApexStubs() bool {
+ for _, variant := range d.properties.Variants {
+ if strings.HasPrefix(variant, "apex") {
+ return true
+ }
+ }
+ return false
+}
+
+func (d *apiLibraryDecorator) hasStubsVariants() bool {
+ return d.hasApexStubs()
+}
+
+func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
+ m, ok := ctx.Module().(*Module)
+
+ if !ok {
+ return nil
+ }
+
+ // TODO(b/244244438) Create more version information for NDK and APEX variations
+ // NDK variants
+ if m.IsSdkVariant() {
+ // TODO(b/249193999) Do not check if module has NDK stubs once all NDK cc_api_library contains ndk variant of cc_api_variant.
+ if d.hasNDKStubs() {
+ return d.getNdkVersions()
+ }
+ }
+
+ if d.hasLLNDKStubs() && m.UseVndk() {
+ // LLNDK libraries only need a single stubs variant.
+ return []string{android.FutureApiLevel.String()}
+ }
+
+ stubsVersions := d.getStubVersions()
+
+ if len(stubsVersions) != 0 {
+ return stubsVersions
+ }
+
+ if m.MinSdkVersion() == "" {
+ return nil
+ }
+
+ firstVersion, err := nativeApiLevelFromUser(ctx,
+ m.MinSdkVersion())
+
+ if err != nil {
+ return nil
+ }
+
+ return ndkLibraryVersions(ctx, firstVersion)
+}
+
+func (d *apiLibraryDecorator) hasLLNDKStubs() bool {
+ return inList("llndk", d.properties.Variants)
+}
+
+func (d *apiLibraryDecorator) hasNDKStubs() bool {
+ for _, variant := range d.properties.Variants {
+ if ndkVariantRegex.MatchString(variant) {
+ return true
+ }
+ }
+ return false
+}
+
+func (d *apiLibraryDecorator) getNdkVersions() []string {
+ ndkVersions := []string{}
+
+ for _, variant := range d.properties.Variants {
+ if match := ndkVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
+ ndkVersions = append(ndkVersions, match[1])
+ }
+ }
+
+ return ndkVersions
+}
+
+func (d *apiLibraryDecorator) getStubVersions() []string {
+ stubVersions := []string{}
+
+ for _, variant := range d.properties.Variants {
+ if match := stubVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
+ stubVersions = append(stubVersions, match[1])
+ }
+ }
+
+ return stubVersions
+}
+
+// 'cc_api_headers' is similar with 'cc_api_library', but which replaces
+// header libraries. The module will replace any dependencies to existing
+// original header libraries.
+type apiHeadersDecorator struct {
+ *libraryDecorator
+}
+
+func CcApiHeadersFactory() android.Module {
+ module, decorator := NewLibrary(android.DeviceSupported)
+ apiHeadersDecorator := &apiHeadersDecorator{
+ libraryDecorator: decorator,
+ }
+ apiHeadersDecorator.HeaderOnly()
+
+ module.stl = nil
+ module.sanitize = nil
+ decorator.disableStripping()
+
+ module.compiler = nil
+ module.linker = apiHeadersDecorator
+ module.installer = nil
+
+ // Prevent default system libs (libc, libm, and libdl) from being linked
+ if apiHeadersDecorator.baseLinker.Properties.System_shared_libs == nil {
+ apiHeadersDecorator.baseLinker.Properties.System_shared_libs = []string{}
+ }
+
+ apiHeadersDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
+ apiHeadersDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+ module.Init()
+
+ return module
+}
+
+func (d *apiHeadersDecorator) Name(basename string) string {
+ return basename + multitree.GetApiImportSuffix()
+}
+
+func (d *apiHeadersDecorator) availableFor(what string) bool {
+ // Stub from API surface should be available for any APEX.
+ return true
+}
+
+type ccApiexportProperties struct {
+ Src *string `android:"arch_variant"`
+ Variant *string
+ Version *string
+}
+
+type variantExporterProperties struct {
+ // Header directory to export
+ Export_include_dirs []string `android:"arch_variant"`
+
+ // Export all headers as system include
+ Export_headers_as_system *bool
+}
+
+type CcApiVariant struct {
+ android.ModuleBase
+
+ properties ccApiexportProperties
+ exportProperties variantExporterProperties
+
+ src android.Path
+}
+
+var _ android.Module = (*CcApiVariant)(nil)
+var _ android.ImageInterface = (*CcApiVariant)(nil)
+
+func CcApiVariantFactory() android.Module {
+ module := &CcApiVariant{}
+
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.exportProperties)
+
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
+ return module
+}
+
+func (v *CcApiVariant) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // No need to build
+
+ if String(v.properties.Src) == "" {
+ ctx.PropertyErrorf("src", "src is a required property")
+ }
+
+ // Skip the existence check of the stub prebuilt file.
+ // The file is not guaranteed to exist during Soong analysis.
+ // Build orchestrator will be responsible for creating a connected ninja graph.
+ v.src = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), String(v.properties.Src))
+}
+
+func (v *CcApiVariant) Name() string {
+ version := String(v.properties.Version)
+ return BuildApiVariantName(v.BaseModuleName(), *v.properties.Variant, version)
+}
+
+func (v *CcApiVariant) Src() android.Path {
+ return v.src
+}
+
+func BuildApiVariantName(baseName string, variant string, version string) string {
+ names := []string{baseName, variant}
+ if version != "" {
+ names = append(names, version)
+ }
+
+ return strings.Join(names[:], ".") + multitree.GetApiImportSuffix()
+}
+
+// Implement ImageInterface to generate image variants
+func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+ return inList(String(v.properties.Variant), []string{"ndk", "apex"})
+}
+func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
+func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
+func (v *CcApiVariant) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
+func (v *CcApiVariant) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { return false }
+func (v *CcApiVariant) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+ var variations []string
+ platformVndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
+
+ if String(v.properties.Variant) == "llndk" {
+ variations = append(variations, VendorVariationPrefix+platformVndkVersion)
+ variations = append(variations, ProductVariationPrefix+platformVndkVersion)
+ }
+
+ return variations
+}
+func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+}
diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go
new file mode 100644
index 0000000..528577a
--- /dev/null
+++ b/cc/library_stub_test.go
@@ -0,0 +1,459 @@
+// 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"
+
+ "github.com/google/blueprint"
+)
+
+func hasDirectDependency(t *testing.T, ctx *android.TestResult, from android.Module, to android.Module) bool {
+ t.Helper()
+ var found bool
+ ctx.VisitDirectDeps(from, func(dep blueprint.Module) {
+ if dep == to {
+ found = true
+ }
+ })
+ return found
+}
+
+func TestApiLibraryReplacesExistingModule(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libbar"],
+ vendor_available: true,
+ }
+
+ cc_library {
+ name: "libbar",
+ }
+
+ cc_api_library {
+ name: "libbar",
+ vendor_available: true,
+ src: "libbar.so",
+ }
+
+ api_imports {
+ name: "api_imports",
+ shared_libs: [
+ "libbar",
+ ],
+ }
+ `
+
+ ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+ libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+ libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+ libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+
+ android.AssertBoolEquals(t, "original library should be linked with non-stub variant", true, hasDirectDependency(t, ctx, libfoo, libbar))
+ android.AssertBoolEquals(t, "Stub library from API surface should be not linked with non-stub variant", false, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+
+ libfooVendor := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
+ libbarApiImportVendor := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
+
+ android.AssertBoolEquals(t, "original library should not be linked", false, hasDirectDependency(t, ctx, libfooVendor, libbar))
+ android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfooVendor, libbarApiImportVendor))
+}
+
+func TestApiLibraryDoNotRequireOriginalModule(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libbar"],
+ vendor: true,
+ }
+
+ cc_api_library {
+ name: "libbar",
+ src: "libbar.so",
+ vendor_available: true,
+ }
+
+ api_imports {
+ name: "api_imports",
+ shared_libs: [
+ "libbar",
+ ],
+ }
+ `
+
+ ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+ libfoo := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
+ libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
+
+ android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+}
+
+func TestApiLibraryShouldNotReplaceWithoutApiImport(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libbar"],
+ vendor_available: true,
+ }
+
+ cc_library {
+ name: "libbar",
+ vendor_available: true,
+ }
+
+ cc_api_library {
+ name: "libbar",
+ src: "libbar.so",
+ vendor_available: true,
+ }
+
+ api_imports {
+ name: "api_imports",
+ shared_libs: [],
+ }
+ `
+
+ ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+ libfoo := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
+ libbar := ctx.ModuleForTests("libbar", "android_vendor.29_arm64_armv8-a_shared").Module()
+ libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
+
+ android.AssertBoolEquals(t, "original library should be linked", true, hasDirectDependency(t, ctx, libfoo, libbar))
+ android.AssertBoolEquals(t, "Stub library from API surface should not be linked", false, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+}
+
+func TestExportDirFromStubLibrary(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libfoo",
+ export_include_dirs: ["source_include_dir"],
+ export_system_include_dirs: ["source_system_include_dir"],
+ vendor_available: true,
+ }
+ cc_api_library {
+ name: "libfoo",
+ export_include_dirs: ["stub_include_dir"],
+ export_system_include_dirs: ["stub_system_include_dir"],
+ vendor_available: true,
+ src: "libfoo.so",
+ }
+ api_imports {
+ name: "api_imports",
+ shared_libs: [
+ "libfoo",
+ ],
+ header_libs: [],
+ }
+ // vendor binary
+ cc_binary {
+ name: "vendorbin",
+ vendor: true,
+ srcs: ["vendor.cc"],
+ shared_libs: ["libfoo"],
+ }
+ `
+ ctx := prepareForCcTest.RunTestWithBp(t, bp)
+ vendorCFlags := ctx.ModuleForTests("vendorbin", "android_vendor.29_arm64_armv8-a").Rule("cc").Args["cFlags"]
+ android.AssertStringDoesContain(t, "Vendor binary should compile using headers provided by stub", vendorCFlags, "-Istub_include_dir")
+ android.AssertStringDoesNotContain(t, "Vendor binary should not compile using headers of source", vendorCFlags, "-Isource_include_dir")
+ android.AssertStringDoesContain(t, "Vendor binary should compile using system headers provided by stub", vendorCFlags, "-isystem stub_system_include_dir")
+ android.AssertStringDoesNotContain(t, "Vendor binary should not compile using system headers of source", vendorCFlags, "-isystem source_system_include_dir")
+
+ vendorImplicits := ctx.ModuleForTests("vendorbin", "android_vendor.29_arm64_armv8-a").Rule("cc").OrderOnly.Strings()
+ // Building the stub.so file first assembles its .h files in multi-tree out.
+ // These header files are required for compiling the other API domain (vendor in this case)
+ android.AssertStringListContains(t, "Vendor binary compilation should have an implicit dep on the stub .so file", vendorImplicits, "libfoo.so")
+}
+
+func TestApiLibraryWithLlndkVariant(t *testing.T) {
+ bp := `
+ cc_binary {
+ name: "binfoo",
+ vendor: true,
+ srcs: ["binfoo.cc"],
+ shared_libs: ["libbar"],
+ }
+
+ cc_api_library {
+ name: "libbar",
+ // TODO(b/244244438) Remove src property once all variants are implemented.
+ src: "libbar.so",
+ vendor_available: true,
+ variants: [
+ "llndk",
+ ],
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "llndk",
+ src: "libbar_llndk.so",
+ export_include_dirs: ["libbar_llndk_include"]
+ }
+
+ api_imports {
+ name: "api_imports",
+ shared_libs: [
+ "libbar",
+ ],
+ header_libs: [],
+ }
+ `
+
+ ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+ binfoo := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Module()
+ libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
+ libbarApiVariant := ctx.ModuleForTests("libbar.llndk.apiimport", "android_vendor.29_arm64_armv8-a").Module()
+
+ android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, binfoo, libbarApiImport))
+ android.AssertBoolEquals(t, "Stub library variant from API surface should be linked", true, hasDirectDependency(t, ctx, libbarApiImport, libbarApiVariant))
+
+ binFooLibFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("ld").Args["libFlags"]
+ android.AssertStringDoesContain(t, "Vendor binary should be linked with LLNDK variant source", binFooLibFlags, "libbar.llndk.apiimport.so")
+
+ binFooCFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("cc").Args["cFlags"]
+ android.AssertStringDoesContain(t, "Vendor binary should include headers from the LLNDK variant source", binFooCFlags, "-Ilibbar_llndk_include")
+}
+
+func TestApiLibraryWithNdkVariant(t *testing.T) {
+ bp := `
+ cc_binary {
+ name: "binfoo",
+ sdk_version: "29",
+ srcs: ["binfoo.cc"],
+ shared_libs: ["libbar"],
+ stl: "c++_shared",
+ }
+
+ cc_binary {
+ name: "binbaz",
+ sdk_version: "30",
+ srcs: ["binbaz.cc"],
+ shared_libs: ["libbar"],
+ stl: "c++_shared",
+ }
+
+ cc_binary {
+ name: "binqux",
+ srcs: ["binfoo.cc"],
+ shared_libs: ["libbar"],
+ }
+
+ cc_library {
+ name: "libbar",
+ srcs: ["libbar.cc"],
+ }
+
+ cc_api_library {
+ name: "libbar",
+ // TODO(b/244244438) Remove src property once all variants are implemented.
+ src: "libbar.so",
+ variants: [
+ "ndk.29",
+ "ndk.30",
+ "ndk.current",
+ ],
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "ndk",
+ version: "29",
+ src: "libbar_ndk_29.so",
+ export_include_dirs: ["libbar_ndk_29_include"]
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "ndk",
+ version: "30",
+ src: "libbar_ndk_30.so",
+ export_include_dirs: ["libbar_ndk_30_include"]
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "ndk",
+ version: "current",
+ src: "libbar_ndk_current.so",
+ export_include_dirs: ["libbar_ndk_current_include"]
+ }
+
+ api_imports {
+ name: "api_imports",
+ shared_libs: [
+ "libbar",
+ ],
+ header_libs: [],
+ }
+ `
+
+ ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+ binfoo := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Module()
+ libbarApiImportv29 := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_sdk_shared_29").Module()
+ libbarApiVariantv29 := ctx.ModuleForTests("libbar.ndk.29.apiimport", "android_arm64_armv8-a_sdk").Module()
+ libbarApiImportv30 := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_sdk_shared_30").Module()
+ libbarApiVariantv30 := ctx.ModuleForTests("libbar.ndk.30.apiimport", "android_arm64_armv8-a_sdk").Module()
+
+ android.AssertBoolEquals(t, "Stub library from API surface should be linked with target version", true, hasDirectDependency(t, ctx, binfoo, libbarApiImportv29))
+ android.AssertBoolEquals(t, "Stub library variant from API surface should be linked with target version", true, hasDirectDependency(t, ctx, libbarApiImportv29, libbarApiVariantv29))
+ android.AssertBoolEquals(t, "Stub library from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, binfoo, libbarApiImportv30))
+ android.AssertBoolEquals(t, "Stub library variant from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, libbarApiImportv29, libbarApiVariantv30))
+
+ binbaz := ctx.ModuleForTests("binbaz", "android_arm64_armv8-a_sdk").Module()
+
+ android.AssertBoolEquals(t, "Stub library from API surface should be linked with target version", true, hasDirectDependency(t, ctx, binbaz, libbarApiImportv30))
+ android.AssertBoolEquals(t, "Stub library from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, binbaz, libbarApiImportv29))
+
+ binFooLibFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("ld").Args["libFlags"]
+ android.AssertStringDoesContain(t, "Binary using sdk should be linked with NDK variant source", binFooLibFlags, "libbar.ndk.29.apiimport.so")
+
+ binFooCFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("cc").Args["cFlags"]
+ android.AssertStringDoesContain(t, "Binary using sdk should include headers from the NDK variant source", binFooCFlags, "-Ilibbar_ndk_29_include")
+
+ binQux := ctx.ModuleForTests("binqux", "android_arm64_armv8-a").Module()
+ android.AssertBoolEquals(t, "NDK Stub library from API surface should not be linked with nonSdk binary", false,
+ (hasDirectDependency(t, ctx, binQux, libbarApiImportv30) || hasDirectDependency(t, ctx, binQux, libbarApiImportv29)))
+}
+
+func TestApiLibraryWithMultipleVariants(t *testing.T) {
+ bp := `
+ cc_binary {
+ name: "binfoo",
+ sdk_version: "29",
+ srcs: ["binfoo.cc"],
+ shared_libs: ["libbar"],
+ stl: "c++_shared",
+ }
+
+ cc_binary {
+ name: "binbaz",
+ vendor: true,
+ srcs: ["binbaz.cc"],
+ shared_libs: ["libbar"],
+ }
+
+ cc_library {
+ name: "libbar",
+ srcs: ["libbar.cc"],
+ }
+
+ cc_api_library {
+ name: "libbar",
+ // TODO(b/244244438) Remove src property once all variants are implemented.
+ src: "libbar.so",
+ vendor_available: true,
+ variants: [
+ "llndk",
+ "ndk.29",
+ "ndk.30",
+ "ndk.current",
+ "apex.29",
+ "apex.30",
+ "apex.current",
+ ],
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "ndk",
+ version: "29",
+ src: "libbar_ndk_29.so",
+ export_include_dirs: ["libbar_ndk_29_include"]
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "ndk",
+ version: "30",
+ src: "libbar_ndk_30.so",
+ export_include_dirs: ["libbar_ndk_30_include"]
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "ndk",
+ version: "current",
+ src: "libbar_ndk_current.so",
+ export_include_dirs: ["libbar_ndk_current_include"]
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "apex",
+ version: "29",
+ src: "libbar_apex_29.so",
+ export_include_dirs: ["libbar_apex_29_include"]
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "apex",
+ version: "30",
+ src: "libbar_apex_30.so",
+ export_include_dirs: ["libbar_apex_30_include"]
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "apex",
+ version: "current",
+ src: "libbar_apex_current.so",
+ export_include_dirs: ["libbar_apex_current_include"]
+ }
+
+ cc_api_variant {
+ name: "libbar",
+ variant: "llndk",
+ src: "libbar_llndk.so",
+ export_include_dirs: ["libbar_llndk_include"]
+ }
+
+ api_imports {
+ name: "api_imports",
+ shared_libs: [
+ "libbar",
+ ],
+ apex_shared_libs: [
+ "libbar",
+ ],
+ }
+ `
+ ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+ binfoo := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Module()
+ libbarApiImportv29 := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_sdk_shared_29").Module()
+ libbarApiImportLlndk := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
+
+ android.AssertBoolEquals(t, "Binary using SDK should be linked with API library from NDK variant", true, hasDirectDependency(t, ctx, binfoo, libbarApiImportv29))
+ android.AssertBoolEquals(t, "Binary using SDK should not be linked with API library from LLNDK variant", false, hasDirectDependency(t, ctx, binfoo, libbarApiImportLlndk))
+
+ binbaz := ctx.ModuleForTests("binbaz", "android_vendor.29_arm64_armv8-a").Module()
+
+ android.AssertBoolEquals(t, "Vendor binary should be linked with API library from LLNDK variant", true, hasDirectDependency(t, ctx, binbaz, libbarApiImportLlndk))
+ android.AssertBoolEquals(t, "Vendor binary should not be linked with API library from NDK variant", false, hasDirectDependency(t, ctx, binbaz, libbarApiImportv29))
+
+}
diff --git a/cc/library_test.go b/cc/library_test.go
index 6d5eda2..dbe2be8 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -23,6 +23,7 @@
)
func TestLibraryReuse(t *testing.T) {
+ t.Parallel()
t.Run("simple", func(t *testing.T) {
ctx := testCc(t, `
cc_library {
@@ -191,6 +192,7 @@
}
func TestStubsVersions(t *testing.T) {
+ t.Parallel()
bp := `
cc_library {
name: "libfoo",
@@ -214,6 +216,7 @@
}
func TestStubsVersions_NotSorted(t *testing.T) {
+ t.Parallel()
bp := `
cc_library {
name: "libfoo",
@@ -229,6 +232,7 @@
}
func TestStubsVersions_ParseError(t *testing.T) {
+ t.Parallel()
bp := `
cc_library {
name: "libfoo",
@@ -243,6 +247,7 @@
}
func TestCcLibraryWithBazel(t *testing.T) {
+ t.Parallel()
bp := `
cc_library {
name: "foo",
@@ -259,6 +264,7 @@
SystemIncludes: []string{"system_include"},
Headers: []string{"foo.h"},
RootDynamicLibraries: []string{"foo.so"},
+ UnstrippedOutput: "foo_unstripped.so",
},
"//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
CcObjectFiles: []string{"foo.o"},
@@ -294,6 +300,76 @@
expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"}
android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+ android.AssertStringEquals(t, "unstripped shared library", "outputbase/execroot/__main__/foo_unstripped.so", sharedFoo.(*Module).linker.unstrippedOutputFilePath().String())
+ flagExporter = ctx.ModuleProvider(sharedFoo, FlagExporterInfoProvider).(FlagExporterInfo)
+ android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs)
+ android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)
+ android.AssertPathsRelativeToTopEquals(t, "exported headers", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.GeneratedHeaders)
+ android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
+}
+
+func TestCcLibraryWithBazelValidations(t *testing.T) {
+ t.Parallel()
+ bp := `
+cc_library {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+ tidy: true,
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcObjectFiles: []string{"foo.o"},
+ Includes: []string{"include"},
+ SystemIncludes: []string{"system_include"},
+ Headers: []string{"foo.h"},
+ RootDynamicLibraries: []string{"foo.so"},
+ UnstrippedOutput: "foo_unstripped.so",
+ },
+ "//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
+ CcObjectFiles: []string{"foo.o"},
+ Includes: []string{"include"},
+ SystemIncludes: []string{"system_include"},
+ Headers: []string{"foo.h"},
+ RootStaticArchives: []string{"foo.a"},
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ },
+ }
+ ctx := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureMergeEnv(map[string]string{
+ "ALLOW_LOCAL_TIDY_TRUE": "1",
+ }),
+ ).RunTestWithConfig(t, config).TestContext
+
+ staticFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+ outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+
+ expectedOutputFiles := []string{"out/soong/.intermediates/foo/android_arm_armv7-a-neon_static/validated/foo.a"}
+ android.AssertPathsRelativeToTopEquals(t, "output files", expectedOutputFiles, outputFiles)
+
+ flagExporter := ctx.ModuleProvider(staticFoo, FlagExporterInfoProvider).(FlagExporterInfo)
+ android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs)
+ android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)
+ android.AssertPathsRelativeToTopEquals(t, "exported headers", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.GeneratedHeaders)
+ android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
+
+ sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ outputFiles, err = sharedFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_library outputfiles %s", err)
+ }
+ expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ android.AssertStringEquals(t, "unstripped shared library", "outputbase/execroot/__main__/foo_unstripped.so", sharedFoo.(*Module).linker.unstrippedOutputFilePath().String())
flagExporter = ctx.ModuleProvider(sharedFoo, FlagExporterInfoProvider).(FlagExporterInfo)
android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs)
android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)
@@ -302,6 +378,7 @@
}
func TestLibraryVersionScript(t *testing.T) {
+ t.Parallel()
result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
cc_library {
name: "libfoo",
@@ -319,6 +396,7 @@
}
func TestLibraryDynamicList(t *testing.T) {
+ t.Parallel()
result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
cc_library {
name: "libfoo",
@@ -335,7 +413,62 @@
}
+func TestCcLibrarySharedWithBazelValidations(t *testing.T) {
+ t.Parallel()
+ bp := `
+cc_library_shared {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+ tidy: true,
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcObjectFiles: []string{"foo.o"},
+ Includes: []string{"include"},
+ SystemIncludes: []string{"system_include"},
+ RootDynamicLibraries: []string{"foo.so"},
+ TocFile: "foo.so.toc",
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ },
+ }
+ ctx := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureMergeEnv(map[string]string{
+ "ALLOW_LOCAL_TIDY_TRUE": "1",
+ }),
+ ).RunTestWithConfig(t, config).TestContext
+
+ sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ producer := sharedFoo.(android.OutputFileProducer)
+ outputFiles, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{"out/soong/.intermediates/foo/android_arm_armv7-a-neon_shared/validated/foo.so"}
+ android.AssertPathsRelativeToTopEquals(t, "output files", expectedOutputFiles, outputFiles)
+
+ tocFilePath := sharedFoo.(*Module).Toc()
+ if !tocFilePath.Valid() {
+ t.Errorf("Invalid tocFilePath: %s", tocFilePath)
+ }
+ tocFile := tocFilePath.Path()
+ expectedToc := "outputbase/execroot/__main__/foo.so.toc"
+ android.AssertStringEquals(t, "toc file", expectedToc, tocFile.String())
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0]
+ expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
+ gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+ android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_library_shared", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+}
+
func TestCcLibrarySharedWithBazel(t *testing.T) {
+ t.Parallel()
bp := `
cc_library_shared {
name: "foo",
@@ -378,9 +511,11 @@
expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_library_shared", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
}
func TestWholeStaticLibPrebuilts(t *testing.T) {
+ t.Parallel()
result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
cc_prebuilt_library_static {
name: "libprebuilt",
diff --git a/cc/linkable.go b/cc/linkable.go
index bad4b4a..9578807 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -3,6 +3,7 @@
import (
"android/soong/android"
"android/soong/bazel/cquery"
+ "android/soong/fuzz"
"android/soong/snapshot"
"github.com/google/blueprint"
@@ -22,16 +23,9 @@
// 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
-
// 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
@@ -127,6 +121,17 @@
IsPrebuilt() bool
Toc() android.OptionalPath
+ // IsFuzzModule returns true if this a *_fuzz module.
+ IsFuzzModule() bool
+
+ // FuzzPackagedModule returns the fuzz.FuzzPackagedModule for this module.
+ // Expects that IsFuzzModule returns true.
+ FuzzPackagedModule() fuzz.FuzzPackagedModule
+
+ // FuzzSharedLibraries returns the shared library dependencies for this module.
+ // Expects that IsFuzzModule returns true.
+ FuzzSharedLibraries() android.Paths
+
Device() bool
Host() bool
@@ -260,6 +265,12 @@
// VndkVersion returns the VNDK version string for this module.
VndkVersion() string
+
+ // Partition returns the partition string for this module.
+ Partition() string
+
+ // FuzzModule returns the fuzz.FuzzModule associated with the module.
+ FuzzModuleStruct() fuzz.FuzzModule
}
var (
diff --git a/cc/linker.go b/cc/linker.go
index bea65d4..257fe86 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "path/filepath"
"android/soong/android"
"android/soong/cc/config"
@@ -221,12 +222,15 @@
// Generate compact dynamic relocation table, default true.
Pack_relocations *bool `android:"arch_variant"`
- // local file name to pass to the linker as --version_script
+ // local file name to pass to the linker as --version-script
Version_script *string `android:"path,arch_variant"`
// 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"`
@@ -234,29 +238,14 @@
Exclude_shared_libs []string `android:"arch_variant"`
}
-func invertBoolPtr(value *bool) *bool {
- if value == nil {
- return nil
- }
- ret := !(*value)
- return &ret
+func (blp *BaseLinkerProperties) crt() bool {
+ // Since crt is enabled for almost every module compiling against the Bionic runtime,
+ // we interpret `nil` as enabled.
+ return blp.Nocrt == nil || !*blp.Nocrt
}
-func (blp *BaseLinkerProperties) crt() *bool {
- val := invertBoolPtr(blp.Nocrt)
- if val != nil && *val {
- // == True
- //
- // Since crt is enabled for almost every module compiling against the Bionic runtime,
- // use `nil` when it's enabled, and rely on the Starlark macro to set it to True by default.
- // This keeps the BUILD files clean.
- return nil
- }
- return val // can be False or nil
-}
-
-func (blp *BaseLinkerProperties) libCrt() *bool {
- return invertBoolPtr(blp.No_libcrt)
+func (blp *BaseLinkerProperties) libCrt() bool {
+ return blp.No_libcrt == nil || !*blp.No_libcrt
}
func NewBaseLinker(sanitize *sanitize) *baseLinker {
@@ -267,8 +256,7 @@
type baseLinker struct {
Properties BaseLinkerProperties
dynamicProperties struct {
- RunPaths []string `blueprint:"mutated"`
- BuildStubs bool `blueprint:"mutated"`
+ BuildStubs bool `blueprint:"mutated"`
}
sanitize *sanitize
@@ -278,13 +266,8 @@
linker.Properties.Ldflags = append(linker.Properties.Ldflags, flags...)
}
-// linkerInit initializes dynamic properties of the linker (such as runpath).
+// linkerInit initializes dynamic properties of the linker.
func (linker *baseLinker) linkerInit(ctx BaseModuleContext) {
- if ctx.toolchain().Is64Bit() {
- linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib64", "lib64")
- } else {
- linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib", "lib")
- }
}
func (linker *baseLinker) linkerProps() []interface{} {
@@ -386,9 +369,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.
@@ -397,8 +378,8 @@
if ctx.toolchain().Bionic() {
// libclang_rt.builtins has to be last on the command line
- if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
- deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+ if linker.Properties.libCrt() && !ctx.header() {
+ deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
}
if inList("libdl", deps.SharedLibs) {
@@ -420,8 +401,8 @@
ctx.PropertyErrorf("system_shared_libs", "libdl must be after libc")
}
} else if ctx.toolchain().Musl() {
- if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
- deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+ if linker.Properties.libCrt() && !ctx.header() {
+ deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
}
}
@@ -435,11 +416,6 @@
}
func (linker *baseLinker) useClangLld(ctx ModuleContext) bool {
- // Clang lld is not ready for for Darwin host executables yet.
- // See https://lld.llvm.org/AtomLLD.html for status of lld for Mach-O.
- if ctx.Darwin() {
- return false
- }
if linker.Properties.Use_clang_lld != nil {
return Bool(linker.Properties.Use_clang_lld)
}
@@ -529,25 +505,12 @@
}
}
- if ctx.toolchain().LibclangRuntimeLibraryArch() != "" {
- flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--exclude-libs="+config.BuiltinsRuntimeLibrary(ctx.toolchain())+".a")
- }
-
CheckBadLinkerFlags(ctx, "ldflags", linker.Properties.Ldflags)
flags.Local.LdFlags = append(flags.Local.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
- if ctx.Host() && !ctx.Windows() {
- rpathPrefix := `\$$ORIGIN/`
- if ctx.Darwin() {
- rpathPrefix = "@loader_path/"
- }
-
- if !ctx.static() {
- for _, rpath := range linker.dynamicProperties.RunPaths {
- flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
- }
- }
+ if ctx.Host() && !ctx.Windows() && !ctx.static() {
+ flags.Global.LdFlags = append(flags.Global.LdFlags, RpathFlags(ctx)...)
}
if ctx.useSdk() {
@@ -580,13 +543,13 @@
ctx.PropertyErrorf("version_script", "Not supported on Darwin")
} else {
flags.Local.LdFlags = append(flags.Local.LdFlags,
- "-Wl,--version-script,"+versionScript.String())
+ config.VersionScriptFlagPrefix+versionScript.String())
flags.LdFlagsDeps = append(flags.LdFlagsDeps, versionScript.Path())
if linker.sanitize.isSanitizerEnabled(cfi) {
- cfiExportsMap := android.PathForSource(ctx, cfiExportsMapPath)
+ cfiExportsMap := android.PathForSource(ctx, cfiExportsMapPath+"/"+cfiExportsMapFilename)
flags.Local.LdFlags = append(flags.Local.LdFlags,
- "-Wl,--version-script,"+cfiExportsMap.String())
+ config.VersionScriptFlagPrefix+cfiExportsMap.String())
flags.LdFlagsDeps = append(flags.LdFlagsDeps, cfiExportsMap)
}
}
@@ -602,11 +565,61 @@
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
}
+// RpathFlags returns the rpath linker flags for current target to search the following directories relative
+// to the binary:
+//
+// - "." to find libraries alongside tests
+// - "lib[64]" to find libraries in a subdirectory of the binaries' directory
+// - "../lib[64]" to find libraries when the binaries are in a bin directory
+// - "../../lib[64]" to find libraries in out/host/linux-x86/lib64 when the test or binary is in
+// out/host/linux-x86/nativetest/<test dir>/<test>
+// - "../../../lib[[64] to find libraries in out/host/linux-x86/lib64 when the test or binary is in
+// out/host/linux-x86/testcases/<test dir>/<CPU>/<test>
+func RpathFlags(ctx android.ModuleContext) []string {
+ key := struct {
+ os android.OsType
+ arch android.ArchType
+ }{ctx.Target().Os, ctx.Target().Arch.ArchType}
+
+ return ctx.Config().OnceStringSlice(android.NewCustomOnceKey(key), func() []string {
+ rpathPrefix := `\$$ORIGIN/`
+ if key.os == android.Darwin {
+ rpathPrefix = "@loader_path/"
+ }
+
+ var libDir string
+ if key.arch.Multilib == "lib64" {
+ libDir = "lib64"
+ } else {
+ libDir = "lib"
+ }
+
+ return []string{
+ "-Wl,-rpath," + rpathPrefix,
+ "-Wl,-rpath," + rpathPrefix + libDir,
+ "-Wl,-rpath," + rpathPrefix + filepath.Join("..", libDir),
+ "-Wl,-rpath," + rpathPrefix + filepath.Join("../..", libDir),
+ "-Wl,-rpath," + rpathPrefix + filepath.Join("../../..", libDir),
+ }
+ })
+}
+
func (linker *baseLinker) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
panic(fmt.Errorf("baseLinker doesn't know how to link"))
diff --git a/cc/lto.go b/cc/lto.go
index 2c274bd..1afa1dd 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -15,9 +15,9 @@
package cc
import (
- "github.com/google/blueprint/proptools"
-
"android/soong/android"
+
+ "github.com/google/blueprint/proptools"
)
// LTO (link-time optimization) allows the compiler to optimize and generate
@@ -49,9 +49,12 @@
// Dep properties indicate that this module needs to be built with LTO
// since it is an object dependency of an LTO module.
- FullDep bool `blueprint:"mutated"`
- ThinDep bool `blueprint:"mutated"`
- NoLtoDep bool `blueprint:"mutated"`
+ FullEnabled bool `blueprint:"mutated"`
+ ThinEnabled bool `blueprint:"mutated"`
+ NoLtoEnabled bool `blueprint:"mutated"`
+ FullDep bool `blueprint:"mutated"`
+ ThinDep bool `blueprint:"mutated"`
+ NoLtoDep bool `blueprint:"mutated"`
// Use clang lld instead of gnu ld.
Use_clang_lld *bool
@@ -70,7 +73,7 @@
func (lto *lto) begin(ctx BaseModuleContext) {
if ctx.Config().IsEnvTrue("DISABLE_LTO") {
- lto.Properties.Lto.Never = proptools.BoolPtr(true)
+ lto.Properties.NoLtoEnabled = true
}
}
@@ -88,6 +91,11 @@
return flags
}
+ // TODO(b/254713216): LTO doesn't work on riscv64 yet.
+ if ctx.Arch().ArchType == android.Riscv64 {
+ return flags
+ }
+
if lto.LTO(ctx) {
var ltoCFlag string
var ltoLdFlag string
@@ -101,6 +109,7 @@
}
flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlag)
+ flags.Local.AsFlags = append(flags.Local.AsFlags, ltoCFlag)
flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlag)
flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlag)
@@ -136,21 +145,29 @@
}
func (lto *lto) DefaultThinLTO(ctx BaseModuleContext) bool {
+ // LP32 has many subtle issues and less test coverage.
+ lib32 := ctx.Arch().ArchType.Multilib == "lib32"
+ // CFI enables full LTO.
+ cfi := ctx.isCfi()
+ // Performance and binary size are less important for host binaries and tests.
host := ctx.Host()
- vndk := ctx.isVndk() // b/169217596
- return GlobalThinLTO(ctx) && !lto.Never() && !host && !vndk
+ test := ctx.testBinary() || ctx.testLibrary()
+ // FIXME: ThinLTO for VNDK produces different output.
+ // b/169217596
+ vndk := ctx.isVndk()
+ return GlobalThinLTO(ctx) && !lto.Never() && !lib32 && !cfi && !host && !test && !vndk
}
func (lto *lto) FullLTO() bool {
- return lto != nil && Bool(lto.Properties.Lto.Full)
+ return lto != nil && (proptools.Bool(lto.Properties.Lto.Full) || lto.Properties.FullEnabled)
}
func (lto *lto) ThinLTO() bool {
- return lto != nil && Bool(lto.Properties.Lto.Thin)
+ return lto != nil && (proptools.Bool(lto.Properties.Lto.Thin) || lto.Properties.ThinEnabled)
}
func (lto *lto) Never() bool {
- return lto != nil && Bool(lto.Properties.Lto.Never)
+ return lto != nil && (proptools.Bool(lto.Properties.Lto.Never) || lto.Properties.NoLtoEnabled)
}
func GlobalThinLTO(ctx android.BaseModuleContext) bool {
@@ -246,13 +263,13 @@
// LTO properties for dependencies
if name == "lto-full" {
- variation.lto.Properties.Lto.Full = proptools.BoolPtr(true)
+ variation.lto.Properties.FullEnabled = true
}
if name == "lto-thin" {
- variation.lto.Properties.Lto.Thin = proptools.BoolPtr(true)
+ variation.lto.Properties.ThinEnabled = true
}
if name == "lto-none" {
- variation.lto.Properties.Lto.Never = proptools.BoolPtr(true)
+ variation.lto.Properties.NoLtoEnabled = true
}
variation.Properties.PreventInstall = true
variation.Properties.HideFromMake = true
diff --git a/cc/lto_test.go b/cc/lto_test.go
new file mode 100644
index 0000000..4220f32
--- /dev/null
+++ b/cc/lto_test.go
@@ -0,0 +1,241 @@
+// 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 (
+ "strings"
+ "testing"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+func TestThinLtoDeps(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "lto_enabled",
+ srcs: ["src.c"],
+ static_libs: ["foo", "lib_never_lto"],
+ shared_libs: ["bar"],
+ lto: {
+ thin: true,
+ }
+ }
+ cc_library_static {
+ name: "foo",
+ static_libs: ["baz"],
+ }
+ cc_library_shared {
+ name: "bar",
+ static_libs: ["qux"],
+ }
+ cc_library_static {
+ name: "baz",
+ }
+ cc_library_static {
+ name: "qux",
+ }
+ cc_library_static {
+ name: "lib_never_lto",
+ lto: {
+ never: true,
+ },
+ }
+`
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ ).RunTestWithBp(t, bp)
+
+ libLto := result.ModuleForTests("lto_enabled", "android_arm64_armv8-a_shared").Module()
+
+ hasDep := func(m android.Module, wantDep android.Module) bool {
+ var found bool
+ result.VisitDirectDeps(m, func(dep blueprint.Module) {
+ if dep == wantDep {
+ found = true
+ }
+ })
+ return found
+ }
+
+ libFoo := result.ModuleForTests("foo", "android_arm64_armv8-a_static_lto-thin").Module()
+ if !hasDep(libLto, libFoo) {
+ t.Errorf("'lto_enabled' missing dependency on thin lto variant of 'foo'")
+ }
+
+ libBaz := result.ModuleForTests("baz", "android_arm64_armv8-a_static_lto-thin").Module()
+ if !hasDep(libFoo, libBaz) {
+ t.Errorf("'foo' missing dependency on thin lto variant of transitive dep 'baz'")
+ }
+
+ libNeverLto := result.ModuleForTests("lib_never_lto", "android_arm64_armv8-a_static_lto-thin").Module()
+ if !hasDep(libLto, libNeverLto) {
+ t.Errorf("'lto_enabled' missing dependency on NO-thin lto variant of 'lib_never_lto'")
+ }
+
+ libBar := result.ModuleForTests("bar", "android_arm64_armv8-a_shared").Module()
+ if !hasDep(libLto, libBar) {
+ t.Errorf("'lto_enabled' missing dependency on non-thin lto variant of 'bar'")
+ }
+
+ barVariants := result.ModuleVariantsForTests("bar")
+ for _, v := range barVariants {
+ if strings.Contains(v, "lto-thin") {
+ t.Errorf("Expected variants for 'bar' to not contain 'lto-thin', but found %q", v)
+ }
+ }
+ quxVariants := result.ModuleVariantsForTests("qux")
+ for _, v := range quxVariants {
+ if strings.Contains(v, "lto-thin") {
+ t.Errorf("Expected variants for 'qux' to not contain 'lto-thin', but found %q", v)
+ }
+ }
+}
+
+func TestThinLtoOnlyOnStaticDep(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "root",
+ srcs: ["src.c"],
+ static_libs: ["foo"],
+ }
+ cc_library_shared {
+ name: "root_no_lto",
+ srcs: ["src.c"],
+ static_libs: ["foo"],
+ lto: {
+ never: true,
+ }
+ }
+ cc_library_static {
+ name: "foo",
+ srcs: ["foo.c"],
+ static_libs: ["baz"],
+ lto: {
+ thin: true,
+ }
+ }
+ cc_library_static {
+ name: "baz",
+ srcs: ["baz.c"],
+ }
+`
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ ).RunTestWithBp(t, bp)
+
+ libRoot := result.ModuleForTests("root", "android_arm64_armv8-a_shared").Module()
+ libRootLtoNever := result.ModuleForTests("root_no_lto", "android_arm64_armv8-a_shared").Module()
+
+ hasDep := func(m android.Module, wantDep android.Module) bool {
+ var found bool
+ result.VisitDirectDeps(m, func(dep blueprint.Module) {
+ if dep == wantDep {
+ found = true
+ }
+ })
+ return found
+ }
+
+ libFoo := result.ModuleForTests("foo", "android_arm64_armv8-a_static")
+ if !hasDep(libRoot, libFoo.Module()) {
+ t.Errorf("'root' missing dependency on thin lto variant of 'foo'")
+ }
+
+ if !hasDep(libRootLtoNever, libFoo.Module()) {
+ t.Errorf("'root_no_lto' missing dependency on thin lto variant of 'foo'")
+ }
+
+ libFooCFlags := libFoo.Rule("cc").Args["cFlags"]
+ if w := "-flto=thin -fsplit-lto-unit"; !strings.Contains(libFooCFlags, w) {
+ t.Errorf("'foo' expected to have flags %q, but got %q", w, libFooCFlags)
+ }
+
+ libBaz := result.ModuleForTests("baz", "android_arm64_armv8-a_static_lto-thin")
+ if !hasDep(libFoo.Module(), libBaz.Module()) {
+ t.Errorf("'foo' missing dependency on thin lto variant of transitive dep 'baz'")
+ }
+
+ libBazCFlags := libFoo.Rule("cc").Args["cFlags"]
+ if w := "-flto=thin -fsplit-lto-unit"; !strings.Contains(libBazCFlags, w) {
+ t.Errorf("'baz' expected to have flags %q, but got %q", w, libFooCFlags)
+ }
+}
+
+func TestLtoDisabledButEnabledForArch(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ lto: {
+ never: true,
+ },
+ target: {
+ android_arm: {
+ lto: {
+ never: false,
+ thin: true,
+ },
+ },
+ },
+ }`
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ ).RunTestWithBp(t, bp)
+
+ libFooWithLto := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+ libFooWithoutLto := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld")
+
+ android.AssertStringDoesContain(t, "missing flag for LTO in variant that expects it",
+ libFooWithLto.Args["ldFlags"], "-flto=thin")
+ android.AssertStringDoesNotContain(t, "got flag for LTO in variant that doesn't expect it",
+ libFooWithoutLto.Args["ldFlags"], "-flto=thin")
+}
+
+func TestLtoDoesNotPropagateToRuntimeLibs(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library {
+ name: "runtime_libbar",
+ srcs: ["bar.c"],
+ }
+
+ cc_library {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ runtime_libs: ["runtime_libbar"],
+ lto: {
+ thin: true,
+ },
+ }`
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ ).RunTestWithBp(t, bp)
+
+ libFoo := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+ libBar := result.ModuleForTests("runtime_libbar", "android_arm_armv7-a-neon_shared").Rule("ld")
+
+ android.AssertStringDoesContain(t, "missing flag for LTO in LTO enabled library",
+ libFoo.Args["ldFlags"], "-flto=thin")
+ android.AssertStringDoesNotContain(t, "got flag for LTO in runtime_lib",
+ libBar.Args["ldFlags"], "-flto=thin")
+}
diff --git a/cc/makevars.go b/cc/makevars.go
index e4a9cf7..6c3f551 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")
)
@@ -53,7 +53,6 @@
func makeStringOfWarningAllowedProjects() string {
allProjects := append([]string{}, config.WarningAllowedProjects...)
- allProjects = append(allProjects, config.WarningAllowedOldProjects...)
sort.Strings(allProjects)
// Makefile rules use pattern "path/%" to match module paths.
if len(allProjects) > 0 {
@@ -94,6 +93,7 @@
ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ExternalCflags}")
ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
+ ctx.Strict("GLOBAL_CLANG_CFLAGS_64_NO_OVERRIDE", "${config.NoOverride64GlobalCflags}")
ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
ctx.Strict("GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideExternalGlobalCflags}")
@@ -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))
diff --git a/cc/ndk_api_coverage_parser/OWNERS b/cc/ndk_api_coverage_parser/OWNERS
deleted file mode 100644
index a90c48c..0000000
--- a/cc/ndk_api_coverage_parser/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-sophiez@google.com
diff --git a/cc/ndk_api_coverage_parser/__init__.py b/cc/ndk_api_coverage_parser/__init__.py
index 8b9cd66..752f7d4 100755
--- a/cc/ndk_api_coverage_parser/__init__.py
+++ b/cc/ndk_api_coverage_parser/__init__.py
@@ -23,6 +23,7 @@
from xml.etree.ElementTree import Element, SubElement, tostring
from symbolfile import (
ALL_ARCHITECTURES,
+ Filter,
FUTURE_API_LEVEL,
MultiplyDefinedSymbolError,
SymbolFileParser,
@@ -139,9 +140,8 @@
with open(args.symbol_file) as symbol_file:
try:
- versions = SymbolFileParser(
- symbol_file, api_map, "", FUTURE_API_LEVEL, True, True
- ).parse()
+ filt = Filter("", FUTURE_API_LEVEL, True, True, True)
+ versions = SymbolFileParser(symbol_file, api_map, filt).parse()
except MultiplyDefinedSymbolError as ex:
sys.exit('{}: error: {}'.format(args.symbol_file, ex))
diff --git a/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
index 141059c..7c6ef68 100644
--- a/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
+++ b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
@@ -20,7 +20,7 @@
import unittest
from xml.etree.ElementTree import fromstring
-from symbolfile import FUTURE_API_LEVEL, SymbolFileParser
+from symbolfile import Filter, FUTURE_API_LEVEL, SymbolFileParser
import ndk_api_coverage_parser as nparser
@@ -78,9 +78,8 @@
"""
)
)
- parser = SymbolFileParser(
- input_file, {}, "", FUTURE_API_LEVEL, True, True
- )
+ filt = Filter("", FUTURE_API_LEVEL, True, True, True)
+ parser = SymbolFileParser(input_file, {}, filt)
generator = nparser.XmlGenerator(io.StringIO())
result = generator.convertToXml(parser.parse())
expected = fromstring(
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index ef38a06..7354be9 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -19,8 +19,10 @@
"path/filepath"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
)
var (
@@ -79,6 +81,7 @@
type headerModule struct {
android.ModuleBase
+ android.BazelModuleBase
properties headerProperties
@@ -144,6 +147,39 @@
}
}
+// TODO(b/243196151): Populate `system` and `arch` metadata
+type bazelCcApiHeadersAttributes struct {
+ Hdrs bazel.LabelListAttribute
+ Include_dir *string
+}
+
+func createCcApiHeadersTarget(ctx android.TopDownMutatorContext, includes []string, excludes []string, include_dir *string) {
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_api_headers",
+ Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+ }
+ attrs := &bazelCcApiHeadersAttributes{
+ Hdrs: bazel.MakeLabelListAttribute(
+ android.BazelLabelForModuleSrcExcludes(
+ ctx,
+ includes,
+ excludes,
+ ),
+ ),
+ Include_dir: include_dir,
+ }
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: android.ApiContributionTargetName(ctx.ModuleName()),
+ }, attrs)
+}
+
+var _ android.ApiProvider = (*headerModule)(nil)
+
+func (h *headerModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+ // Generate `cc_api_headers` target for Multi-tree API export
+ createCcApiHeadersTarget(ctx, h.properties.Srcs, h.properties.Exclude_srcs, h.properties.From)
+}
+
// ndk_headers installs the sets of ndk headers defined in the srcs property
// to the sysroot base + "usr/include" + to directory + directory component.
// ndk_headers requires the license file to be specified. Example:
@@ -190,6 +226,7 @@
// Note that this is really only built to handle bionic/libc/include.
type versionedHeaderModule struct {
android.ModuleBase
+ android.BazelModuleBase
properties versionedHeaderProperties
@@ -197,6 +234,11 @@
licensePath android.Path
}
+// Return the glob pattern to find all .h files beneath `dir`
+func headerGlobPattern(dir string) string {
+ return filepath.Join(dir, "**", "*.h")
+}
+
func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if String(m.properties.License) == "" {
ctx.PropertyErrorf("license", "field is required")
@@ -206,7 +248,7 @@
fromSrcPath := android.PathForModuleSrc(ctx, String(m.properties.From))
toOutputPath := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
- srcFiles := ctx.GlobFiles(filepath.Join(fromSrcPath.String(), "**/*.h"), nil)
+ srcFiles := ctx.GlobFiles(headerGlobPattern(fromSrcPath.String()), nil)
var installPaths []android.WritablePath
for _, header := range srcFiles {
installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To))
@@ -222,6 +264,15 @@
processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
}
+var _ android.ApiProvider = (*versionedHeaderModule)(nil)
+
+func (h *versionedHeaderModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+ // Glob all .h files under `From`
+ includePattern := headerGlobPattern(proptools.String(h.properties.From))
+ // Generate `cc_api_headers` target for Multi-tree API export
+ createCcApiHeadersTarget(ctx, []string{includePattern}, []string{}, h.properties.From)
+}
+
func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path,
srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
// The versioner depends on a dependencies directory to simplify determining include paths
@@ -275,12 +326,14 @@
return module
}
-// preprocessed_ndk_header {
-// name: "foo",
-// preprocessor: "foo.sh",
-// srcs: [...],
-// to: "android",
-// }
+// preprocessed_ndk_header {
+//
+// name: "foo",
+// preprocessor: "foo.sh",
+// srcs: [...],
+// to: "android",
+//
+// }
//
// Will invoke the preprocessor as:
//
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 2410540..f8a3559 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -25,6 +25,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/cc/config"
)
@@ -84,15 +85,17 @@
//
// Example:
//
-// ndk_library {
-// name: "libfoo",
-// symbol_file: "libfoo.map.txt",
-// first_version: "9",
-// }
+// ndk_library {
+//
+// name: "libfoo",
+// symbol_file: "libfoo.map.txt",
+// first_version: "9",
+//
+// }
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.
@@ -109,6 +112,9 @@
// where it is enabled pending a fix for http://b/190554910 (no debug info
// for asm implemented symbols).
Allow_untyped_symbols *bool
+
+ // Headers presented by this library to the Public API Surface
+ Export_header_libs []string
}
type stubDecorator struct {
@@ -283,6 +289,10 @@
}
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, nil)
}
@@ -297,6 +307,7 @@
impl, ok := dep.(*Module)
if !ok {
ctx.ModuleErrorf("Implementation for stub is not correct module type")
+ return nil
}
output := impl.UnstrippedOutputFile()
if output == nil {
@@ -331,7 +342,14 @@
if android.InList("hwaddress", config.SanitizeDevice()) {
return false
}
- return true
+ // http://b/156513478
+ // http://b/277624006
+ // This step is expensive. We're not able to do anything with the outputs of
+ // this step yet (canDiffAbi is flagged off because libabigail isn't able to
+ // handle all our libraries), disable it. There's no sense in protecting
+ // against checking in code that breaks abidw since by the time any of this
+ // can be turned on we'll need to migrate to STG anyway.
+ return false
}
// Feature flag to disable diffing against prebuilts.
@@ -385,14 +403,14 @@
}
func (this *stubDecorator) diffAbi(ctx ModuleContext) {
- missingPrebuiltError := fmt.Sprintf(
- "Did not find prebuilt ABI dump for %q. Generate with "+
- "//development/tools/ndk/update_ndk_abi.sh.", this.libraryName(ctx))
-
// Catch any ABI changes compared to the checked-in definition of this API
// level.
abiDiffPath := android.PathForModuleOut(ctx, "abidiff.timestamp")
prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel)
+ missingPrebuiltError := fmt.Sprintf(
+ "Did not find prebuilt ABI dump for %q (%q). Generate with "+
+ "//development/tools/ndk/update_ndk_abi.sh.", this.libraryName(ctx),
+ prebuiltAbiDump.InvalidReason())
if !prebuiltAbiDump.Valid() {
ctx.Build(pctx, android.BuildParams{
Rule: android.ErrorRule,
@@ -479,8 +497,11 @@
return objs
}
+// Add a dependency on the header modules of this ndk_library
func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
- return Deps{}
+ return Deps{
+ HeaderLibs: linker.properties.Export_header_libs,
+ }
}
func (linker *stubDecorator) Name(name string) string {
@@ -514,17 +535,20 @@
return false
}
-func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
- arch := ctx.Target().Arch.ArchType.Name
- // arm64 isn't actually a multilib toolchain, so unlike the other LP64
- // architectures it's just installed to lib.
- libDir := "lib"
- if ctx.toolchain().Is64Bit() && arch != "arm64" {
- libDir = "lib64"
- }
+// Returns the install path for unversioned NDK libraries (currently only static
+// libraries).
+func getUnversionedLibraryInstallPath(ctx ModuleContext) android.InstallPath {
+ return getNdkSysrootBase(ctx).Join(ctx, "usr/lib", config.NDKTriple(ctx.toolchain()))
+}
- installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
- "platforms/android-%s/arch-%s/usr/%s", stub.apiLevel, arch, libDir))
+// Returns the install path for versioned NDK libraries. These are most often
+// stubs, but the same paths are used for CRT objects.
+func getVersionedLibraryInstallPath(ctx ModuleContext, apiLevel android.ApiLevel) android.InstallPath {
+ return getUnversionedLibraryInstallPath(ctx).Join(ctx, apiLevel.String())
+}
+
+func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
+ installDir := getVersionedLibraryInstallPath(ctx, stub.apiLevel)
stub.installPath = ctx.InstallFile(installDir, path.Base(), path)
}
@@ -556,5 +580,43 @@
func NdkLibraryFactory() android.Module {
module := newStubLibrary()
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
+ android.InitBazelModule(module)
return module
}
+
+type bazelCcApiContributionAttributes struct {
+ Api bazel.LabelAttribute
+ Api_surfaces bazel.StringListAttribute
+ Hdrs bazel.LabelListAttribute
+ Library_name string
+}
+
+// Names of the cc_api_header targets in the bp2build workspace
+func apiHeaderLabels(ctx android.TopDownMutatorContext, hdrLibs []string) bazel.LabelList {
+ addSuffix := func(ctx android.BazelConversionPathContext, module blueprint.Module) string {
+ label := android.BazelModuleLabel(ctx, module)
+ return android.ApiContributionTargetName(label)
+ }
+ return android.BazelLabelForModuleDepsWithFn(ctx, hdrLibs, addSuffix)
+}
+
+func ndkLibraryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_api_contribution",
+ Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+ }
+ stubLibrary := m.compiler.(*stubDecorator)
+ attrs := &bazelCcApiContributionAttributes{
+ Library_name: stubLibrary.implementationModuleName(m.Name()),
+ Api_surfaces: bazel.MakeStringListAttribute(
+ []string{android.PublicApi.String()}),
+ }
+ if symbolFile := stubLibrary.properties.Symbol_file; symbolFile != nil {
+ apiLabel := android.BazelLabelForModuleSrcSingle(ctx, proptools.String(symbolFile)).Label
+ attrs.Api = *bazel.MakeLabelAttribute(apiLabel)
+ }
+ apiHeaders := apiHeaderLabels(ctx, stubLibrary.properties.Export_header_libs)
+ attrs.Hdrs = bazel.MakeLabelListAttribute(apiHeaders)
+ apiContributionTargetName := android.ApiContributionTargetName(ctx.ModuleName())
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: apiContributionTargetName}, attrs)
+}
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 51ec6b8..1d15cf8 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -15,15 +15,12 @@
package cc
import (
- "fmt"
"strings"
"android/soong/android"
- "android/soong/cc/config"
)
func init() {
- android.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
android.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
android.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
}
@@ -34,68 +31,6 @@
// either (with the exception of the shared STLs, which are installed to the app's directory rather
// than to the system image).
-func getNdkLibDir(ctx android.ModuleContext, toolchain config.Toolchain, version string) android.SourcePath {
- suffix := ""
- // Most 64-bit NDK prebuilts store libraries in "lib64", except for arm64 which is not a
- // multilib toolchain and stores the libraries in "lib".
- if toolchain.Is64Bit() && ctx.Arch().ArchType != android.Arm64 {
- suffix = "64"
- }
- return android.PathForSource(ctx, fmt.Sprintf("prebuilts/ndk/current/platforms/android-%s/arch-%s/usr/lib%s",
- version, toolchain.Name(), suffix))
-}
-
-func ndkPrebuiltModuleToPath(ctx android.ModuleContext, toolchain config.Toolchain,
- ext string, version string) android.Path {
-
- // NDK prebuilts are named like: ndk_NAME.EXT.SDK_VERSION.
- // We want to translate to just NAME.EXT
- name := strings.Split(strings.TrimPrefix(ctx.ModuleName(), "ndk_"), ".")[0]
- dir := getNdkLibDir(ctx, toolchain, version)
- return dir.Join(ctx, name+ext)
-}
-
-type ndkPrebuiltObjectLinker struct {
- objectLinker
-}
-
-func (*ndkPrebuiltObjectLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
- // NDK objects can't have any dependencies
- return deps
-}
-
-// ndk_prebuilt_object exports a precompiled ndk object file for linking
-// operations. Soong's module name format is ndk_<NAME>.o.<sdk_version> where
-// the object is located under
-// ./prebuilts/ndk/current/platforms/android-<sdk_version>/arch-$(HOST_ARCH)/usr/lib/<NAME>.o.
-func NdkPrebuiltObjectFactory() android.Module {
- module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
- module.linker = &ndkPrebuiltObjectLinker{
- objectLinker: objectLinker{
- baseLinker: NewBaseLinker(nil),
- },
- }
- module.Properties.AlwaysSdk = true
- module.Properties.Sdk_version = StringPtr("current")
- module.Properties.HideFromMake = true
- return module.Init()
-}
-
-func (c *ndkPrebuiltObjectLinker) link(ctx ModuleContext, flags Flags,
- deps PathDeps, objs Objects) android.Path {
- // A null build step, but it sets up the output path.
- if !strings.HasPrefix(ctx.ModuleName(), "ndk_crt") {
- ctx.ModuleErrorf("NDK prebuilt objects must have an ndk_crt prefixed name")
- }
-
- return ndkPrebuiltModuleToPath(ctx, flags.Toolchain, objectExtension, ctx.sdkVersion())
-}
-
-func (*ndkPrebuiltObjectLinker) availableFor(what string) bool {
- // ndk prebuilt objects are available to everywhere
- return true
-}
-
type ndkPrebuiltStlLinker struct {
*libraryDecorator
}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 6c200f5..dffc6c6 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -57,15 +57,18 @@
)
func init() {
- android.RegisterModuleType("ndk_headers", ndkHeadersFactory)
- android.RegisterModuleType("ndk_library", NdkLibraryFactory)
- android.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
- android.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
- android.RegisterSingletonType("ndk", NdkSingleton)
-
+ RegisterNdkModuleTypes(android.InitRegistrationContext)
pctx.Import("android/soong/android")
}
+func RegisterNdkModuleTypes(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("ndk_headers", ndkHeadersFactory)
+ ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
+ ctx.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
+ ctx.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
+ ctx.RegisterSingletonType("ndk", NdkSingleton)
+}
+
func getNdkInstallBase(ctx android.PathContext) android.InstallPath {
return android.PathForNdkInstall(ctx)
}
@@ -139,6 +142,13 @@
staticLibInstallPaths, library.ndkSysrootPath)
}
}
+
+ if object, ok := m.linker.(*objectLinker); ok {
+ if object.ndkSysrootPath != nil {
+ staticLibInstallPaths = append(
+ staticLibInstallPaths, object.ndkSysrootPath)
+ }
+ }
}
})
diff --git a/cc/ndk_test.go b/cc/ndk_test.go
new file mode 100644
index 0000000..f20d3c6
--- /dev/null
+++ b/cc/ndk_test.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 cc
+
+import (
+ "testing"
+
+ "github.com/google/blueprint"
+
+ "android/soong/android"
+)
+
+func TestNdkHeaderDependency(t *testing.T) {
+ isDep := func(ctx *android.TestResult, from, toExpected android.Module) bool {
+ foundDep := false
+ ctx.VisitDirectDeps(from, func(toActual blueprint.Module) {
+ if toExpected.Name() == toActual.Name() {
+ foundDep = true
+ }
+ })
+ return foundDep
+ }
+ bp := `
+ ndk_library {
+ name: "libfoo",
+ first_version: "29",
+ symbol_file: "libfoo.map.txt",
+ export_header_libs: ["libfoo_headers"],
+ }
+ ndk_headers {
+ name: "libfoo_headers",
+ srcs: ["foo.h"],
+ license: "NOTICE",
+ }
+ //This module is needed since Soong creates a dep edge on source
+ cc_library {
+ name: "libfoo",
+ }
+ `
+ ctx := prepareForCcTest.RunTestWithBp(t, bp)
+ libfoo := ctx.ModuleForTests("libfoo.ndk", "android_arm64_armv8-a_sdk_shared")
+ libfoo_headers := ctx.ModuleForTests("libfoo_headers", "")
+ android.AssertBoolEquals(t, "Could not find headers of ndk_library", true, isDep(ctx, libfoo.Module(), libfoo_headers.Module()))
+}
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 5e6b8f5..efad70a 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -29,15 +29,12 @@
class Generator:
"""Output generator that writes stub source files and version scripts."""
def __init__(self, src_file: TextIO, version_script: TextIO,
- symbol_list: TextIO, arch: Arch, api: int, llndk: bool,
- apex: bool) -> None:
+ symbol_list: TextIO, filt: symbolfile.Filter) -> None:
self.src_file = src_file
self.version_script = version_script
self.symbol_list = symbol_list
- self.arch = arch
- self.api = api
- self.llndk = llndk
- self.apex = apex
+ self.filter = filt
+ self.api = filt.api
def write(self, versions: Iterable[Version]) -> None:
"""Writes all symbol data to the output files."""
@@ -47,8 +44,7 @@
def write_version(self, version: Version) -> None:
"""Writes a single version block's data to the output files."""
- if symbolfile.should_omit_version(version, self.arch, self.api,
- self.llndk, self.apex):
+ if self.filter.should_omit_version(version):
return
section_versioned = symbolfile.symbol_versioned_in_api(
@@ -56,8 +52,7 @@
version_empty = True
pruned_symbols = []
for symbol in version.symbols:
- if symbolfile.should_omit_symbol(symbol, self.arch, self.api,
- self.llndk, self.apex):
+ if self.filter.should_omit_symbol(symbol):
continue
if symbolfile.symbol_versioned_in_api(symbol.tags, self.api):
@@ -110,12 +105,17 @@
parser.add_argument(
'--apex',
action='store_true',
- help='Use the APEX variant. Note: equivalent to --system-api.')
+ help='Use the APEX variant.')
parser.add_argument(
- '--system-api',
+ '--systemapi',
action='store_true',
- dest='apex',
- help='Use the SystemAPI variant. Note: equivalent to --apex.')
+ dest='systemapi',
+ help='Use the SystemAPI variant.')
+ parser.add_argument(
+ '--no-ndk',
+ action='store_false',
+ dest='ndk',
+ help='Do not include NDK APIs.')
parser.add_argument('--api-map',
type=resolved_path,
@@ -152,11 +152,10 @@
verbosity = 2
logging.basicConfig(level=verbose_map[verbosity])
+ filt = symbolfile.Filter(args.arch, api, args.llndk, args.apex, args.systemapi, args.ndk)
with args.symbol_file.open() as symbol_file:
try:
- versions = symbolfile.SymbolFileParser(symbol_file, api_map,
- args.arch, api, args.llndk,
- args.apex).parse()
+ versions = symbolfile.SymbolFileParser(symbol_file, api_map, filt).parse()
except symbolfile.MultiplyDefinedSymbolError as ex:
sys.exit(f'{args.symbol_file}: error: {ex}')
@@ -164,7 +163,7 @@
with args.version_script.open('w') as version_script:
with args.symbol_list.open('w') as symbol_list:
generator = Generator(src_file, version_script, symbol_list,
- args.arch, api, args.llndk, args.apex)
+ filt)
generator.write(versions)
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index c8cd056..1e0bdf3 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -18,6 +18,7 @@
import io
import textwrap
import unittest
+from copy import copy
import symbolfile
from symbolfile import Arch, Tags
@@ -29,6 +30,9 @@
class GeneratorTest(unittest.TestCase):
+ def setUp(self) -> None:
+ self.filter = symbolfile.Filter(Arch('arm'), 9, False, False)
+
def test_omit_version(self) -> None:
# Thorough testing of the cases involved here is handled by
# OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
@@ -37,7 +41,7 @@
symbol_list_file = io.StringIO()
generator = ndkstubgen.Generator(src_file,
version_file, symbol_list_file,
- Arch('arm'), 9, False, False)
+ self.filter)
version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [
symbolfile.Symbol('foo', Tags()),
@@ -70,7 +74,7 @@
symbol_list_file = io.StringIO()
generator = ndkstubgen.Generator(src_file,
version_file, symbol_list_file,
- Arch('arm'), 9, False, False)
+ self.filter)
version = symbolfile.Version('VERSION_1', None, Tags(), [
symbolfile.Symbol('foo', Tags.from_strs(['x86'])),
@@ -106,7 +110,7 @@
symbol_list_file = io.StringIO()
generator = ndkstubgen.Generator(src_file,
version_file, symbol_list_file,
- Arch('arm'), 9, False, False)
+ self.filter)
versions = [
symbolfile.Version('VERSION_1', None, Tags(), [
@@ -162,6 +166,9 @@
class IntegrationTest(unittest.TestCase):
+ def setUp(self) -> None:
+ self.filter = symbolfile.Filter(Arch('arm'), 9, False, False)
+
def test_integration(self) -> None:
api_map = {
'O': 9000,
@@ -199,8 +206,7 @@
wobble;
} VERSION_4;
"""))
- parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
- 9, False, False)
+ parser = symbolfile.SymbolFileParser(input_file, api_map, self.filter)
versions = parser.parse()
src_file = io.StringIO()
@@ -208,7 +214,7 @@
symbol_list_file = io.StringIO()
generator = ndkstubgen.Generator(src_file,
version_file, symbol_list_file,
- Arch('arm'), 9, False, False)
+ self.filter)
generator.write(versions)
expected_src = textwrap.dedent("""\
@@ -263,16 +269,18 @@
*;
};
"""))
- parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
- 9001, False, False)
+ f = copy(self.filter)
+ f.api = 9001
+ parser = symbolfile.SymbolFileParser(input_file, api_map, f)
versions = parser.parse()
src_file = io.StringIO()
version_file = io.StringIO()
symbol_list_file = io.StringIO()
+ f = copy(self.filter)
+ f.api = 9001
generator = ndkstubgen.Generator(src_file,
- version_file, symbol_list_file,
- Arch('arm'), 9001, False, False)
+ version_file, symbol_list_file, f)
generator.write(versions)
expected_src = textwrap.dedent("""\
@@ -322,8 +330,9 @@
} VERSION_2;
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ f = copy(self.filter)
+ f.api = 16
+ parser = symbolfile.SymbolFileParser(input_file, {}, f)
with self.assertRaises(
symbolfile.MultiplyDefinedSymbolError) as ex_context:
@@ -370,16 +379,18 @@
wobble;
} VERSION_4;
"""))
- parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
- 9, False, True)
+ f = copy(self.filter)
+ f.apex = True
+ parser = symbolfile.SymbolFileParser(input_file, api_map, f)
versions = parser.parse()
src_file = io.StringIO()
version_file = io.StringIO()
symbol_list_file = io.StringIO()
+ f = copy(self.filter)
+ f.apex = True
generator = ndkstubgen.Generator(src_file,
- version_file, symbol_list_file,
- Arch('arm'), 9, False, True)
+ version_file, symbol_list_file, f)
generator.write(versions)
expected_src = textwrap.dedent("""\
@@ -413,6 +424,45 @@
""")
self.assertEqual(expected_version, version_file.getvalue())
+ def test_integration_with_nondk(self) -> None:
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo;
+ bar; # apex
+ local:
+ *;
+ };
+ """))
+ f = copy(self.filter)
+ f.apex = True
+ f.ndk = False # ndk symbols should be excluded
+ parser = symbolfile.SymbolFileParser(input_file, {}, f)
+ versions = parser.parse()
+
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ symbol_list_file = io.StringIO()
+ f = copy(self.filter)
+ f.apex = True
+ f.ndk = False # ndk symbols should be excluded
+ generator = ndkstubgen.Generator(src_file,
+ version_file, symbol_list_file, f)
+ generator.write(versions)
+
+ expected_src = textwrap.dedent("""\
+ void bar() {}
+ """)
+ self.assertEqual(expected_src, src_file.getvalue())
+
+ expected_version = textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ bar;
+ };
+ """)
+ self.assertEqual(expected_version, version_file.getvalue())
+
def test_empty_stub(self) -> None:
"""Tests that empty stubs can be generated.
@@ -428,20 +478,19 @@
*;
};
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
- 9, llndk=False, apex=True)
+ f = copy(self.filter)
+ f.apex = True
+ parser = symbolfile.SymbolFileParser(input_file, {}, f)
versions = parser.parse()
src_file = io.StringIO()
version_file = io.StringIO()
symbol_list_file = io.StringIO()
+ f = copy(self.filter)
+ f.apex = True
generator = ndkstubgen.Generator(src_file,
version_file,
- symbol_list_file,
- Arch('arm'),
- 9,
- llndk=False,
- apex=True)
+ symbol_list_file, f)
generator.write(versions)
self.assertEqual('', src_file.getvalue())
diff --git a/cc/object.go b/cc/object.go
index 3558efb..d65cdea 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -16,9 +16,11 @@
import (
"fmt"
+ "strings"
"android/soong/android"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
)
//
@@ -42,26 +44,37 @@
type objectLinker struct {
*baseLinker
Properties ObjectLinkerProperties
+
+ // Location of the object in the sysroot. Empty if the object is not
+ // included in the NDK.
+ ndkSysrootPath android.Path
}
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.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
+}
+
+func (handler *objectBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ objPaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(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 {
@@ -69,7 +82,7 @@
Static_libs []string `android:"arch_variant,variant_prepend"`
// list of shared library modules should only provide headers for this module.
- Shared_libs []string `android:"arch_variant"`
+ Shared_libs []string `android:"arch_variant,variant_prepend"`
// list of modules that should only provide headers for this module.
Header_libs []string `android:"arch_variant,variant_prepend"`
@@ -90,6 +103,10 @@
// Indicates that this module is a CRT object. CRT objects will be split
// into a variant per-API level between min_sdk_version and current.
Crt *bool
+
+ // Indicates that this module should not be included in the NDK sysroot.
+ // Only applies to CRT objects. Defaults to false.
+ Exclude_from_ndk_sysroot *bool
}
func newObject(hod android.HostOrDeviceSupported) *Module {
@@ -124,6 +141,7 @@
Srcs bazel.LabelListAttribute
Srcs_as bazel.LabelListAttribute
Hdrs bazel.LabelListAttribute
+ Objs bazel.LabelListAttribute
Deps bazel.LabelListAttribute
System_dynamic_deps bazel.LabelListAttribute
Copts bazel.StringListAttribute
@@ -132,6 +150,7 @@
Absolute_includes bazel.StringListAttribute
Stl *string
Linker_script bazel.LabelAttribute
+ Crt *bool
sdkAttributes
}
@@ -146,6 +165,7 @@
// Set arch-specific configurable attributes
baseAttributes := bp2BuildParseBaseProps(ctx, m)
compilerAttrs := baseAttributes.compilerAttributes
+ var objs bazel.LabelListAttribute
var deps bazel.LabelListAttribute
systemDynamicDeps := bazel.LabelListAttribute{ForceSpecifyEmptyList: true}
@@ -158,16 +178,21 @@
label := android.BazelLabelForModuleSrcSingle(ctx, *objectLinkerProps.Linker_script)
linkerScript.SetSelectValue(axis, config, label)
}
- deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs))
+ objs.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs))
systemSharedLibs := objectLinkerProps.System_shared_libs
if len(systemSharedLibs) > 0 {
systemSharedLibs = android.FirstUniqueStrings(systemSharedLibs)
}
systemDynamicDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs))
+ deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Static_libs))
+ deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Shared_libs))
+ deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Header_libs))
+ // static_libs, shared_libs, and header_libs have variant_prepend tag
+ deps.Prepend = true
}
}
}
- deps.ResolveExcludes()
+ objs.ResolveExcludes()
// Don't split cc_object srcs across languages. Doing so would add complexity,
// and this isn't typically done for cc_object.
@@ -183,6 +208,7 @@
attrs := &bazelObjectAttributes{
Srcs: srcs,
Srcs_as: compilerAttrs.asSrcs,
+ Objs: objs,
Deps: deps,
System_dynamic_deps: systemDynamicDeps,
Copts: compilerAttrs.copts,
@@ -191,6 +217,7 @@
Absolute_includes: compilerAttrs.absoluteIncludes,
Stl: compilerAttrs.stl,
Linker_script: linkerScript,
+ Crt: m.linker.(*objectLinker).Properties.Crt,
sdkAttributes: bp2BuildParseSdkAttributes(m),
}
@@ -199,7 +226,12 @@
Bzl_load_location: "//build/bazel/rules/cc:cc_object.bzl",
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+ tags := android.ApexAvailableTags(m)
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: m.Name(),
+ Tags: tags,
+ }, attrs)
}
func (object *objectLinker) appendLdflags(flags []string) {
@@ -244,24 +276,41 @@
objs = objs.Append(deps.Objs)
- var outputFile android.Path
+ var output android.WritablePath
builderFlags := flagsToBuilderFlags(flags)
+ outputName := ctx.ModuleName()
+ if !strings.HasSuffix(outputName, objectExtension) {
+ outputName += objectExtension
+ }
+
+ // isForPlatform is terribly named and actually means isNotApex.
+ if Bool(object.Properties.Crt) &&
+ !Bool(object.Properties.Exclude_from_ndk_sysroot) && ctx.useSdk() &&
+ ctx.isSdkVariant() && ctx.isForPlatform() {
+
+ output = getVersionedLibraryInstallPath(ctx,
+ nativeApiLevelOrPanic(ctx, ctx.sdkVersion())).Join(ctx, outputName)
+ object.ndkSysrootPath = output
+ } else {
+ output = android.PathForModuleOut(ctx, outputName)
+ }
+
+ outputFile := output
if len(objs.objFiles) == 1 && String(object.Properties.Linker_script) == "" {
- outputFile = objs.objFiles[0]
-
if String(object.Properties.Prefix_symbols) != "" {
- output := android.PathForModuleOut(ctx, ctx.ModuleName()+objectExtension)
- transformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), outputFile,
+ transformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), objs.objFiles[0],
builderFlags, output)
- outputFile = output
+ } else {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: objs.objFiles[0],
+ Output: output,
+ })
}
} else {
- output := android.PathForModuleOut(ctx, ctx.ModuleName()+objectExtension)
- outputFile = output
-
if String(object.Properties.Prefix_symbols) != "" {
- input := android.PathForModuleOut(ctx, "unprefixed", ctx.ModuleName()+objectExtension)
+ input := android.PathForModuleOut(ctx, "unprefixed", outputName)
transformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), input,
builderFlags, output)
output = input
diff --git a/cc/object_test.go b/cc/object_test.go
index 259a892..b1e2a0f 100644
--- a/cc/object_test.go
+++ b/cc/object_test.go
@@ -15,6 +15,7 @@
package cc
import (
+ "fmt"
"testing"
"android/soong/android"
@@ -64,7 +65,7 @@
variant := "android_arm64_armv8-a_sdk"
crt := ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
android.AssertStringDoesContain(t, "crt dep of sdk variant", crt,
- variant+"_29/crtbegin_dynamic.o")
+ "29/crtbegin_dynamic.o")
// platform variant uses the crt object built for platform
variant = "android_arm64_armv8-a"
@@ -107,3 +108,65 @@
expectedOutputFiles := []string{"outputbase/execroot/__main__/bazel_out.o"}
android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
}
+
+func TestCcObjectOutputFile(t *testing.T) {
+ testcases := []struct {
+ name string
+ moduleName string
+ bp string
+ }{
+ {
+ name: "normal",
+ moduleName: "foo",
+ bp: `
+ srcs: ["bar.c"],
+ `,
+ },
+ {
+ name: "suffix",
+ moduleName: "foo.o",
+ bp: `
+ srcs: ["bar.c"],
+ `,
+ },
+ {
+ name: "keep symbols",
+ moduleName: "foo",
+ bp: `
+ srcs: ["bar.c"],
+ prefix_symbols: "foo_",
+ `,
+ },
+ {
+ name: "partial linking",
+ moduleName: "foo",
+ bp: `
+ srcs: ["bar.c", "baz.c"],
+ `,
+ },
+ {
+ name: "partial linking and prefix symbols",
+ moduleName: "foo",
+ bp: `
+ srcs: ["bar.c", "baz.c"],
+ prefix_symbols: "foo_",
+ `,
+ },
+ }
+
+ for _, testcase := range testcases {
+ bp := fmt.Sprintf(`
+ cc_object {
+ name: "%s",
+ %s
+ }
+ `, testcase.moduleName, testcase.bp)
+ t.Run(testcase.name, func(t *testing.T) {
+ ctx := PrepareForIntegrationTestWithCc.RunTestWithBp(t, bp)
+ android.AssertPathRelativeToTopEquals(t, "expected output file foo.o",
+ fmt.Sprintf("out/soong/.intermediates/%s/android_arm64_armv8-a/foo.o", testcase.moduleName),
+ ctx.ModuleForTests(testcase.moduleName, "android_arm64_armv8-a").Output("foo.o").Output)
+ })
+ }
+
+}
diff --git a/cc/pgo.go b/cc/pgo.go
index 0632c15..463e2e6 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -41,7 +41,6 @@
const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
const profileUseInstrumentFormat = "-fprofile-use=%s"
-const profileUseSamplingFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s"
func getPgoProfileProjects(config android.DeviceConfig) []string {
return config.OnceStringSlice(pgoProfileProjectsConfigKey, func() []string {
@@ -56,12 +55,11 @@
type PgoProperties struct {
Pgo struct {
Instrumentation *bool
- Sampling *bool `android:"arch_variant"`
Profile_file *string `android:"arch_variant"`
Benchmarks []string
Enable_profile_use *bool `android:"arch_variant"`
// Additional compiler flags to use when building this module
- // for profiling (either instrumentation or sampling).
+ // for profiling.
Cflags []string `android:"arch_variant"`
} `android:"arch_variant"`
@@ -79,10 +77,6 @@
return props.Pgo.Instrumentation != nil && *props.Pgo.Instrumentation == true
}
-func (props *PgoProperties) isSampling() bool {
- return props.Pgo.Sampling != nil && *props.Pgo.Sampling == true
-}
-
func (pgo *pgo) props() []interface{} {
return []interface{}{&pgo.Properties}
}
@@ -135,18 +129,8 @@
return android.OptionalPathForPath(nil)
}
-func (props *PgoProperties) profileUseFlag(ctx ModuleContext, file string) string {
- if props.isInstrumentation() {
- return fmt.Sprintf(profileUseInstrumentFormat, file)
- }
- if props.isSampling() {
- return fmt.Sprintf(profileUseSamplingFormat, file)
- }
- return ""
-}
-
func (props *PgoProperties) profileUseFlags(ctx ModuleContext, file string) []string {
- flags := []string{props.profileUseFlag(ctx, file)}
+ flags := []string{fmt.Sprintf(profileUseInstrumentFormat, file)}
flags = append(flags, profileUseOtherFlags...)
return flags
}
@@ -169,19 +153,14 @@
// if profileFile gets updated
flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath)
flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath)
-
- if props.isSampling() {
- flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true")
- }
}
return flags
}
func (props *PgoProperties) isPGO(ctx BaseModuleContext) bool {
isInstrumentation := props.isInstrumentation()
- isSampling := props.isSampling()
- profileKindPresent := isInstrumentation || isSampling
+ profileKindPresent := isInstrumentation
filePresent := props.Pgo.Profile_file != nil
benchmarksPresent := len(props.Pgo.Benchmarks) > 0
@@ -194,7 +173,7 @@
if !profileKindPresent || !filePresent {
var missing []string
if !profileKindPresent {
- missing = append(missing, "profile kind (either \"instrumentation\" or \"sampling\" property)")
+ missing = append(missing, "profile kind")
}
if !filePresent {
missing = append(missing, "profile_file property")
@@ -208,14 +187,6 @@
ctx.ModuleErrorf("Instrumentation PGO specification is missing benchmark property")
}
- if isSampling {
- ctx.ModuleErrorf("Sampling PGO is deprecated, use AFDO instead")
- }
-
- if isSampling && isInstrumentation {
- ctx.PropertyErrorf("pgo", "Exactly one of \"instrumentation\" and \"sampling\" properties must be set")
- }
-
return true
}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index e0c28ce..0b5841e 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,10 +16,10 @@
import (
"path/filepath"
- "strings"
"android/soong/android"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
)
func init() {
@@ -31,8 +31,8 @@
ctx.RegisterModuleType("cc_prebuilt_library_shared", PrebuiltSharedLibraryFactory)
ctx.RegisterModuleType("cc_prebuilt_library_static", PrebuiltStaticLibraryFactory)
ctx.RegisterModuleType("cc_prebuilt_test_library_shared", PrebuiltSharedTestLibraryFactory)
- ctx.RegisterModuleType("cc_prebuilt_object", prebuiltObjectFactory)
- ctx.RegisterModuleType("cc_prebuilt_binary", prebuiltBinaryFactory)
+ ctx.RegisterModuleType("cc_prebuilt_object", PrebuiltObjectFactory)
+ ctx.RegisterModuleType("cc_prebuilt_binary", PrebuiltBinaryFactory)
}
type prebuiltLinkerInterface interface {
@@ -50,10 +50,20 @@
// symbols, etc), default true.
Check_elf_files *bool
+ // if set, add an extra objcopy --prefix-symbols= step
+ Prefix_symbols *string
+
// Optionally provide an import library if this is a Windows PE DLL prebuilt.
// This is needed only if this library is linked by other modules in build time.
// Only makes sense for the Windows target.
Windows_import_lib *string `android:"path,arch_variant"`
+
+ // MixedBuildsDisabled is true if and only if building this prebuilt is explicitly disabled in mixed builds for either
+ // its static or shared version on the current build variant. This is to prevent Bazel targets for build variants with
+ // which either the static or shared version is incompatible from participating in mixed buiods. Please note that this
+ // is an override and does not fully determine whether Bazel or Soong will be used. For the full determination, see
+ // cc.ProcessBazelQueryResponse, cc.QueueBazelCall, and cc.MixedBuildsDisabled.
+ MixedBuildsDisabled bool `blueprint:"mutated"`
}
type prebuiltLinker struct {
@@ -122,6 +132,13 @@
in := android.PathForModuleSrc(ctx, srcs[0])
+ if String(p.prebuiltLinker.properties.Prefix_symbols) != "" {
+ prefixed := android.PathForModuleOut(ctx, "prefixed", srcs[0])
+ transformBinaryPrefixSymbols(ctx, String(p.prebuiltLinker.properties.Prefix_symbols),
+ in, flagsToBuilderFlags(flags), prefixed)
+ in = prefixed
+ }
+
if p.static() {
depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(in).Build()
ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
@@ -190,12 +207,13 @@
})
// 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.") {
+ // library as their source and must not be installed, but other prebuilts like
+ // libclang_rt.* libraries set `stubs` property 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.
+ isModuleSdkPrebuilts := android.HasAnyPrefix(ctx.ModuleDir(), []string{
+ "prebuilts/runtime/mainline/", "prebuilts/module_sdk/"})
+ if p.hasStubsVariants() && !p.buildStubs() && !ctx.Host() && isModuleSdkPrebuilts {
ctx.Module().MakeUninstallable()
}
@@ -243,6 +261,7 @@
func (p *prebuiltLibraryLinker) disablePrebuilt() {
p.properties.Srcs = nil
+ p.properties.MixedBuildsDisabled = true
}
// Implements versionedInterface
@@ -254,6 +273,7 @@
module, library := NewLibrary(hod)
module.compiler = nil
module.bazelable = true
+ module.bazelHandler = &prebuiltLibraryBazelHandler{module: module, library: library}
prebuilt := &prebuiltLibraryLinker{
libraryDecorator: library,
@@ -273,8 +293,6 @@
android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, srcsProperty)
}
- // Prebuilt libraries can be used in SDKs.
- android.InitSdkAwareModule(module)
return module, library
}
@@ -309,8 +327,6 @@
func NewPrebuiltSharedLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module, library := NewPrebuiltLibrary(hod, "srcs")
library.BuildOnlyShared()
- module.bazelable = true
- module.bazelHandler = &prebuiltSharedLibraryBazelHandler{module: module, library: library}
// Prebuilt shared libraries can be included in APEXes
android.InitApexModule(module)
@@ -328,8 +344,7 @@
func NewPrebuiltStaticLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module, library := NewPrebuiltLibrary(hod, "srcs")
library.BuildOnlyStatic()
- module.bazelable = true
- module.bazelHandler = &prebuiltStaticLibraryBazelHandler{module: module, library: library}
+
return module, library
}
@@ -337,16 +352,17 @@
Static_library bazel.LabelAttribute
Export_includes bazel.StringListAttribute
Export_system_includes bazel.StringListAttribute
+ Alwayslink bazel.BoolAttribute
}
// 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
+// - Only a cc_prebuilt_library_static if the shared.enabled property is set to false across all variants.
+// - Only a cc_prebuilt_library_shared if the static.enabled property is set to false across all variants
+// - Both a cc_prebuilt_library_static and cc_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".
+// In all cases, cc_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)
@@ -354,44 +370,58 @@
func prebuiltLibraryStaticBp2Build(ctx android.TopDownMutatorContext, module *Module, fullBuild bool) {
prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module, true)
- exportedIncludes := Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx, module)
+ exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, nil)
attrs := &bazelPrebuiltLibraryStaticAttributes{
Static_library: prebuiltAttrs.Src,
Export_includes: exportedIncludes.Includes,
Export_system_includes: exportedIncludes.SystemIncludes,
+ // TODO: ¿Alwayslink?
}
props := bazel.BazelTargetModuleProperties{
- Rule_class: "prebuilt_library_static",
- Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_static.bzl",
+ Rule_class: "cc_prebuilt_library_static",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_prebuilt_library_static.bzl",
}
name := android.RemoveOptionalPrebuiltPrefix(module.Name())
if fullBuild {
name += "_bp2build_cc_library_static"
}
- ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled)
+
+ tags := android.ApexAvailableTags(module)
+ ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled)
+
+ _true := true
+ alwayslinkAttrs := *attrs
+ alwayslinkAttrs.Alwayslink.SetValue(&_true)
+ ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name + "_alwayslink", Tags: tags}, &alwayslinkAttrs, prebuiltAttrs.Enabled)
}
type bazelPrebuiltLibrarySharedAttributes struct {
- Shared_library bazel.LabelAttribute
+ Shared_library bazel.LabelAttribute
+ Export_includes bazel.StringListAttribute
+ Export_system_includes bazel.StringListAttribute
}
func prebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext, module *Module) {
prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module, false)
+ exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, nil)
attrs := &bazelPrebuiltLibrarySharedAttributes{
- Shared_library: prebuiltAttrs.Src,
+ Shared_library: prebuiltAttrs.Src,
+ Export_includes: exportedIncludes.Includes,
+ Export_system_includes: exportedIncludes.SystemIncludes,
}
props := bazel.BazelTargetModuleProperties{
- Rule_class: "prebuilt_library_shared",
- Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_shared.bzl",
+ Rule_class: "cc_prebuilt_library_shared",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_prebuilt_library_shared.bzl",
}
name := android.RemoveOptionalPrebuiltPrefix(module.Name())
- ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled)
+ tags := android.ApexAvailableTags(module)
+ ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled)
}
type prebuiltObjectProperties struct {
@@ -405,22 +435,50 @@
properties prebuiltObjectProperties
}
-type prebuiltStaticLibraryBazelHandler struct {
- android.BazelHandler
-
+type prebuiltLibraryBazelHandler struct {
module *Module
library *libraryDecorator
}
-func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+var _ BazelHandler = (*prebuiltLibraryBazelHandler)(nil)
+
+func (h *prebuiltLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
+ if h.module.linker.(*prebuiltLibraryLinker).properties.MixedBuildsDisabled {
+ return
+ }
bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+ bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
+}
+
+func (h *prebuiltLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ if h.module.linker.(*prebuiltLibraryLinker).properties.MixedBuildsDisabled {
+ return
+ }
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil {
- ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+ ctx.ModuleErrorf(err.Error())
+ return
}
- if !ok {
- return false
+
+ if h.module.static() {
+ if ok := h.processStaticBazelQueryResponse(ctx, label, ccInfo); !ok {
+ return
+ }
+ } else if h.module.Shared() {
+ if ok := h.processSharedBazelQueryResponse(ctx, label, ccInfo); !ok {
+ return
+ }
+ } else {
+ return
}
+
+ h.module.maybeUnhideFromMake()
+
+ h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
+}
+
+func (h *prebuiltLibraryBazelHandler) processStaticBazelQueryResponse(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
staticLibs := ccInfo.CcStaticLibraryFiles
if len(staticLibs) > 1 {
ctx.ModuleErrorf("expected 1 static library from bazel target %q, got %s", label, staticLibs)
@@ -442,37 +500,26 @@
return true
}
- out := android.PathForBazelOut(ctx, staticLibs[0])
- h.module.outputFile = android.OptionalPathForPath(out)
+ var outputPath android.Path = android.PathForBazelOut(ctx, staticLibs[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ h.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputPath = android.AttachValidationActions(ctx, outputPath, h.module.tidyFiles)
+ }
- depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(out).Build()
+ h.module.outputFile = android.OptionalPathForPath(outputPath)
+
+ depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(outputPath).Build()
ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
- StaticLibrary: out,
-
+ StaticLibrary: outputPath,
TransitiveStaticLibrariesForOrdering: depSet,
})
return true
}
-type prebuiltSharedLibraryBazelHandler struct {
- android.BazelHandler
-
- module *Module
- library *libraryDecorator
-}
-
-func (h *prebuiltSharedLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
- bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, 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
- }
+func (h *prebuiltLibraryBazelHandler) processSharedBazelQueryResponse(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
sharedLibs := ccInfo.CcSharedLibraryFiles
- if len(sharedLibs) != 1 {
+ if len(sharedLibs) > 1 {
ctx.ModuleErrorf("expected 1 shared library from bazel target %s, got %q", label, sharedLibs)
return false
}
@@ -482,31 +529,31 @@
// TODO(eakammer):Add stub-related flags if this library is a stub library.
// h.library.exportVersioningMacroIfNeeded(ctx)
- // Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise
- // validation will fail. For now, set this to an empty list.
- // TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
- h.library.collectedSnapshotHeaders = android.Paths{}
-
if len(sharedLibs) == 0 {
h.module.outputFile = android.OptionalPath{}
return true
}
- out := android.PathForBazelOut(ctx, sharedLibs[0])
- h.module.outputFile = android.OptionalPathForPath(out)
+ var outputPath android.Path = android.PathForBazelOut(ctx, sharedLibs[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ h.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputPath = android.AttachValidationActions(ctx, outputPath, h.module.tidyFiles)
+ }
+
+ h.module.outputFile = android.OptionalPathForPath(outputPath)
// FIXME(b/214600441): We don't yet strip prebuilt shared libraries
- h.library.unstrippedOutputFile = out
+ h.library.unstrippedOutputFile = outputPath
var toc android.Path
if len(ccInfo.TocFile) > 0 {
toc = android.PathForBazelOut(ctx, ccInfo.TocFile)
} else {
- toc = out // Just reuse `out` so ninja still gets an input but won't matter
+ toc = outputPath // Just reuse `out` so ninja still gets an input but won't matter
}
info := SharedLibraryInfo{
- SharedLibrary: out,
+ SharedLibrary: outputPath,
TableOfContents: android.OptionalPathForPath(toc),
Target: ctx.Target(),
}
@@ -514,7 +561,6 @@
h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo)
h.module.maybeUnhideFromMake()
-
return true
}
@@ -547,6 +593,8 @@
func NewPrebuiltObject(hod android.HostOrDeviceSupported) *Module {
module := newObject(hod)
+ module.bazelHandler = &prebuiltObjectBazelHandler{module: module}
+ module.bazelable = true
prebuilt := &prebuiltObjectLinker{
objectLinker: objectLinker{
baseLinker: NewBaseLinker(nil),
@@ -555,11 +603,58 @@
module.linker = prebuilt
module.AddProperties(&prebuilt.properties)
android.InitPrebuiltModule(module, &prebuilt.properties.Srcs)
- android.InitSdkAwareModule(module)
return module
}
-func prebuiltObjectFactory() android.Module {
+type prebuiltObjectBazelHandler struct {
+ module *Module
+}
+
+var _ BazelHandler = (*prebuiltObjectBazelHandler)(nil)
+
+func (h *prebuiltObjectBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
+}
+
+func (h *prebuiltObjectBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ outputs, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
+ }
+ if len(outputs) != 1 {
+ ctx.ModuleErrorf("Expected a single output for `%s`, but got:\n%v", label, outputs)
+ return
+ }
+ out := android.PathForBazelOut(ctx, outputs[0])
+ h.module.outputFile = android.OptionalPathForPath(out)
+ h.module.maybeUnhideFromMake()
+}
+
+type bazelPrebuiltObjectAttributes struct {
+ Src bazel.LabelAttribute
+}
+
+func prebuiltObjectBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+ prebuiltAttrs := bp2BuildParsePrebuiltObjectProps(ctx, module)
+
+ attrs := &bazelPrebuiltObjectAttributes{
+ Src: prebuiltAttrs.Src,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_prebuilt_object",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_prebuilt_object.bzl",
+ }
+
+ name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+ tags := android.ApexAvailableTags(module)
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name, Tags: tags}, attrs)
+}
+
+func PrebuiltObjectFactory() android.Module {
module := NewPrebuiltObject(android.HostAndDeviceSupported)
return module.Init()
}
@@ -646,15 +741,21 @@
}
// cc_prebuilt_binary installs a precompiled executable in srcs property in the
-// device's directory.
-func prebuiltBinaryFactory() android.Module {
+// device's directory, for both the host and device
+func PrebuiltBinaryFactory() android.Module {
module, _ := NewPrebuiltBinary(android.HostAndDeviceSupported)
return module.Init()
}
+type prebuiltBinaryBazelHandler struct {
+ module *Module
+ decorator *binaryDecorator
+}
+
func NewPrebuiltBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
- module, binary := newBinary(hod, false)
+ module, binary := newBinary(hod, true)
module.compiler = nil
+ module.bazelHandler = &prebuiltBinaryBazelHandler{module, binary}
prebuilt := &prebuiltBinaryLinker{
binaryDecorator: binary,
@@ -668,6 +769,54 @@
return module, binary
}
+var _ BazelHandler = (*prebuiltBinaryBazelHandler)(nil)
+
+func (h *prebuiltBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
+}
+
+func (h *prebuiltBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ outputs, err := bazelCtx.GetOutputFiles(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
+ }
+ if len(outputs) != 1 {
+ ctx.ModuleErrorf("Expected a single output for `%s`, but got:\n%v", label, outputs)
+ return
+ }
+ out := android.PathForBazelOut(ctx, outputs[0])
+ h.module.outputFile = android.OptionalPathForPath(out)
+ h.module.maybeUnhideFromMake()
+}
+
+type bazelPrebuiltBinaryAttributes struct {
+ Src bazel.LabelAttribute
+ Strip stripAttributes
+}
+
+func prebuiltBinaryBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+ prebuiltAttrs := bp2BuildParsePrebuiltBinaryProps(ctx, module)
+
+ var la linkerAttributes
+ la.convertStripProps(ctx, module)
+ attrs := &bazelPrebuiltBinaryAttributes{
+ Src: prebuiltAttrs.Src,
+ Strip: stripAttrsFromLinkerAttrs(&la),
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_prebuilt_binary",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_prebuilt_binary.bzl",
+ }
+
+ name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+ tags := android.ApexAvailableTags(module)
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name, Tags: tags}, attrs)
+}
+
type Sanitized struct {
None struct {
Srcs []string `android:"path,arch_variant"`
@@ -684,10 +833,10 @@
if sanitize == nil {
return nil
}
- if Bool(sanitize.Properties.Sanitize.Address) && sanitized.Address.Srcs != nil {
+ if sanitize.isSanitizerEnabled(Asan) && sanitized.Address.Srcs != nil {
return sanitized.Address.Srcs
}
- if Bool(sanitize.Properties.Sanitize.Hwaddress) && sanitized.Hwaddress.Srcs != nil {
+ if sanitize.isSanitizerEnabled(Hwasan) && sanitized.Hwaddress.Srcs != nil {
return sanitized.Hwaddress.Srcs
}
return sanitized.None.Srcs
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 901f458..0c79e55 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -169,6 +169,11 @@
if !hasDep(crtx, prebuiltCrtx) {
t.Errorf("crtx missing dependency on prebuilt_crtx")
}
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, prebuiltLiba)[0]
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_prebuilt_library_shared", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+ entries = android.AndroidMkEntriesForTest(t, ctx, prebuiltLibb)[0]
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_prebuilt_library_static", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
}
func TestPrebuiltLibraryShared(t *testing.T) {
@@ -381,6 +386,218 @@
assertString(t, static2.OutputFile().Path().Base(), "libf.hwasan.a")
}
+func TestPrebuiltLibraryWithBazel(t *testing.T) {
+ const bp = `
+cc_prebuilt_library {
+ name: "foo",
+ shared: {
+ srcs: ["foo.so"],
+ },
+ static: {
+ srcs: ["foo.a"],
+ },
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ outBaseDir := "outputbase"
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcSharedLibraryFiles: []string{"foo.so"},
+ },
+ "//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
+ CcStaticLibraryFiles: []string{"foo.a"},
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ sharedFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ pathPrefix := outBaseDir + "/execroot/__main__/"
+
+ sharedInfo := result.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ android.AssertPathRelativeToTopEquals(t,
+ "prebuilt library shared target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+ pathPrefix+"foo.so", sharedInfo.SharedLibrary)
+
+ outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{pathPrefix + "foo.so"}
+ android.AssertDeepEquals(t,
+ "prebuilt library shared target output files did not match expected.",
+ expectedOutputFiles, outputFiles.Strings())
+
+ staticFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+ staticInfo := result.ModuleProvider(staticFoo, StaticLibraryInfoProvider).(StaticLibraryInfo)
+ android.AssertPathRelativeToTopEquals(t,
+ "prebuilt library static target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+ pathPrefix+"foo.a", staticInfo.StaticLibrary)
+
+ staticOutputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object staticOutputFiles %s", err)
+ }
+ expectedStaticOutputFiles := []string{pathPrefix + "foo.a"}
+ android.AssertDeepEquals(t,
+ "prebuilt library static target output files did not match expected.",
+ expectedStaticOutputFiles, staticOutputFiles.Strings())
+}
+
+func TestPrebuiltLibraryWithBazelValidations(t *testing.T) {
+ const bp = `
+cc_prebuilt_library {
+ name: "foo",
+ shared: {
+ srcs: ["foo.so"],
+ },
+ static: {
+ srcs: ["foo.a"],
+ },
+ bazel_module: { label: "//foo/bar:bar" },
+ tidy: true,
+}`
+ outBaseDir := "outputbase"
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltTest,
+ android.FixtureMergeEnv(map[string]string{
+ "ALLOW_LOCAL_TIDY_TRUE": "1",
+ }),
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcSharedLibraryFiles: []string{"foo.so"},
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ "//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
+ CcStaticLibraryFiles: []string{"foo.a"},
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ sharedFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+
+ expectedOutputFile := "out/soong/.intermediates/foo/android_arm_armv7-a-neon_shared/validated/foo.so"
+ sharedInfo := result.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ android.AssertPathRelativeToTopEquals(t,
+ "prebuilt library shared target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+ expectedOutputFile, sharedInfo.SharedLibrary)
+
+ outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{expectedOutputFile}
+ android.AssertPathsRelativeToTopEquals(t,
+ "prebuilt library shared target output files did not match expected.",
+ expectedOutputFiles, outputFiles)
+
+ staticFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+ staticInfo := result.ModuleProvider(staticFoo, StaticLibraryInfoProvider).(StaticLibraryInfo)
+ expectedStaticOutputFile := "out/soong/.intermediates/foo/android_arm_armv7-a-neon_static/validated/foo.a"
+ android.AssertPathRelativeToTopEquals(t,
+ "prebuilt library static target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+ expectedStaticOutputFile, staticInfo.StaticLibrary)
+
+ staticOutputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object staticOutputFiles %s", err)
+ }
+ expectedStaticOutputFiles := []string{expectedStaticOutputFile}
+ android.AssertPathsRelativeToTopEquals(t,
+ "prebuilt library static target output files did not match expected.",
+ expectedStaticOutputFiles, staticOutputFiles)
+}
+
+func TestPrebuiltLibraryWithBazelStaticDisabled(t *testing.T) {
+ const bp = `
+cc_prebuilt_library {
+ name: "foo",
+ shared: {
+ srcs: ["foo.so"],
+ },
+ static: {
+ enabled: false
+ },
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ outBaseDir := "outputbase"
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcSharedLibraryFiles: []string{"foo.so"},
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ sharedFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ pathPrefix := outBaseDir + "/execroot/__main__/"
+
+ sharedInfo := result.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ android.AssertPathRelativeToTopEquals(t,
+ "prebuilt library shared target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+ pathPrefix+"foo.so", sharedInfo.SharedLibrary)
+
+ outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{pathPrefix + "foo.so"}
+ android.AssertDeepEquals(t,
+ "prebuilt library shared target output files did not match expected.",
+ expectedOutputFiles, outputFiles.Strings())
+}
+
+func TestPrebuiltLibraryStaticWithBazel(t *testing.T) {
+ const bp = `
+cc_prebuilt_library_static {
+ name: "foo",
+ srcs: ["foo.so"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ outBaseDir := "outputbase"
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcStaticLibraryFiles: []string{"foo.so"},
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ staticFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+ pathPrefix := outBaseDir + "/execroot/__main__/"
+
+ info := result.ModuleProvider(staticFoo, StaticLibraryInfoProvider).(StaticLibraryInfo)
+ android.AssertPathRelativeToTopEquals(t,
+ "prebuilt library static path did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+ pathPrefix+"foo.so", info.StaticLibrary)
+
+ outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{pathPrefix + "foo.so"}
+ android.AssertDeepEquals(t, "prebuilt library static output files did not match expected.", expectedOutputFiles, outputFiles.Strings())
+}
+
func TestPrebuiltLibrarySharedWithBazelWithoutToc(t *testing.T) {
const bp = `
cc_prebuilt_library_shared {
@@ -453,11 +670,15 @@
}
func TestPrebuiltStubNoinstall(t *testing.T) {
- testFunc := func(t *testing.T, bp string) {
+ testFunc := func(t *testing.T, expectLibfooOnSystemLib bool, fs android.MockFS) {
result := android.GroupFixturePreparers(
prepareForPrebuiltTest,
android.PrepareForTestWithMakevars,
- ).RunTestWithBp(t, bp)
+ android.FixtureMergeMockFs(fs),
+ ).RunTest(t)
+
+ ldRule := result.ModuleForTests("installedlib", "android_arm64_armv8-a_shared").Rule("ld")
+ android.AssertStringDoesContain(t, "", ldRule.Args["libFlags"], "android_arm64_armv8-a_shared/libfoo.so")
installRules := result.InstallMakeRulesForTesting(t)
var installedlibRule *android.InstallMakeRule
@@ -474,49 +695,126 @@
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")
+ if expectLibfooOnSystemLib {
+ android.AssertStringListContains(t,
+ "installedlib doesn't have install dependency on libfoo impl",
+ installedlibRule.OrderOnlyDeps,
+ "out/target/product/test_device/system/lib/libfoo.so")
+ } else {
+ android.AssertStringListDoesNotContain(t,
+ "installedlib has install dependency on libfoo stub",
+ installedlibRule.Deps,
+ "out/target/product/test_device/system/lib/libfoo.so")
+ android.AssertStringListDoesNotContain(t,
+ "installedlib has order-only install dependency on libfoo stub",
+ installedlibRule.OrderOnlyDeps,
+ "out/target/product/test_device/system/lib/libfoo.so")
+ }
}
- const prebuiltStublibBp = `
+ prebuiltLibfooBp := []byte(`
cc_prebuilt_library {
- name: "stublib",
+ name: "libfoo",
prefer: true,
- srcs: ["foo.so"],
+ srcs: ["libfoo.so"],
stubs: {
versions: ["1"],
},
}
- `
+ `)
- const installedlibBp = `
+ installedlibBp := []byte(`
cc_library {
name: "installedlib",
- shared_libs: ["stublib"],
+ shared_libs: ["libfoo"],
}
- `
+ `)
- t.Run("prebuilt without source", func(t *testing.T) {
- testFunc(t, prebuiltStublibBp+installedlibBp)
+ t.Run("prebuilt stub (without source): no install", func(t *testing.T) {
+ testFunc(
+ t,
+ /*expectLibfooOnSystemLib=*/ false,
+ android.MockFS{
+ "prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp,
+ "Android.bp": installedlibBp,
+ },
+ )
})
- const disabledSourceStublibBp = `
+ disabledSourceLibfooBp := []byte(`
cc_library {
- name: "stublib",
+ name: "libfoo",
enabled: false,
stubs: {
versions: ["1"],
},
}
- `
+ `)
- t.Run("prebuilt with disabled source", func(t *testing.T) {
- testFunc(t, disabledSourceStublibBp+prebuiltStublibBp+installedlibBp)
+ t.Run("prebuilt stub (with disabled source): no install", func(t *testing.T) {
+ testFunc(
+ t,
+ /*expectLibfooOnSystemLib=*/ false,
+ android.MockFS{
+ "prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp,
+ "impl/Android.bp": disabledSourceLibfooBp,
+ "Android.bp": installedlibBp,
+ },
+ )
})
+
+ t.Run("prebuilt impl (with `stubs` property set): install", func(t *testing.T) {
+ testFunc(
+ t,
+ /*expectLibfooOnSystemLib=*/ true,
+ android.MockFS{
+ "impl/Android.bp": prebuiltLibfooBp,
+ "Android.bp": installedlibBp,
+ },
+ )
+ })
+}
+
+func TestPrebuiltBinaryNoSrcsNoError(t *testing.T) {
+ const bp = `
+cc_prebuilt_binary {
+ name: "bintest",
+ srcs: [],
+}`
+ ctx := testPrebuilt(t, bp, map[string][]byte{})
+ mod := ctx.ModuleForTests("bintest", "android_arm64_armv8-a").Module().(*Module)
+ android.AssertBoolEquals(t, `expected no srcs to yield no output file`, false, mod.OutputFile().Valid())
+}
+
+func TestPrebuiltBinaryMultipleSrcs(t *testing.T) {
+ const bp = `
+cc_prebuilt_binary {
+ name: "bintest",
+ srcs: ["foo", "bar"],
+}`
+ testCcError(t, `Android.bp:4:6: module "bintest" variant "android_arm64_armv8-a": srcs: multiple prebuilt source files`, bp)
+}
+
+func TestPrebuiltBinaryWithBazel(t *testing.T) {
+ const bp = `
+cc_prebuilt_binary {
+ name: "bintest",
+ srcs: ["bin"],
+ bazel_module: { label: "//bin/foo:foo" },
+}`
+ const outBaseDir = "outputbase"
+ const expectedOut = outBaseDir + "/execroot/__main__/bin"
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToOutputFiles: map[string][]string{"//bin/foo:foo": []string{"bin"}},
+ }
+ ctx := testCcWithConfig(t, config)
+ bin := ctx.ModuleForTests("bintest", "android_arm64_armv8-a").Module().(*Module)
+ out := bin.OutputFile()
+ if !out.Valid() {
+ t.Error("Invalid output file")
+ return
+ }
+ android.AssertStringEquals(t, "output file", expectedOut, out.String())
}
diff --git a/cc/proto.go b/cc/proto.go
index 8e6d5ed..97470e5 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -165,7 +165,8 @@
}
type protoAttributes struct {
- Deps bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Min_sdk_version *string
}
type bp2buildProtoDeps struct {
@@ -178,7 +179,7 @@
var ret bp2buildProtoDeps
protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs)
- if !ok {
+ if !ok || protoInfo.Proto_libs.IsEmpty() {
return ret
}
@@ -201,18 +202,18 @@
dep := android.BazelLabelForModuleDepSingle(ctx, depName)
ret.protoDep = &bazel.LabelAttribute{Value: &dep}
- protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
var protoAttrs protoAttributes
- protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}})
+ protoAttrs.Deps.SetValue(protoInfo.Proto_libs)
+ protoAttrs.Min_sdk_version = m.Properties.Min_sdk_version
name := m.Name() + suffix
-
+ tags := android.ApexAvailableTags(m)
ctx.CreateBazelTargetModule(
bazel.BazelTargetModuleProperties{
Rule_class: rule_class,
Bzl_load_location: "//build/bazel/rules/cc:cc_proto.bzl",
},
- android.CommonAttributes{Name: name},
+ android.CommonAttributes{Name: name, Tags: tags},
&protoAttrs)
var privateHdrs bool
diff --git a/cc/sabi.go b/cc/sabi.go
index e62ca66..4cd776a 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -26,6 +26,40 @@
lsdumpPathsLock sync.Mutex
)
+// Properties for ABI compatibility checker in Android.bp.
+type headerAbiCheckerProperties struct {
+ // Enable ABI checks (even if this is not an LLNDK/VNDK lib)
+ Enabled *bool
+
+ // Path to a symbol file that specifies the symbols to be included in the generated
+ // ABI dump file
+ Symbol_file *string `android:"path"`
+
+ // Symbol versions that should be ignored from the symbol file
+ Exclude_symbol_versions []string
+
+ // Symbol tags that should be ignored from the symbol file
+ Exclude_symbol_tags []string
+
+ // 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
+
+ // Opt-in reference dump directories
+ Ref_dump_dirs []string
+}
+
+func (props *headerAbiCheckerProperties) enabled() bool {
+ return Bool(props.Enabled)
+}
+
+func (props *headerAbiCheckerProperties) explicitlyDisabled() bool {
+ return !BoolDefault(props.Enabled, true)
+}
+
type SAbiProperties struct {
// Whether ABI dump should be created for this module.
// Set by `sabiDepsMutator` if this module is a shared library that needs ABI check, or a static
@@ -67,7 +101,8 @@
// Returns an empty string if ABI check is disabled for this library.
func classifySourceAbiDump(ctx android.BaseModuleContext) string {
m := ctx.Module().(*Module)
- if m.library.headerAbiCheckerExplicitlyDisabled() {
+ headerAbiChecker := m.library.getHeaderAbiCheckerProperties(ctx)
+ if headerAbiChecker.explicitlyDisabled() {
return ""
}
// Return NDK if the library is both NDK and LLNDK.
@@ -92,7 +127,16 @@
}
}
}
- if m.library.headerAbiCheckerEnabled() || m.library.hasStubsVariants() {
+ if m.library.hasStubsVariants() && !m.InProduct() && !m.InVendor() {
+ return "PLATFORM"
+ }
+ if headerAbiChecker.enabled() {
+ if m.InProduct() {
+ return "PRODUCT"
+ }
+ if m.InVendor() {
+ return "VENDOR"
+ }
return "PLATFORM"
}
return ""
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 814fef6..7fddc1b 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -62,21 +62,30 @@
"-fast-isel=false",
}
- cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
- "-fsanitize-ignorelist=external/compiler-rt/lib/cfi/cfi_blocklist.txt"}
+ cfiBlocklistPath = "external/compiler-rt/lib/cfi"
+ cfiBlocklistFilename = "cfi_blocklist.txt"
+ cfiCrossDsoFlag = "-fsanitize-cfi-cross-dso"
+ cfiCflags = []string{"-flto", cfiCrossDsoFlag,
+ "-fsanitize-ignorelist=" + cfiBlocklistPath + "/" + cfiBlocklistFilename}
// -flto and -fvisibility are required by clang when -fsanitize=cfi is
// used, but have no effect on assembly files
cfiAsflags = []string{"-flto", "-fvisibility=default"}
- cfiLdflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fsanitize=cfi",
+ cfiLdflags = []string{"-flto", cfiCrossDsoFlag, "-fsanitize=cfi",
"-Wl,-plugin-opt,O1"}
- cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
+ cfiExportsMapPath = "build/soong/cc/config"
+ cfiExportsMapFilename = "cfi_exports.map"
+ cfiAssemblySupportFlag = "-fno-sanitize-cfi-canonical-jump-tables"
intOverflowCflags = []string{"-fsanitize-ignorelist=build/soong/cc/config/integer_overflow_blocklist.txt"}
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=4096", "malloc_fill_byte=0"}
+ "export_memory_stats=0", "max_malloc_fill_size=131072", "malloc_fill_byte=0"}
+ memtagStackCommonFlags = []string{"-march=armv8-a+memtag", "-mllvm", "-dom-tree-reachability-max-bbs-to-explore=128"}
+
+ hostOnlySanitizeFlags = []string{"-fno-sanitize-recover=all"}
+ deviceOnlySanitizeFlags = []string{"-fsanitize-trap=all", "-ftrap-function=abort"}
)
type SanitizerType int
@@ -89,6 +98,7 @@
scs
Fuzzer
Memtag_heap
+ Memtag_stack
cfi // cfi is last to prevent it running before incompatible mutators
)
@@ -100,6 +110,7 @@
scs,
Fuzzer,
Memtag_heap,
+ Memtag_stack,
cfi, // cfi is last to prevent it running before incompatible mutators
}
@@ -120,6 +131,8 @@
return "scs"
case Memtag_heap:
return "memtag_heap"
+ case Memtag_stack:
+ return "memtag_stack"
case Fuzzer:
return "fuzzer"
default:
@@ -136,6 +149,8 @@
return "hwaddress"
case Memtag_heap:
return "memtag_heap"
+ case Memtag_stack:
+ return "memtag_stack"
case tsan:
return "thread"
case intOverflow:
@@ -153,16 +168,31 @@
func (t SanitizerType) registerMutators(ctx android.RegisterMutatorsContext) {
switch t {
- case Asan, Hwasan, Fuzzer, scs, tsan, cfi:
- ctx.TopDown(t.variationName()+"_deps", sanitizerDepsMutator(t))
- ctx.BottomUp(t.variationName(), sanitizerMutator(t))
- case Memtag_heap, intOverflow:
+ case cfi, Hwasan, Asan, tsan, Fuzzer, scs:
+ sanitizer := &sanitizerSplitMutator{t}
+ ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
+ ctx.Transition(t.variationName(), sanitizer)
+ case Memtag_heap, Memtag_stack, intOverflow:
// do nothing
default:
panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
+// shouldPropagateToSharedLibraryDeps returns whether a sanitizer type should propagate to share
+// dependencies. In most cases, sanitizers only propagate to static dependencies; however, some
+// sanitizers also must be enabled for shared libraries for linking.
+func (t SanitizerType) shouldPropagateToSharedLibraryDeps() bool {
+ switch t {
+ case Fuzzer:
+ // Typically, shared libs are not split. However, for fuzzer, we split even for shared libs
+ // because a library sanitized for fuzzer can't be linked from a library that isn't sanitized
+ // for fuzzer.
+ return true
+ default:
+ return false
+ }
+}
func (*Module) SanitizerSupported(t SanitizerType) bool {
switch t {
case Asan:
@@ -181,6 +211,8 @@
return true
case Memtag_heap:
return true
+ case Memtag_stack:
+ return true
default:
return false
}
@@ -227,11 +259,14 @@
// This should not be used in Android 11+ : https://source.android.com/devices/tech/debug/scudo
// deprecated
Scudo *bool `android:"arch_variant"`
- // shadow-call-stack sanitizer, only available on arm64
+ // shadow-call-stack sanitizer, only available on arm64/riscv64.
Scs *bool `android:"arch_variant"`
// Memory-tagging, only available on arm64
// if diag.memtag unset or false, enables async memory tagging
Memtag_heap *bool `android:"arch_variant"`
+ // Memory-tagging stack instrumentation, only available on arm64
+ // Adds instrumentation to detect stack buffer overflows and use-after-scope using MTE.
+ Memtag_stack *bool `android:"arch_variant"`
// A modifier for ASAN and HWASAN for write only instrumentation
Writeonly *bool `android:"arch_variant"`
@@ -273,16 +308,72 @@
Blocklist *string
}
+type sanitizeMutatedProperties struct {
+ // Whether sanitizers can be enabled on this module
+ Never *bool `blueprint:"mutated"`
+
+ // Whether ASan (Address sanitizer) is enabled for this module.
+ // Hwaddress sanitizer takes precedence over this sanitizer.
+ Address *bool `blueprint:"mutated"`
+ // Whether TSan (Thread sanitizer) is enabled for this module
+ Thread *bool `blueprint:"mutated"`
+ // Whether HWASan (Hardware Address sanitizer) is enabled for this module
+ Hwaddress *bool `blueprint:"mutated"`
+
+ // Whether Undefined behavior sanitizer is enabled for this module
+ All_undefined *bool `blueprint:"mutated"`
+ // Whether undefined behavior sanitizer subset is enabled for this module
+ Undefined *bool `blueprint:"mutated"`
+ // List of specific undefined behavior sanitizers enabled for this module
+ Misc_undefined []string `blueprint:"mutated"`
+ // Whether Fuzzeris enabled for this module
+ Fuzzer *bool `blueprint:"mutated"`
+ // whether safe-stack sanitizer is enabled for this module
+ Safestack *bool `blueprint:"mutated"`
+ // Whether cfi sanitizer is enabled for this module
+ Cfi *bool `blueprint:"mutated"`
+ // Whether signed/unsigned integer overflow sanitizer is enabled for this module
+ Integer_overflow *bool `blueprint:"mutated"`
+ // Whether scudo sanitizer is enabled for this module
+ Scudo *bool `blueprint:"mutated"`
+ // Whether shadow-call-stack sanitizer is enabled for this module.
+ Scs *bool `blueprint:"mutated"`
+ // Whether Memory-tagging is enabled for this module
+ Memtag_heap *bool `blueprint:"mutated"`
+ // Whether Memory-tagging stack instrumentation is enabled for this module
+ Memtag_stack *bool `blueprint:"mutated"`
+
+ // Whether a modifier for ASAN and HWASAN for write only instrumentation is enabled for this
+ // module
+ Writeonly *bool `blueprint:"mutated"`
+
+ // Sanitizers to run in the diagnostic mode (as opposed to the release mode).
+ Diag struct {
+ // Whether Undefined behavior sanitizer, diagnostic mode is enabled for this module
+ Undefined *bool `blueprint:"mutated"`
+ // Whether cfi sanitizer, diagnostic mode is enabled for this module
+ Cfi *bool `blueprint:"mutated"`
+ // Whether signed/unsigned integer overflow sanitizer, diagnostic mode is enabled for this
+ // module
+ Integer_overflow *bool `blueprint:"mutated"`
+ // Whether Memory-tagging, diagnostic mode is enabled for this module
+ Memtag_heap *bool `blueprint:"mutated"`
+ // List of specific undefined behavior sanitizers enabled in diagnostic mode
+ Misc_undefined []string `blueprint:"mutated"`
+ } `blueprint:"mutated"`
+}
+
type SanitizeProperties struct {
- Sanitize SanitizeUserProps `android:"arch_variant"`
- SanitizerEnabled bool `blueprint:"mutated"`
- SanitizeDep bool `blueprint:"mutated"`
- MinimalRuntimeDep bool `blueprint:"mutated"`
- BuiltinsDep bool `blueprint:"mutated"`
- UbsanRuntimeDep bool `blueprint:"mutated"`
- InSanitizerDir bool `blueprint:"mutated"`
- Sanitizers []string `blueprint:"mutated"`
- DiagSanitizers []string `blueprint:"mutated"`
+ Sanitize SanitizeUserProps `android:"arch_variant"`
+ SanitizeMutated sanitizeMutatedProperties `blueprint:"mutated"`
+
+ SanitizerEnabled bool `blueprint:"mutated"`
+ MinimalRuntimeDep bool `blueprint:"mutated"`
+ BuiltinsDep bool `blueprint:"mutated"`
+ UbsanRuntimeDep bool `blueprint:"mutated"`
+ InSanitizerDir bool `blueprint:"mutated"`
+ Sanitizers []string `blueprint:"mutated"`
+ DiagSanitizers []string `blueprint:"mutated"`
}
type sanitize struct {
@@ -296,7 +387,26 @@
var _ android.SkipApexAllowedDependenciesCheck = (*libraryDependencyTag)(nil)
+var exportedVars = android.NewExportedVariables(pctx)
+
func init() {
+ exportedVars.ExportStringListStaticVariable("HostOnlySanitizeFlags", hostOnlySanitizeFlags)
+ exportedVars.ExportStringList("DeviceOnlySanitizeFlags", deviceOnlySanitizeFlags)
+
+ // Leave out "-flto" from the slices exported to bazel, as we will use the
+ // dedicated LTO feature for this. For C Flags and Linker Flags, also leave
+ // out the cross DSO flag which will be added separately by transitions.
+ exportedVars.ExportStringList("CfiCFlags", cfiCflags[2:])
+ exportedVars.ExportStringList("CfiLdFlags", cfiLdflags[2:])
+ exportedVars.ExportStringList("CfiAsFlags", cfiAsflags[1:])
+
+ exportedVars.ExportString("CfiCrossDsoFlag", cfiCrossDsoFlag)
+ exportedVars.ExportString("CfiBlocklistPath", cfiBlocklistPath)
+ exportedVars.ExportString("CfiBlocklistFilename", cfiBlocklistFilename)
+ exportedVars.ExportString("CfiExportsMapPath", cfiExportsMapPath)
+ exportedVars.ExportString("CfiExportsMapFilename", cfiExportsMapFilename)
+ exportedVars.ExportString("CfiAssemblySupportFlag", cfiAssemblySupportFlag)
+
android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
}
@@ -305,8 +415,42 @@
return []interface{}{&sanitize.Properties}
}
+func (p *sanitizeMutatedProperties) copyUserPropertiesToMutated(userProps *SanitizeUserProps) {
+ p.Never = userProps.Never
+ p.Address = userProps.Address
+ p.All_undefined = userProps.All_undefined
+ p.Cfi = userProps.Cfi
+ p.Fuzzer = userProps.Fuzzer
+ p.Hwaddress = userProps.Hwaddress
+ p.Integer_overflow = userProps.Integer_overflow
+ p.Memtag_heap = userProps.Memtag_heap
+ p.Memtag_stack = userProps.Memtag_stack
+ p.Safestack = userProps.Safestack
+ p.Scs = userProps.Scs
+ p.Scudo = userProps.Scudo
+ p.Thread = userProps.Thread
+ p.Undefined = userProps.Undefined
+ p.Writeonly = userProps.Writeonly
+
+ p.Misc_undefined = make([]string, 0, len(userProps.Misc_undefined))
+ for _, v := range userProps.Misc_undefined {
+ p.Misc_undefined = append(p.Misc_undefined, v)
+ }
+
+ p.Diag.Cfi = userProps.Diag.Cfi
+ p.Diag.Integer_overflow = userProps.Diag.Integer_overflow
+ p.Diag.Memtag_heap = userProps.Diag.Memtag_heap
+ p.Diag.Undefined = userProps.Diag.Undefined
+
+ p.Diag.Misc_undefined = make([]string, 0, len(userProps.Diag.Misc_undefined))
+ for _, v := range userProps.Diag.Misc_undefined {
+ p.Diag.Misc_undefined = append(p.Diag.Misc_undefined, v)
+ }
+}
+
func (sanitize *sanitize) begin(ctx BaseModuleContext) {
- s := &sanitize.Properties.Sanitize
+ s := &sanitize.Properties.SanitizeMutated
+ s.copyUserPropertiesToMutated(&sanitize.Properties.Sanitize)
// Don't apply sanitizers to NDK code.
if ctx.useSdk() {
@@ -318,7 +462,7 @@
return
}
- // cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap}).
+ // cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap: false}).
if ctx.testBinary() {
if s.Memtag_heap == nil {
s.Memtag_heap = proptools.BoolPtr(true)
@@ -404,6 +548,10 @@
}
}
+ if found, globalSanitizers = removeFromList("memtag_stack", globalSanitizers); found && s.Memtag_stack == nil {
+ s.Memtag_stack = proptools.BoolPtr(true)
+ }
+
if len(globalSanitizers) > 0 {
ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
}
@@ -430,7 +578,7 @@
}
// Enable Memtag for all components in the include paths (for Aarch64 only)
- if ctx.Arch().ArchType == android.Arm64 {
+ if ctx.Arch().ArchType == android.Arm64 && ctx.toolchain().Bionic() {
if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
if s.Memtag_heap == nil {
s.Memtag_heap = proptools.BoolPtr(true)
@@ -445,6 +593,12 @@
}
}
+ // Enable HWASan for all components in the include paths (for Aarch64 only)
+ if s.Hwaddress == nil && ctx.Config().HWASanEnabledForPath(ctx.ModuleDir()) &&
+ ctx.Arch().ArchType == android.Arm64 && ctx.toolchain().Bionic() {
+ s.Hwaddress = proptools.BoolPtr(true)
+ }
+
// Enable CFI for non-host components in the include paths
if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && !ctx.Host() {
s.Cfi = proptools.BoolPtr(true)
@@ -460,24 +614,33 @@
}
// HWASan requires AArch64 hardware feature (top-byte-ignore).
- if ctx.Arch().ArchType != android.Arm64 {
+ if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
s.Hwaddress = nil
}
- // SCS is only implemented on AArch64.
- if ctx.Arch().ArchType != android.Arm64 {
+ // SCS is only implemented on AArch64/riscv64.
+ if (ctx.Arch().ArchType != android.Arm64 && ctx.Arch().ArchType != android.Riscv64) || !ctx.toolchain().Bionic() {
+ s.Scs = nil
+ }
+ // ...but temporarily globally disabled on riscv64 (http://b/277909695).
+ if ctx.Arch().ArchType == android.Riscv64 {
s.Scs = nil
}
// Memtag_heap is only implemented on AArch64.
- if ctx.Arch().ArchType != android.Arm64 {
+ // Memtag ABI is Android specific for now, so disable for host.
+ if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() || ctx.Host() {
s.Memtag_heap = nil
+ s.Memtag_stack = nil
}
// Also disable CFI if ASAN is enabled.
if Bool(s.Address) || Bool(s.Hwaddress) {
s.Cfi = nil
s.Diag.Cfi = nil
+ // HWASAN and ASAN win against MTE.
+ s.Memtag_heap = nil
+ s.Memtag_stack = nil
}
// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
@@ -490,6 +653,12 @@
s.Integer_overflow = nil
}
+ // TODO(b/254713216): CFI doesn't work for riscv64 yet because LTO doesn't work.
+ if ctx.Arch().ArchType == android.Riscv64 {
+ s.Cfi = nil
+ s.Diag.Cfi = nil
+ }
+
// Disable CFI for musl
if ctx.toolchain().Musl() {
s.Cfi = nil
@@ -498,15 +667,8 @@
// Also disable CFI for VNDK variants of components
if ctx.isVndk() && ctx.useVndk() {
- if ctx.static() {
- // Cfi variant for static vndk should be captured as vendor snapshot,
- // so don't strictly disable Cfi.
- s.Cfi = nil
- s.Diag.Cfi = nil
- } else {
- s.Cfi = nil
- s.Diag.Cfi = nil
- }
+ s.Cfi = nil
+ s.Diag.Cfi = nil
}
// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
@@ -534,7 +696,7 @@
if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
- Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap)) {
+ Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap) || Bool(s.Memtag_stack)) {
sanitize.Properties.SanitizerEnabled = true
}
@@ -587,19 +749,13 @@
return false
}
-func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
- minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
-
- if sanitize.Properties.MinimalRuntimeDep {
- flags.Local.LdFlags = append(flags.Local.LdFlags,
- "-Wl,--exclude-libs,"+minimalRuntimeLib)
- }
-
- if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
+func (s *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
+ if !s.Properties.SanitizerEnabled && !s.Properties.UbsanRuntimeDep {
return flags
}
+ sanProps := &s.Properties.SanitizeMutated
- if Bool(sanitize.Properties.Sanitize.Address) {
+ if Bool(sanProps.Address) {
if ctx.Arch().ArchType == android.Arm {
// Frame pointer based unwinder in ASan requires ARM frame setup.
// TODO: put in flags?
@@ -608,7 +764,7 @@
flags.Local.CFlags = append(flags.Local.CFlags, asanCflags...)
flags.Local.LdFlags = append(flags.Local.LdFlags, asanLdflags...)
- if Bool(sanitize.Properties.Sanitize.Writeonly) {
+ if Bool(sanProps.Writeonly) {
flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-instrument-reads=0")
}
@@ -629,7 +785,7 @@
}
}
- if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ if Bool(sanProps.Hwaddress) {
flags.Local.CFlags = append(flags.Local.CFlags, hwasanCflags...)
for _, flag := range hwasanCommonflags {
@@ -639,12 +795,19 @@
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,"+flag)
}
- if Bool(sanitize.Properties.Sanitize.Writeonly) {
+ if Bool(sanProps.Writeonly) {
flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-hwasan-instrument-reads=0")
}
+ if !ctx.staticBinary() && !ctx.Host() {
+ if ctx.bootstrap() {
+ flags.DynamicLinker = "/system/bin/bootstrap/linker_hwasan64"
+ } else {
+ flags.DynamicLinker = "/system/bin/linker_hwasan64"
+ }
+ }
}
- if Bool(sanitize.Properties.Sanitize.Fuzzer) {
+ if Bool(sanProps.Fuzzer) {
flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize=fuzzer-no-link")
// TODO(b/131771163): LTO and Fuzzer support is mutually incompatible.
@@ -673,7 +836,7 @@
flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN`)
}
- if Bool(sanitize.Properties.Sanitize.Cfi) {
+ if Bool(sanProps.Cfi) {
if ctx.Arch().ArchType == android.Arm {
// __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). LLVM is not set up
// to do this on a function basis, so force Thumb on the entire module.
@@ -682,8 +845,8 @@
flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...)
flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...)
- if Bool(sanitize.Properties.Sanitize.Config.Cfi_assembly_support) {
- flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-cfi-canonical-jump-tables")
+ if Bool(s.Properties.Sanitize.Config.Cfi_assembly_support) {
+ flags.Local.CFlags = append(flags.Local.CFlags, cfiAssemblySupportFlag)
}
// Only append the default visibility flag if -fvisibility has not already been set
// to hidden.
@@ -698,12 +861,31 @@
}
}
- if Bool(sanitize.Properties.Sanitize.Integer_overflow) {
+ if Bool(sanProps.Memtag_stack) {
+ flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...)
+ // TODO(fmayer): remove -Wno-error once https://reviews.llvm.org/D127917 is in Android toolchain.
+ flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-error=frame-larger-than")
+ flags.Local.AsFlags = append(flags.Local.AsFlags, memtagStackCommonFlags...)
+ flags.Local.LdFlags = append(flags.Local.LdFlags, memtagStackCommonFlags...)
+ // This works around LLD complaining about the stack frame size.
+ // TODO(fmayer): remove once https://reviews.llvm.org/D127917 is in Android toolchain.
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-fatal-warnings")
+ }
+
+ if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack)) && ctx.binary() {
+ if Bool(sanProps.Diag.Memtag_heap) {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=sync")
+ } else {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=async")
+ }
+ }
+
+ if Bool(sanProps.Integer_overflow) {
flags.Local.CFlags = append(flags.Local.CFlags, intOverflowCflags...)
}
- if len(sanitize.Properties.Sanitizers) > 0 {
- sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
+ if len(s.Properties.Sanitizers) > 0 {
+ sanitizeArg := "-fsanitize=" + strings.Join(s.Properties.Sanitizers, ",")
flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
@@ -717,24 +899,29 @@
// 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)
+ }
- // non-Bionic toolchain prebuilts are missing UBSan's vptr and function san
+ if !ctx.toolchain().Bionic() {
+ // non-Bionic toolchain prebuilts are missing UBSan's vptr and function san.
+ // Musl toolchain prebuilts have vptr and function sanitizers, but enabling them
+ // implicitly enables RTTI which causes RTTI mismatch issues with dependencies.
+
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.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
- }
-
- if Bool(sanitize.Properties.Sanitize.Fuzzer) {
+ if Bool(sanProps.Fuzzer) {
// When fuzzing, we wish to crash with diagnostics on any bug.
flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
} else if ctx.Host() {
- flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover=all")
+ flags.Local.CFlags = append(flags.Local.CFlags, hostOnlySanitizeFlags...)
} else {
- flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
+ flags.Local.CFlags = append(flags.Local.CFlags, deviceOnlySanitizeFlags...)
}
+
+ if enableMinimalRuntime(s) {
+ flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
+ }
+
// 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")
@@ -745,22 +932,22 @@
}
}
- if len(sanitize.Properties.DiagSanitizers) > 0 {
- flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(sanitize.Properties.DiagSanitizers, ","))
+ if len(s.Properties.DiagSanitizers) > 0 {
+ flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(s.Properties.DiagSanitizers, ","))
}
// FIXME: enable RTTI if diag + (cfi or vptr)
- if sanitize.Properties.Sanitize.Recover != nil {
+ if s.Properties.Sanitize.Recover != nil {
flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-recover="+
- strings.Join(sanitize.Properties.Sanitize.Recover, ","))
+ strings.Join(s.Properties.Sanitize.Recover, ","))
}
- if sanitize.Properties.Sanitize.Diag.No_recover != nil {
+ if s.Properties.Sanitize.Diag.No_recover != nil {
flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover="+
- strings.Join(sanitize.Properties.Sanitize.Diag.No_recover, ","))
+ strings.Join(s.Properties.Sanitize.Diag.No_recover, ","))
}
- blocklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blocklist)
+ blocklist := android.OptionalPathForModuleSrc(ctx, s.Properties.Sanitize.Blocklist)
if blocklist.Valid() {
flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-ignorelist="+blocklist.String())
flags.CFlagsDeps = append(flags.CFlagsDeps, blocklist.Path())
@@ -769,45 +956,47 @@
return flags
}
-func (sanitize *sanitize) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (s *sanitize) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
// Add a suffix for cfi/hwasan/scs-enabled static/header libraries to allow surfacing
// both the sanitized and non-sanitized variants to make without a name conflict.
if entries.Class == "STATIC_LIBRARIES" || entries.Class == "HEADER_LIBRARIES" {
- if Bool(sanitize.Properties.Sanitize.Cfi) {
+ if Bool(s.Properties.SanitizeMutated.Cfi) {
entries.SubName += ".cfi"
}
- if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ if Bool(s.Properties.SanitizeMutated.Hwaddress) {
entries.SubName += ".hwasan"
}
- if Bool(sanitize.Properties.Sanitize.Scs) {
+ if Bool(s.Properties.SanitizeMutated.Scs) {
entries.SubName += ".scs"
}
}
}
-func (sanitize *sanitize) inSanitizerDir() bool {
- return sanitize.Properties.InSanitizerDir
+func (s *sanitize) inSanitizerDir() bool {
+ return s.Properties.InSanitizerDir
}
// getSanitizerBoolPtr returns the SanitizerTypes associated bool pointer from SanitizeProperties.
-func (sanitize *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
+func (s *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
switch t {
case Asan:
- return sanitize.Properties.Sanitize.Address
+ return s.Properties.SanitizeMutated.Address
case Hwasan:
- return sanitize.Properties.Sanitize.Hwaddress
+ return s.Properties.SanitizeMutated.Hwaddress
case tsan:
- return sanitize.Properties.Sanitize.Thread
+ return s.Properties.SanitizeMutated.Thread
case intOverflow:
- return sanitize.Properties.Sanitize.Integer_overflow
+ return s.Properties.SanitizeMutated.Integer_overflow
case cfi:
- return sanitize.Properties.Sanitize.Cfi
+ return s.Properties.SanitizeMutated.Cfi
case scs:
- return sanitize.Properties.Sanitize.Scs
+ return s.Properties.SanitizeMutated.Scs
case Memtag_heap:
- return sanitize.Properties.Sanitize.Memtag_heap
+ return s.Properties.SanitizeMutated.Memtag_heap
+ case Memtag_stack:
+ return s.Properties.SanitizeMutated.Memtag_stack
case Fuzzer:
- return sanitize.Properties.Sanitize.Fuzzer
+ return s.Properties.SanitizeMutated.Fuzzer
default:
panic(fmt.Errorf("unknown SanitizerType %d", t))
}
@@ -821,6 +1010,7 @@
!sanitize.isSanitizerEnabled(cfi) &&
!sanitize.isSanitizerEnabled(scs) &&
!sanitize.isSanitizerEnabled(Memtag_heap) &&
+ !sanitize.isSanitizerEnabled(Memtag_stack) &&
!sanitize.isSanitizerEnabled(Fuzzer)
}
@@ -839,21 +1029,28 @@
}
switch t {
case Asan:
- sanitize.Properties.Sanitize.Address = bPtr
+ sanitize.Properties.SanitizeMutated.Address = bPtr
+ // For ASAN variant, we need to disable Memtag_stack
+ sanitize.Properties.SanitizeMutated.Memtag_stack = nil
case Hwasan:
- sanitize.Properties.Sanitize.Hwaddress = bPtr
+ sanitize.Properties.SanitizeMutated.Hwaddress = bPtr
+ // For HWAsan variant, we need to disable Memtag_stack
+ sanitize.Properties.SanitizeMutated.Memtag_stack = nil
case tsan:
- sanitize.Properties.Sanitize.Thread = bPtr
+ sanitize.Properties.SanitizeMutated.Thread = bPtr
case intOverflow:
- sanitize.Properties.Sanitize.Integer_overflow = bPtr
+ sanitize.Properties.SanitizeMutated.Integer_overflow = bPtr
case cfi:
- sanitize.Properties.Sanitize.Cfi = bPtr
+ sanitize.Properties.SanitizeMutated.Cfi = bPtr
case scs:
- sanitize.Properties.Sanitize.Scs = bPtr
+ sanitize.Properties.SanitizeMutated.Scs = bPtr
case Memtag_heap:
- sanitize.Properties.Sanitize.Memtag_heap = bPtr
+ sanitize.Properties.SanitizeMutated.Memtag_heap = bPtr
+ case Memtag_stack:
+ sanitize.Properties.SanitizeMutated.Memtag_stack = bPtr
+ // We do not need to disable ASAN or HWASan here, as there is no Memtag_stack variant.
case Fuzzer:
- sanitize.Properties.Sanitize.Fuzzer = bPtr
+ sanitize.Properties.SanitizeMutated.Fuzzer = bPtr
default:
panic(fmt.Errorf("unknown SanitizerType %d", t))
}
@@ -906,7 +1103,12 @@
// Determines if the current module is a static library going to be captured
// as vendor snapshot. Such modules must create both cfi and non-cfi variants,
// except for ones which explicitly disable cfi.
-func needsCfiForVendorSnapshot(mctx android.TopDownMutatorContext) bool {
+func needsCfiForVendorSnapshot(mctx android.BaseModuleContext) bool {
+ if inList("hwaddress", mctx.Config().SanitizeDevice()) {
+ // cfi will not be built if SANITIZE_TARGET=hwaddress is set
+ return false
+ }
+
if snapshot.IsVendorProprietaryModule(mctx) {
return false
}
@@ -934,54 +1136,250 @@
!c.IsSanitizerExplicitlyDisabled(cfi)
}
-// Propagate sanitizer requirements down from binaries
-func sanitizerDepsMutator(t SanitizerType) func(android.TopDownMutatorContext) {
- return func(mctx android.TopDownMutatorContext) {
- if c, ok := mctx.Module().(PlatformSanitizeable); ok {
- enabled := c.IsSanitizerEnabled(t)
- if t == cfi && needsCfiForVendorSnapshot(mctx) {
- // We shouldn't change the result of isSanitizerEnabled(cfi) to correctly
- // determine defaultVariation in sanitizerMutator below.
- // Instead, just mark SanitizeDep to forcefully create cfi variant.
+type sanitizerSplitMutator struct {
+ sanitizer SanitizerType
+}
+
+// If an APEX is sanitized or not depends on whether it contains at least one
+// sanitized module. Transition mutators cannot propagate information up the
+// dependency graph this way, so we need an auxiliary mutator to do so.
+func (s *sanitizerSplitMutator) markSanitizableApexesMutator(ctx android.TopDownMutatorContext) {
+ if sanitizeable, ok := ctx.Module().(Sanitizeable); ok {
+ enabled := sanitizeable.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ if c, ok := dep.(*Module); ok && c.sanitize.isSanitizerEnabled(s.sanitizer) {
enabled = true
- c.SetSanitizeDep(true)
}
- if enabled {
- isSanitizableDependencyTag := c.SanitizableDepTagChecker()
- mctx.WalkDeps(func(child, parent android.Module) bool {
- if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
- return false
- }
- if d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() &&
- !d.SanitizeNever() &&
- !d.IsSanitizerExplicitlyDisabled(t) {
- if t == cfi || t == Hwasan || t == scs || t == Asan {
- 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)
- }
- } else {
- d.SetSanitizeDep(true)
- }
- }
- return true
- })
+ })
+
+ if enabled {
+ sanitizeable.EnableSanitizer(s.sanitizer.name())
+ }
+ }
+}
+
+func (s *sanitizerSplitMutator) Split(ctx android.BaseModuleContext) []string {
+ if c, ok := ctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
+ if s.sanitizer == cfi && needsCfiForVendorSnapshot(ctx) {
+ return []string{"", s.sanitizer.variationName()}
+ }
+
+ // If the given sanitizer is not requested in the .bp file for a module, it
+ // won't automatically build the sanitized variation.
+ if !c.IsSanitizerEnabled(s.sanitizer) {
+ return []string{""}
+ }
+
+ if c.Binary() {
+ // If a sanitizer is enabled for a binary, we do not build the version
+ // without the sanitizer
+ return []string{s.sanitizer.variationName()}
+ } else if c.StaticallyLinked() || c.Header() {
+ // For static libraries, we build both versions. Some Make modules
+ // apparently depend on this behavior.
+ return []string{"", s.sanitizer.variationName()}
+ } else {
+ // We only build the requested variation of dynamic libraries
+ return []string{s.sanitizer.variationName()}
+ }
+ }
+
+ if _, ok := ctx.Module().(JniSanitizeable); ok {
+ // TODO: this should call into JniSanitizable.IsSanitizerEnabledForJni but
+ // that is short-circuited for now
+ return []string{""}
+ }
+
+ // If an APEX has a sanitized dependency, we build the APEX in the sanitized
+ // variation. This is useful because such APEXes require extra dependencies.
+ if sanitizeable, ok := ctx.Module().(Sanitizeable); ok {
+ enabled := sanitizeable.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
+ if enabled {
+ return []string{s.sanitizer.variationName()}
+ } else {
+ return []string{""}
+ }
+ }
+
+ if c, ok := ctx.Module().(*Module); ok {
+ //TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
+
+ // Check if it's a snapshot module supporting sanitizer
+ if ss, ok := c.linker.(snapshotSanitizer); ok {
+ if ss.isSanitizerAvailable(s.sanitizer) {
+ return []string{"", s.sanitizer.variationName()}
+ } else {
+ return []string{""}
}
- } 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.
- mctx.VisitDirectDeps(func(child android.Module) {
- if c, ok := child.(*Module); ok && c.sanitize.isSanitizerEnabled(t) {
- sanitizeable.EnableSanitizer(t.name())
+ }
+ }
+
+ return []string{""}
+}
+
+func (s *sanitizerSplitMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+ if c, ok := ctx.Module().(PlatformSanitizeable); ok {
+ if !c.SanitizableDepTagChecker()(ctx.DepTag()) {
+ // If the dependency is through a non-sanitizable tag, use the
+ // non-sanitized variation
+ return ""
+ }
+
+ return sourceVariation
+ } else if _, ok := ctx.Module().(JniSanitizeable); ok {
+ // TODO: this should call into JniSanitizable.IsSanitizerEnabledForJni but
+ // that is short-circuited for now
+ return ""
+ } else {
+ // Otherwise, do not rock the boat.
+ return sourceVariation
+ }
+}
+
+func (s *sanitizerSplitMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+ if d, ok := ctx.Module().(PlatformSanitizeable); ok {
+ if dm, ok := ctx.Module().(*Module); ok {
+ if ss, ok := dm.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
+ return incomingVariation
+ }
+ }
+
+ if !d.SanitizePropDefined() ||
+ d.SanitizeNever() ||
+ d.IsSanitizerExplicitlyDisabled(s.sanitizer) ||
+ !d.SanitizerSupported(s.sanitizer) {
+ // If a module opts out of a sanitizer, use its non-sanitized variation
+ return ""
+ }
+
+ // Binaries are always built in the variation they requested.
+ if d.Binary() {
+ if d.IsSanitizerEnabled(s.sanitizer) {
+ return s.sanitizer.variationName()
+ } else {
+ return ""
+ }
+ }
+
+ // If a shared library requests to be sanitized, it will be built for that
+ // sanitizer. Otherwise, some sanitizers propagate through shared library
+ // dependency edges, some do not.
+ if !d.StaticallyLinked() && !d.Header() {
+ if d.IsSanitizerEnabled(s.sanitizer) {
+ return s.sanitizer.variationName()
+ }
+
+ // Some sanitizers do not propagate to shared dependencies
+ if !s.sanitizer.shouldPropagateToSharedLibraryDeps() {
+ return ""
+ }
+ }
+
+ // Static and header libraries inherit whether they are sanitized from the
+ // module they are linked into
+ return incomingVariation
+ } else if d, ok := ctx.Module().(Sanitizeable); ok {
+ // If an APEX contains a sanitized module, it will be built in the variation
+ // corresponding to that sanitizer.
+ enabled := d.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
+ if enabled {
+ return s.sanitizer.variationName()
+ }
+
+ return incomingVariation
+ }
+
+ return ""
+}
+
+func (s *sanitizerSplitMutator) Mutate(mctx android.BottomUpMutatorContext, variationName string) {
+ sanitizerVariation := variationName == s.sanitizer.variationName()
+
+ if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
+ sanitizerEnabled := c.IsSanitizerEnabled(s.sanitizer)
+
+ oneMakeVariation := false
+ if c.StaticallyLinked() || c.Header() {
+ if s.sanitizer != cfi && s.sanitizer != scs && s.sanitizer != Hwasan {
+ // These sanitizers export only one variation to Make. For the rest,
+ // Make targets can depend on both the sanitized and non-sanitized
+ // versions.
+ oneMakeVariation = true
+ }
+ } else if !c.Binary() {
+ // Shared library. These are the sanitizers that do propagate through shared
+ // library dependencies and therefore can cause multiple variations of a
+ // shared library to be built.
+ if s.sanitizer != cfi && s.sanitizer != Hwasan && s.sanitizer != scs && s.sanitizer != Asan {
+ oneMakeVariation = true
+ }
+ }
+
+ if oneMakeVariation {
+ if sanitizerEnabled != sanitizerVariation {
+ c.SetPreventInstall()
+ c.SetHideFromMake()
+ }
+ }
+
+ if sanitizerVariation {
+ c.SetSanitizer(s.sanitizer, true)
+
+ // CFI is incompatible with ASAN so disable it in ASAN variations
+ if s.sanitizer.incompatibleWithCfi() {
+ cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
+ if mctx.Device() && cfiSupported {
+ c.SetSanitizer(cfi, false)
}
- })
+ }
+
+ // locate the asan libraries under /data/asan
+ if !c.Binary() && !c.StaticallyLinked() && !c.Header() && mctx.Device() && s.sanitizer == Asan && sanitizerEnabled {
+ c.SetInSanitizerDir()
+ }
+
+ if c.StaticallyLinked() && c.ExportedToMake() {
+ if s.sanitizer == Hwasan {
+ hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
+ } else if s.sanitizer == cfi {
+ cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
+ }
+ }
+ } else if c.IsSanitizerEnabled(s.sanitizer) {
+ // Disable the sanitizer for the non-sanitized variation
+ c.SetSanitizer(s.sanitizer, false)
+ }
+ } else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok {
+ // If an APEX has sanitized dependencies, it gets a few more dependencies
+ if sanitizerVariation {
+ sanitizeable.AddSanitizerDependencies(mctx, s.sanitizer.name())
+ }
+ } else if c, ok := mctx.Module().(*Module); ok {
+ if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
+ if !ss.isUnsanitizedVariant() {
+ // Snapshot sanitizer may have only one variantion.
+ // Skip exporting the module if it already has a sanitizer variation.
+ c.SetPreventInstall()
+ c.SetHideFromMake()
+ return
+ }
+ c.linker.(snapshotSanitizer).setSanitizerVariation(s.sanitizer, sanitizerVariation)
+
+ // Export the static lib name to make
+ if c.static() && c.ExportedToMake() {
+ // use BaseModuleName which is the name for Make.
+ if s.sanitizer == cfi {
+ cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
+ } else if s.sanitizer == Hwasan {
+ hwasanStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
+ }
+ }
}
}
}
func (c *Module) SanitizeNever() bool {
- return Bool(c.sanitize.Properties.Sanitize.Never)
+ return Bool(c.sanitize.Properties.SanitizeMutated.Never)
}
func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
@@ -1049,10 +1447,12 @@
var sanitizers []string
var diagSanitizers []string
- if Bool(c.sanitize.Properties.Sanitize.All_undefined) {
+ sanProps := &c.sanitize.Properties.SanitizeMutated
+
+ if Bool(sanProps.All_undefined) {
sanitizers = append(sanitizers, "undefined")
} else {
- if Bool(c.sanitize.Properties.Sanitize.Undefined) {
+ if Bool(sanProps.Undefined) {
sanitizers = append(sanitizers,
"bool",
"integer-divide-by-zero",
@@ -1077,78 +1477,66 @@
// "object-size",
)
}
- sanitizers = append(sanitizers, c.sanitize.Properties.Sanitize.Misc_undefined...)
+ sanitizers = append(sanitizers, sanProps.Misc_undefined...)
}
- if Bool(c.sanitize.Properties.Sanitize.Diag.Undefined) {
+ if Bool(sanProps.Diag.Undefined) {
diagSanitizers = append(diagSanitizers, "undefined")
}
- diagSanitizers = append(diagSanitizers, c.sanitize.Properties.Sanitize.Diag.Misc_undefined...)
+ diagSanitizers = append(diagSanitizers, sanProps.Diag.Misc_undefined...)
- if Bool(c.sanitize.Properties.Sanitize.Address) {
+ if Bool(sanProps.Address) {
sanitizers = append(sanitizers, "address")
diagSanitizers = append(diagSanitizers, "address")
}
- if Bool(c.sanitize.Properties.Sanitize.Hwaddress) {
+ if Bool(sanProps.Hwaddress) {
sanitizers = append(sanitizers, "hwaddress")
}
- if Bool(c.sanitize.Properties.Sanitize.Thread) {
+ if Bool(sanProps.Thread) {
sanitizers = append(sanitizers, "thread")
}
- if Bool(c.sanitize.Properties.Sanitize.Safestack) {
+ if Bool(sanProps.Safestack) {
sanitizers = append(sanitizers, "safe-stack")
}
- if Bool(c.sanitize.Properties.Sanitize.Cfi) {
+ if Bool(sanProps.Cfi) {
sanitizers = append(sanitizers, "cfi")
- if Bool(c.sanitize.Properties.Sanitize.Diag.Cfi) {
+ if Bool(sanProps.Diag.Cfi) {
diagSanitizers = append(diagSanitizers, "cfi")
}
}
- if Bool(c.sanitize.Properties.Sanitize.Integer_overflow) {
+ if Bool(sanProps.Integer_overflow) {
sanitizers = append(sanitizers, "unsigned-integer-overflow")
sanitizers = append(sanitizers, "signed-integer-overflow")
- if Bool(c.sanitize.Properties.Sanitize.Diag.Integer_overflow) {
+ if Bool(sanProps.Diag.Integer_overflow) {
diagSanitizers = append(diagSanitizers, "unsigned-integer-overflow")
diagSanitizers = append(diagSanitizers, "signed-integer-overflow")
}
}
- if Bool(c.sanitize.Properties.Sanitize.Scudo) {
+ if Bool(sanProps.Scudo) {
sanitizers = append(sanitizers, "scudo")
}
- if Bool(c.sanitize.Properties.Sanitize.Scs) {
+ if Bool(sanProps.Scs) {
sanitizers = append(sanitizers, "shadow-call-stack")
}
- if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.Binary() {
- noteDep := "note_memtag_heap_async"
- if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
- noteDep = "note_memtag_heap_sync"
- }
- // If we're using snapshots, redirect to snapshot whenever possible
- // TODO(b/178470649): clean manual snapshot redirections
- snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
- if lib, ok := snapshot.StaticLibs[noteDep]; ok {
- noteDep = lib
- }
- depTag := StaticDepTag(true)
- variations := append(mctx.Target().Variations(),
- blueprint.Variation{Mutator: "link", Variation: "static"})
- if c.Device() {
- variations = append(variations, c.ImageVariation())
- }
- mctx.AddFarVariationDependencies(variations, depTag, noteDep)
+ if Bool(sanProps.Memtag_heap) && c.Binary() {
+ sanitizers = append(sanitizers, "memtag-heap")
}
- if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
+ if Bool(sanProps.Memtag_stack) {
+ sanitizers = append(sanitizers, "memtag-stack")
+ }
+
+ if Bool(sanProps.Fuzzer) {
sanitizers = append(sanitizers, "fuzzer-no-link")
}
@@ -1162,48 +1550,15 @@
diagSanitizers = sanitizers
}
- // Determine the runtime library required
- runtimeLibrary := ""
- var extraStaticDeps []string
- toolchain := c.toolchain(mctx)
- if Bool(c.sanitize.Properties.Sanitize.Address) {
- runtimeLibrary = config.AddressSanitizerRuntimeLibrary(toolchain)
- } else if Bool(c.sanitize.Properties.Sanitize.Hwaddress) {
- if c.staticBinary() {
- runtimeLibrary = config.HWAddressSanitizerStaticLibrary(toolchain)
- extraStaticDeps = []string{"libdl"}
- } else {
- runtimeLibrary = config.HWAddressSanitizerRuntimeLibrary(toolchain)
- }
- } else if Bool(c.sanitize.Properties.Sanitize.Thread) {
- runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(toolchain)
- } else if Bool(c.sanitize.Properties.Sanitize.Scudo) {
- if len(diagSanitizers) == 0 && !c.sanitize.Properties.UbsanRuntimeDep {
- runtimeLibrary = config.ScudoMinimalRuntimeLibrary(toolchain)
- } else {
- runtimeLibrary = config.ScudoRuntimeLibrary(toolchain)
- }
- } else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep ||
- Bool(c.sanitize.Properties.Sanitize.Fuzzer) ||
- Bool(c.sanitize.Properties.Sanitize.Undefined) ||
- Bool(c.sanitize.Properties.Sanitize.All_undefined) {
- runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
- if c.staticBinary() {
- runtimeLibrary += ".static"
- }
- }
-
- addStaticDeps := func(deps ...string) {
+ addStaticDeps := func(dep string, hideSymbols bool) {
// 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
- }
+ if lib, ok := snapshot.StaticLibs[dep]; ok {
+ dep = lib
}
// static executable gets static runtime libs
- depTag := libraryDependencyTag{Kind: staticLibraryDependency}
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency, unexportedSymbols: hideSymbols}
variations := append(mctx.Target().Variations(),
blueprint.Variation{Mutator: "link", Variation: "static"})
if c.Device() {
@@ -1213,17 +1568,60 @@
variations = append(variations,
blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
}
- mctx.AddFarVariationDependencies(variations, depTag, deps...)
-
+ mctx.AddFarVariationDependencies(variations, depTag, dep)
}
+
+ // Determine the runtime library required
+ runtimeSharedLibrary := ""
+ toolchain := c.toolchain(mctx)
+ if Bool(sanProps.Address) {
+ if toolchain.Musl() || (c.staticBinary() && toolchain.Bionic()) {
+ // Use a static runtime for musl to match what clang does for glibc.
+ addStaticDeps(config.AddressSanitizerStaticRuntimeLibrary(toolchain), false)
+ addStaticDeps(config.AddressSanitizerCXXStaticRuntimeLibrary(toolchain), false)
+ } else {
+ runtimeSharedLibrary = config.AddressSanitizerRuntimeLibrary(toolchain)
+ }
+ } else if Bool(sanProps.Hwaddress) {
+ if c.staticBinary() {
+ addStaticDeps(config.HWAddressSanitizerStaticLibrary(toolchain), true)
+ addStaticDeps("libdl", false)
+ } else {
+ runtimeSharedLibrary = config.HWAddressSanitizerRuntimeLibrary(toolchain)
+ }
+ } else if Bool(sanProps.Thread) {
+ runtimeSharedLibrary = config.ThreadSanitizerRuntimeLibrary(toolchain)
+ } else if Bool(sanProps.Scudo) {
+ if len(diagSanitizers) == 0 && !c.sanitize.Properties.UbsanRuntimeDep {
+ runtimeSharedLibrary = config.ScudoMinimalRuntimeLibrary(toolchain)
+ } else {
+ runtimeSharedLibrary = config.ScudoRuntimeLibrary(toolchain)
+ }
+ } else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep ||
+ Bool(sanProps.Fuzzer) ||
+ Bool(sanProps.Undefined) ||
+ Bool(sanProps.All_undefined) {
+ if toolchain.Musl() || (c.staticBinary() && toolchain.Bionic()) {
+ // Use a static runtime for static binaries.
+ // Also use a static runtime for musl to match
+ // what clang does for glibc. Otherwise dlopening
+ // libraries that depend on libclang_rt.ubsan_standalone.so
+ // fails with:
+ // Error relocating ...: initial-exec TLS resolves to dynamic definition
+ addStaticDeps(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)+".static", true)
+ } else {
+ runtimeSharedLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
+ }
+ }
+
if enableMinimalRuntime(c.sanitize) || c.sanitize.Properties.MinimalRuntimeDep {
- addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain))
+ addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), true)
}
if c.sanitize.Properties.BuiltinsDep {
- addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain))
+ addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain), true)
}
- if runtimeLibrary != "" && (toolchain.Bionic() || toolchain.Musl() || c.sanitize.Properties.UbsanRuntimeDep) {
+ if runtimeSharedLibrary != "" && (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*
@@ -1234,13 +1632,16 @@
// 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() {
- addStaticDeps(runtimeLibrary)
- addStaticDeps(extraStaticDeps...)
+ // Most sanitizers are either disabled for static binaries or have already
+ // handled the static binary case above through a direct call to addStaticDeps.
+ // If not, treat the runtime shared library as a static library and hope for
+ // the best.
+ addStaticDeps(runtimeSharedLibrary, true)
} else if !c.static() && !c.Header() {
// If we're using snapshots, redirect to snapshot whenever possible
snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
- if lib, ok := snapshot.SharedLibs[runtimeLibrary]; ok {
- runtimeLibrary = lib
+ if lib, ok := snapshot.SharedLibs[runtimeSharedLibrary]; ok {
+ runtimeSharedLibrary = lib
}
// Skip apex dependency check for sharedLibraryDependency
@@ -1264,7 +1665,7 @@
variations = append(variations,
blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
}
- AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeLibrary, "", true)
+ AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeSharedLibrary, "", true)
}
// static lib does not have dependency to the runtime library. The
// dependency will be added to the executables or shared libs using
@@ -1275,11 +1676,16 @@
type Sanitizeable interface {
android.Module
- IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool
+ IsSanitizerEnabled(config android.Config, sanitizerName string) bool
EnableSanitizer(sanitizerName string)
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
}
@@ -1296,10 +1702,6 @@
return c.sanitize.isSanitizerEnabled(t)
}
-func (c *Module) SanitizeDep() bool {
- return c.sanitize.Properties.SanitizeDep
-}
-
func (c *Module) StaticallyLinked() bool {
return c.static()
}
@@ -1316,124 +1718,8 @@
}
}
-func (c *Module) SetSanitizeDep(b bool) {
- if c.sanitize != nil {
- c.sanitize.Properties.SanitizeDep = b
- }
-}
-
var _ PlatformSanitizeable = (*Module)(nil)
-// Create sanitized variants for modules that need them
-func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) {
- return func(mctx android.BottomUpMutatorContext) {
- if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
-
- // Make sure we're not setting CFI to any value if it's not supported.
- cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
-
- if c.Binary() && c.IsSanitizerEnabled(t) {
- modules := mctx.CreateVariations(t.variationName())
- modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
- } else if c.IsSanitizerEnabled(t) || c.SanitizeDep() {
- isSanitizerEnabled := c.IsSanitizerEnabled(t)
- if c.StaticallyLinked() || c.Header() || t == Fuzzer {
- // Static and header libs are split into non-sanitized and sanitized variants.
- // Shared libs are not split. However, for asan and fuzzer, we split even for shared
- // libs because a library sanitized for asan/fuzzer can't be linked from a library
- // that isn't sanitized for asan/fuzzer.
- //
- // Note for defaultVariation: since we don't split for shared libs but for static/header
- // libs, it is possible for the sanitized variant of a static/header lib to depend
- // on non-sanitized variant of a shared lib. Such unfulfilled variation causes an
- // error when the module is split. defaultVariation is the name of the variation that
- // will be used when such a dangling dependency occurs during the split of the current
- // module. By setting it to the name of the sanitized variation, the dangling dependency
- // is redirected to the sanitized variant of the dependent module.
- defaultVariation := t.variationName()
- // Not all PlatformSanitizeable modules support the CFI sanitizer
- mctx.SetDefaultDependencyVariation(&defaultVariation)
-
- 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
- // are incompatible with cfi
- modules[1].(PlatformSanitizeable).SetSanitizer(cfi, false)
- }
-
- // For cfi/scs/hwasan, we can export both sanitized and un-sanitized variants
- // to Make, because the sanitized version has a different suffix in name.
- // For other types of sanitizers, suppress the variation that is disabled.
- if t != cfi && t != scs && t != Hwasan {
- if isSanitizerEnabled {
- modules[0].(PlatformSanitizeable).SetPreventInstall()
- modules[0].(PlatformSanitizeable).SetHideFromMake()
- } else {
- modules[1].(PlatformSanitizeable).SetPreventInstall()
- modules[1].(PlatformSanitizeable).SetHideFromMake()
- }
- }
-
- // Export the static lib name to make
- if c.StaticallyLinked() && c.ExportedToMake() {
- if t == cfi {
- cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
- } else if t == Hwasan {
- hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
- }
- }
- } else {
- // 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 {
- modules[0].(PlatformSanitizeable).SetInSanitizerDir()
- }
-
- if mctx.Device() && t.incompatibleWithCfi() && cfiSupported {
- // TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
- // are incompatible with cfi
- modules[0].(PlatformSanitizeable).SetSanitizer(cfi, false)
- }
- }
- }
- c.SetSanitizeDep(false)
- } else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
- // APEX modules fall here
- sanitizeable.AddSanitizerDependencies(mctx, t.name())
- mctx.CreateVariations(t.variationName())
- } else if c, ok := mctx.Module().(*Module); ok {
- //TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
-
- // Check if it's a snapshot module supporting sanitizer
- if s, ok := c.linker.(snapshotSanitizer); ok && s.isSanitizerEnabled(t) {
- // Set default variation as above.
- defaultVariation := t.variationName()
- mctx.SetDefaultDependencyVariation(&defaultVariation)
- modules := mctx.CreateVariations("", t.variationName())
- modules[0].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, false)
- modules[1].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, true)
-
- // Export the static lib name to make
- if c.static() && c.ExportedToMake() {
- if t == cfi {
- // use BaseModuleName which is the name for Make.
- cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
- }
- }
- }
- }
- }
-}
-
type sanitizerStaticLibsMap struct {
// libsMap contains one list of modules per each image and each arch.
// e.g. libs[vendor]["arm"] contains arm modules installed to vendor
@@ -1471,9 +1757,9 @@
// These are to be used by use_soong_sanitized_static_libraries.
// See build/make/core/binary.mk for more details.
func (s *sanitizerStaticLibsMap) exportToMake(ctx android.MakeVarsContext) {
- for _, image := range android.SortedStringKeys(s.libsMap) {
+ for _, image := range android.SortedKeys(s.libsMap) {
archMap := s.libsMap[ImageVariantType(image)]
- for _, arch := range android.SortedStringKeys(archMap) {
+ for _, arch := range android.SortedKeys(archMap) {
libs := archMap[arch]
sort.Strings(libs)
@@ -1505,23 +1791,27 @@
}
func enableMinimalRuntime(sanitize *sanitize) bool {
- 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) ||
- len(sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0) {
-
- return true
+ if sanitize.isSanitizerEnabled(Asan) {
+ return false
+ } else if sanitize.isSanitizerEnabled(Hwasan) {
+ return false
+ } else if sanitize.isSanitizerEnabled(Fuzzer) {
+ return false
}
- return false
+
+ if enableUbsanRuntime(sanitize) {
+ return false
+ }
+
+ sanitizeProps := &sanitize.Properties.SanitizeMutated
+ if Bool(sanitizeProps.Diag.Cfi) {
+ return false
+ }
+
+ return Bool(sanitizeProps.Integer_overflow) ||
+ len(sanitizeProps.Misc_undefined) > 0 ||
+ Bool(sanitizeProps.Undefined) ||
+ Bool(sanitizeProps.All_undefined)
}
func (m *Module) UbsanRuntimeNeeded() bool {
@@ -1533,9 +1823,10 @@
}
func enableUbsanRuntime(sanitize *sanitize) bool {
- return Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
- Bool(sanitize.Properties.Sanitize.Diag.Undefined) ||
- len(sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0
+ sanitizeProps := &sanitize.Properties.SanitizeMutated
+ return Bool(sanitizeProps.Diag.Integer_overflow) ||
+ Bool(sanitizeProps.Diag.Undefined) ||
+ len(sanitizeProps.Diag.Misc_undefined) > 0
}
func cfiMakeVarsProvider(ctx android.MakeVarsContext) {
@@ -1545,3 +1836,7 @@
func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
hwasanStaticLibs(ctx.Config()).exportToMake(ctx)
}
+
+func BazelCcSanitizerToolchainVars(config android.Config) string {
+ return android.BazelToolchainVars(config, exportedVars)
+}
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index c1ca034..29b17d4 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -16,19 +16,122 @@
import (
"fmt"
+ "runtime"
"strings"
"testing"
"android/soong/android"
+
+ "github.com/google/blueprint"
)
var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(`
cc_library_shared {
name: "libclang_rt.asan",
+ host_supported: true,
+ }
+ cc_library_static {
+ name: "libclang_rt.asan.static",
+ host_supported: true,
+ }
+ cc_library_static {
+ name: "libclang_rt.asan_cxx.static",
+ host_supported: true,
}
`))
+var prepareForTsanTest = android.FixtureAddFile("tsan/Android.bp", []byte(`
+ cc_library_shared {
+ name: "libclang_rt.tsan",
+ host_supported: true,
+ }
+`))
+
+type providerInterface interface {
+ ModuleProvider(blueprint.Module, blueprint.ProviderKey) interface{}
+}
+
+// expectSharedLinkDep verifies that the from module links against the to module as a
+// shared library.
+func expectSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+ t.Helper()
+ fromLink := from.Description("link")
+ toInfo := ctx.ModuleProvider(to.Module(), SharedLibraryInfoProvider).(SharedLibraryInfo)
+
+ if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); !android.InList(w, g) {
+ t.Errorf("%s should link against %s, expected %q, got %q",
+ from.Module(), to.Module(), w, g)
+ }
+}
+
+// expectNoSharedLinkDep verifies that the from module links against the to module as a
+// shared library.
+func expectNoSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+ t.Helper()
+ fromLink := from.Description("link")
+ toInfo := ctx.ModuleProvider(to.Module(), SharedLibraryInfoProvider).(SharedLibraryInfo)
+
+ if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); android.InList(w, g) {
+ t.Errorf("%s should not link against %s, expected %q, got %q",
+ from.Module(), to.Module(), w, g)
+ }
+}
+
+// expectStaticLinkDep verifies that the from module links against the to module as a
+// static library.
+func expectStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+ t.Helper()
+ fromLink := from.Description("link")
+ toInfo := ctx.ModuleProvider(to.Module(), StaticLibraryInfoProvider).(StaticLibraryInfo)
+
+ if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); !android.InList(w, g) {
+ t.Errorf("%s should link against %s, expected %q, got %q",
+ from.Module(), to.Module(), w, g)
+ }
+
+}
+
+// expectNoStaticLinkDep verifies that the from module links against the to module as a
+// static library.
+func expectNoStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+ t.Helper()
+ fromLink := from.Description("link")
+ toInfo := ctx.ModuleProvider(to.Module(), StaticLibraryInfoProvider).(StaticLibraryInfo)
+
+ if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); android.InList(w, g) {
+ t.Errorf("%s should not link against %s, expected %q, got %q",
+ from.Module(), to.Module(), w, g)
+ }
+
+}
+
+// expectInstallDep verifies that the install rule of the from module depends on the
+// install rule of the to module.
+func expectInstallDep(t *testing.T, from, to android.TestingModule) {
+ t.Helper()
+ fromInstalled := from.Description("install")
+ toInstalled := to.Description("install")
+
+ // combine implicits and order-only dependencies, host uses implicit but device uses
+ // order-only.
+ got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...)
+ want := toInstalled.Output.String()
+ if !android.InList(want, got) {
+ t.Errorf("%s installation should depend on %s, expected %q, got %q",
+ from.Module(), to.Module(), want, got)
+ }
+}
+
+type expectedRuntimeLinkage int
+
+const (
+ RUNTIME_LINKAGE_NONE = expectedRuntimeLinkage(0)
+ RUNTIME_LINKAGE_SHARED = iota
+ RUNTIME_LINKAGE_STATIC
+)
+
func TestAsan(t *testing.T) {
+ t.Parallel()
bp := `
cc_binary {
name: "bin_with_asan",
@@ -40,6 +143,7 @@
static_libs: [
"libstatic",
"libnoasan",
+ "libstatic_asan",
],
sanitize: {
address: true,
@@ -56,6 +160,7 @@
static_libs: [
"libstatic",
"libnoasan",
+ "libstatic_asan",
],
}
@@ -91,14 +196,26 @@
address: false,
}
}
+
+ cc_library_static {
+ name: "libstatic_asan",
+ host_supported: true,
+ sanitize: {
+ address: true,
+ }
+ }
+
`
- result := android.GroupFixturePreparers(
+ preparer := android.GroupFixturePreparers(
prepareForCcTest,
prepareForAsanTest,
- ).RunTestWithBp(t, bp)
+ )
+ buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()
- check := func(t *testing.T, result *android.TestResult, variant string) {
+ check := func(t *testing.T, variant string, runtimeLinkage expectedRuntimeLinkage, preparer android.FixturePreparer) {
+ result := preparer.RunTestWithBp(t, bp)
+ ctx := result.TestContext
asanVariant := variant + "_asan"
sharedVariant := variant + "_shared"
sharedAsanVariant := sharedVariant + "_asan"
@@ -124,83 +241,584 @@
// Static library that never uses asan.
libNoAsan := result.ModuleForTests("libnoasan", staticVariant)
- // expectSharedLinkDep verifies that the from module links against the to module as a
- // shared library.
- expectSharedLinkDep := func(from, to android.TestingModule) {
- t.Helper()
- fromLink := from.Description("link")
- toLink := to.Description("strip")
+ // Static library that specifies asan
+ libStaticAsan := result.ModuleForTests("libstatic_asan", staticAsanVariant)
+ libStaticAsanNoAsanVariant := result.ModuleForTests("libstatic_asan", staticVariant)
- if g, w := fromLink.OrderOnly.Strings(), toLink.Output.String(); !android.InList(w, g) {
- t.Errorf("%s should link against %s, expected %q, got %q",
- from.Module(), to.Module(), w, g)
- }
+ libAsanSharedRuntime := result.ModuleForTests("libclang_rt.asan", sharedVariant)
+ libAsanStaticRuntime := result.ModuleForTests("libclang_rt.asan.static", staticVariant)
+ libAsanStaticCxxRuntime := result.ModuleForTests("libclang_rt.asan_cxx.static", staticVariant)
+
+ expectSharedLinkDep(t, ctx, binWithAsan, libShared)
+ expectSharedLinkDep(t, ctx, binWithAsan, libAsan)
+ expectSharedLinkDep(t, ctx, libShared, libTransitive)
+ expectSharedLinkDep(t, ctx, libAsan, libTransitive)
+
+ expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsanVariant)
+ expectStaticLinkDep(t, ctx, binWithAsan, libNoAsan)
+ expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsan)
+
+ expectInstallDep(t, binWithAsan, libShared)
+ expectInstallDep(t, binWithAsan, libAsan)
+ expectInstallDep(t, binWithAsan, libTransitive)
+ expectInstallDep(t, libShared, libTransitive)
+ expectInstallDep(t, libAsan, libTransitive)
+
+ expectSharedLinkDep(t, ctx, binNoAsan, libShared)
+ expectSharedLinkDep(t, ctx, binNoAsan, libAsan)
+ expectSharedLinkDep(t, ctx, libShared, libTransitive)
+ expectSharedLinkDep(t, ctx, libAsan, libTransitive)
+
+ expectStaticLinkDep(t, ctx, binNoAsan, libStaticNoAsanVariant)
+ expectStaticLinkDep(t, ctx, binNoAsan, libNoAsan)
+ expectStaticLinkDep(t, ctx, binNoAsan, libStaticAsanNoAsanVariant)
+
+ expectInstallDep(t, binNoAsan, libShared)
+ expectInstallDep(t, binNoAsan, libAsan)
+ expectInstallDep(t, binNoAsan, libTransitive)
+ expectInstallDep(t, libShared, libTransitive)
+ expectInstallDep(t, libAsan, libTransitive)
+
+ if runtimeLinkage == RUNTIME_LINKAGE_SHARED {
+ expectSharedLinkDep(t, ctx, binWithAsan, libAsanSharedRuntime)
+ expectNoSharedLinkDep(t, ctx, binNoAsan, libAsanSharedRuntime)
+ expectSharedLinkDep(t, ctx, libAsan, libAsanSharedRuntime)
+ expectNoSharedLinkDep(t, ctx, libShared, libAsanSharedRuntime)
+ expectNoSharedLinkDep(t, ctx, libTransitive, libAsanSharedRuntime)
+ } else {
+ expectNoSharedLinkDep(t, ctx, binWithAsan, libAsanSharedRuntime)
+ expectNoSharedLinkDep(t, ctx, binNoAsan, libAsanSharedRuntime)
+ expectNoSharedLinkDep(t, ctx, libAsan, libAsanSharedRuntime)
+ expectNoSharedLinkDep(t, ctx, libShared, libAsanSharedRuntime)
+ expectNoSharedLinkDep(t, ctx, libTransitive, libAsanSharedRuntime)
}
- // expectStaticLinkDep verifies that the from module links against the to module as a
- // static library.
- expectStaticLinkDep := func(from, to android.TestingModule) {
- t.Helper()
- fromLink := from.Description("link")
- toLink := to.Description("static link")
+ if runtimeLinkage == RUNTIME_LINKAGE_STATIC {
+ expectStaticLinkDep(t, ctx, binWithAsan, libAsanStaticRuntime)
+ expectNoStaticLinkDep(t, ctx, binNoAsan, libAsanStaticRuntime)
+ expectStaticLinkDep(t, ctx, libAsan, libAsanStaticRuntime)
+ expectNoStaticLinkDep(t, ctx, libShared, libAsanStaticRuntime)
+ expectNoStaticLinkDep(t, ctx, libTransitive, libAsanStaticRuntime)
- if g, w := fromLink.Implicits.Strings(), toLink.Output.String(); !android.InList(w, g) {
- t.Errorf("%s should link against %s, expected %q, got %q",
- from.Module(), to.Module(), w, g)
- }
+ expectStaticLinkDep(t, ctx, binWithAsan, libAsanStaticCxxRuntime)
+ expectNoStaticLinkDep(t, ctx, binNoAsan, libAsanStaticCxxRuntime)
+ expectStaticLinkDep(t, ctx, libAsan, libAsanStaticCxxRuntime)
+ expectNoStaticLinkDep(t, ctx, libShared, libAsanStaticCxxRuntime)
+ expectNoStaticLinkDep(t, ctx, libTransitive, libAsanStaticCxxRuntime)
+ } else {
+ expectNoStaticLinkDep(t, ctx, binWithAsan, libAsanStaticRuntime)
+ expectNoStaticLinkDep(t, ctx, binNoAsan, libAsanStaticRuntime)
+ expectNoStaticLinkDep(t, ctx, libAsan, libAsanStaticRuntime)
+ expectNoStaticLinkDep(t, ctx, libShared, libAsanStaticRuntime)
+ expectNoStaticLinkDep(t, ctx, libTransitive, libAsanStaticRuntime)
+ expectNoStaticLinkDep(t, ctx, binWithAsan, libAsanStaticCxxRuntime)
+ expectNoStaticLinkDep(t, ctx, binNoAsan, libAsanStaticCxxRuntime)
+ expectNoStaticLinkDep(t, ctx, libAsan, libAsanStaticCxxRuntime)
+ expectNoStaticLinkDep(t, ctx, libShared, libAsanStaticCxxRuntime)
+ expectNoStaticLinkDep(t, ctx, libTransitive, libAsanStaticCxxRuntime)
}
-
- // expectInstallDep verifies that the install rule of the from module depends on the
- // install rule of the to module.
- expectInstallDep := func(from, to android.TestingModule) {
- t.Helper()
- fromInstalled := from.Description("install")
- toInstalled := to.Description("install")
-
- // combine implicits and order-only dependencies, host uses implicit but device uses
- // order-only.
- got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...)
- want := toInstalled.Output.String()
- if !android.InList(want, got) {
- t.Errorf("%s installation should depend on %s, expected %q, got %q",
- from.Module(), to.Module(), want, got)
- }
- }
-
- expectSharedLinkDep(binWithAsan, libShared)
- expectSharedLinkDep(binWithAsan, libAsan)
- expectSharedLinkDep(libShared, libTransitive)
- expectSharedLinkDep(libAsan, libTransitive)
-
- expectStaticLinkDep(binWithAsan, libStaticAsanVariant)
- expectStaticLinkDep(binWithAsan, libNoAsan)
-
- expectInstallDep(binWithAsan, libShared)
- expectInstallDep(binWithAsan, libAsan)
- expectInstallDep(binWithAsan, libTransitive)
- expectInstallDep(libShared, libTransitive)
- expectInstallDep(libAsan, libTransitive)
-
- expectSharedLinkDep(binNoAsan, libShared)
- expectSharedLinkDep(binNoAsan, libAsan)
- expectSharedLinkDep(libShared, libTransitive)
- expectSharedLinkDep(libAsan, libTransitive)
-
- expectStaticLinkDep(binNoAsan, libStaticNoAsanVariant)
- expectStaticLinkDep(binNoAsan, libNoAsan)
-
- expectInstallDep(binNoAsan, libShared)
- expectInstallDep(binNoAsan, libAsan)
- expectInstallDep(binNoAsan, libTransitive)
- expectInstallDep(libShared, libTransitive)
- expectInstallDep(libAsan, libTransitive)
}
- t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
+ t.Run("host", func(t *testing.T) { check(t, buildOS, RUNTIME_LINKAGE_NONE, preparer) })
+ t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", RUNTIME_LINKAGE_SHARED, preparer) })
+ t.Run("host musl", func(t *testing.T) {
+ check(t, "linux_musl_x86_64", RUNTIME_LINKAGE_STATIC,
+ android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
+ })
+}
+
+func TestTsan(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_binary {
+ name: "bin_with_tsan",
+ host_supported: true,
+ shared_libs: [
+ "libshared",
+ "libtsan",
+ ],
+ sanitize: {
+ thread: true,
+ }
+ }
+
+ cc_binary {
+ name: "bin_no_tsan",
+ host_supported: true,
+ shared_libs: [
+ "libshared",
+ "libtsan",
+ ],
+ }
+
+ cc_library_shared {
+ name: "libshared",
+ host_supported: true,
+ shared_libs: ["libtransitive"],
+ }
+
+ cc_library_shared {
+ name: "libtsan",
+ host_supported: true,
+ shared_libs: ["libtransitive"],
+ sanitize: {
+ thread: true,
+ }
+ }
+
+ cc_library_shared {
+ name: "libtransitive",
+ host_supported: true,
+ }
+`
+
+ preparer := android.GroupFixturePreparers(
+ prepareForCcTest,
+ prepareForTsanTest,
+ )
+ buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()
+
+ check := func(t *testing.T, variant string, preparer android.FixturePreparer) {
+ result := preparer.RunTestWithBp(t, bp)
+ ctx := result.TestContext
+ tsanVariant := variant + "_tsan"
+ sharedVariant := variant + "_shared"
+ sharedTsanVariant := sharedVariant + "_tsan"
+
+ // The binaries, one with tsan and one without
+ binWithTsan := result.ModuleForTests("bin_with_tsan", tsanVariant)
+ binNoTsan := result.ModuleForTests("bin_no_tsan", variant)
+
+ // Shared libraries that don't request tsan
+ libShared := result.ModuleForTests("libshared", sharedVariant)
+ libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+
+ // Shared library that requests tsan
+ libTsan := result.ModuleForTests("libtsan", sharedTsanVariant)
+
+ expectSharedLinkDep(t, ctx, binWithTsan, libShared)
+ expectSharedLinkDep(t, ctx, binWithTsan, libTsan)
+ expectSharedLinkDep(t, ctx, libShared, libTransitive)
+ expectSharedLinkDep(t, ctx, libTsan, libTransitive)
+
+ expectSharedLinkDep(t, ctx, binNoTsan, libShared)
+ expectSharedLinkDep(t, ctx, binNoTsan, libTsan)
+ expectSharedLinkDep(t, ctx, libShared, libTransitive)
+ expectSharedLinkDep(t, ctx, libTsan, libTransitive)
+ }
+
+ t.Run("host", func(t *testing.T) { check(t, buildOS, preparer) })
+ t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", preparer) })
+ t.Run("host musl", func(t *testing.T) {
+ check(t, "linux_musl_x86_64", android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
+ })
+}
+
+func TestMiscUndefined(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("requires linux")
+ }
+
+ t.Parallel()
+ bp := `
+ cc_binary {
+ name: "bin_with_ubsan",
+ srcs: ["src.cc"],
+ host_supported: true,
+ static_libs: [
+ "libstatic",
+ "libubsan",
+ ],
+ sanitize: {
+ misc_undefined: ["integer"],
+ }
+ }
+
+ cc_binary {
+ name: "bin_no_ubsan",
+ host_supported: true,
+ srcs: ["src.cc"],
+ static_libs: [
+ "libstatic",
+ "libubsan",
+ ],
+ }
+
+ cc_library_static {
+ name: "libstatic",
+ host_supported: true,
+ srcs: ["src.cc"],
+ static_libs: ["libtransitive"],
+ }
+
+ cc_library_static {
+ name: "libubsan",
+ host_supported: true,
+ srcs: ["src.cc"],
+ whole_static_libs: ["libtransitive"],
+ sanitize: {
+ misc_undefined: ["integer"],
+ }
+ }
+
+ cc_library_static {
+ name: "libtransitive",
+ host_supported: true,
+ srcs: ["src.cc"],
+ }
+`
+
+ preparer := android.GroupFixturePreparers(
+ prepareForCcTest,
+ )
+ buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()
+
+ check := func(t *testing.T, variant string, preparer android.FixturePreparer) {
+ result := preparer.RunTestWithBp(t, bp)
+ ctx := result.TestContext
+ staticVariant := variant + "_static"
+
+ // The binaries, one with ubsan and one without
+ binWithUbsan := result.ModuleForTests("bin_with_ubsan", variant)
+ binNoUbsan := result.ModuleForTests("bin_no_ubsan", variant)
+
+ // Static libraries that don't request ubsan
+ libStatic := result.ModuleForTests("libstatic", staticVariant)
+ libTransitive := result.ModuleForTests("libtransitive", staticVariant)
+
+ libUbsan := result.ModuleForTests("libubsan", staticVariant)
+
+ libUbsanMinimal := result.ModuleForTests("libclang_rt.ubsan_minimal", staticVariant)
+
+ expectStaticLinkDep(t, ctx, binWithUbsan, libStatic)
+ expectStaticLinkDep(t, ctx, binWithUbsan, libUbsan)
+ expectStaticLinkDep(t, ctx, binWithUbsan, libUbsanMinimal)
+
+ miscUndefinedSanFlag := "-fsanitize=integer"
+ binWithUbsanCflags := binWithUbsan.Rule("cc").Args["cFlags"]
+ if !strings.Contains(binWithUbsanCflags, miscUndefinedSanFlag) {
+ t.Errorf("'bin_with_ubsan' Expected %q to be in flags %q, was not", miscUndefinedSanFlag, binWithUbsanCflags)
+ }
+ libStaticCflags := libStatic.Rule("cc").Args["cFlags"]
+ if strings.Contains(libStaticCflags, miscUndefinedSanFlag) {
+ t.Errorf("'libstatic' Expected %q to NOT be in flags %q, was", miscUndefinedSanFlag, binWithUbsanCflags)
+ }
+ libUbsanCflags := libUbsan.Rule("cc").Args["cFlags"]
+ if !strings.Contains(libUbsanCflags, miscUndefinedSanFlag) {
+ t.Errorf("'libubsan' Expected %q to be in flags %q, was not", miscUndefinedSanFlag, binWithUbsanCflags)
+ }
+ libTransitiveCflags := libTransitive.Rule("cc").Args["cFlags"]
+ if strings.Contains(libTransitiveCflags, miscUndefinedSanFlag) {
+ t.Errorf("'libtransitive': Expected %q to NOT be in flags %q, was", miscUndefinedSanFlag, binWithUbsanCflags)
+ }
+
+ expectStaticLinkDep(t, ctx, binNoUbsan, libStatic)
+ expectStaticLinkDep(t, ctx, binNoUbsan, libUbsan)
+ }
+
+ t.Run("host", func(t *testing.T) { check(t, buildOS, preparer) })
+ t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", preparer) })
+ t.Run("host musl", func(t *testing.T) {
+ check(t, "linux_musl_x86_64", android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
+ })
+}
+
+func TestFuzz(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_binary {
+ name: "bin_with_fuzzer",
+ host_supported: true,
+ shared_libs: [
+ "libshared",
+ "libfuzzer",
+ ],
+ static_libs: [
+ "libstatic",
+ "libnofuzzer",
+ "libstatic_fuzzer",
+ ],
+ sanitize: {
+ fuzzer: true,
+ }
+ }
+
+ cc_binary {
+ name: "bin_no_fuzzer",
+ host_supported: true,
+ shared_libs: [
+ "libshared",
+ "libfuzzer",
+ ],
+ static_libs: [
+ "libstatic",
+ "libnofuzzer",
+ "libstatic_fuzzer",
+ ],
+ }
+
+ cc_library_shared {
+ name: "libshared",
+ host_supported: true,
+ shared_libs: ["libtransitive"],
+ }
+
+ cc_library_shared {
+ name: "libfuzzer",
+ host_supported: true,
+ shared_libs: ["libtransitive"],
+ sanitize: {
+ fuzzer: true,
+ }
+ }
+
+ cc_library_shared {
+ name: "libtransitive",
+ host_supported: true,
+ }
+
+ cc_library_static {
+ name: "libstatic",
+ host_supported: true,
+ }
+
+ cc_library_static {
+ name: "libnofuzzer",
+ host_supported: true,
+ sanitize: {
+ fuzzer: false,
+ }
+ }
+
+ cc_library_static {
+ name: "libstatic_fuzzer",
+ host_supported: true,
+ }
+
+ `
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ ).RunTestWithBp(t, bp)
+
+ check := func(t *testing.T, result *android.TestResult, variant string) {
+ ctx := result.TestContext
+ fuzzerVariant := variant + "_fuzzer"
+ sharedVariant := variant + "_shared"
+ sharedFuzzerVariant := sharedVariant + "_fuzzer"
+ staticVariant := variant + "_static"
+ staticFuzzerVariant := staticVariant + "_fuzzer"
+
+ // The binaries, one with fuzzer and one without
+ binWithFuzzer := result.ModuleForTests("bin_with_fuzzer", fuzzerVariant)
+ binNoFuzzer := result.ModuleForTests("bin_no_fuzzer", variant)
+
+ // Shared libraries that don't request fuzzer
+ libShared := result.ModuleForTests("libshared", sharedVariant)
+ libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+
+ // Shared libraries that don't request fuzzer
+ libSharedFuzzer := result.ModuleForTests("libshared", sharedFuzzerVariant)
+ libTransitiveFuzzer := result.ModuleForTests("libtransitive", sharedFuzzerVariant)
+
+ // Shared library that requests fuzzer
+ libFuzzer := result.ModuleForTests("libfuzzer", sharedFuzzerVariant)
+
+ // Static library that uses an fuzzer variant for bin_with_fuzzer and a non-fuzzer variant
+ // for bin_no_fuzzer.
+ libStaticFuzzerVariant := result.ModuleForTests("libstatic", staticFuzzerVariant)
+ libStaticNoFuzzerVariant := result.ModuleForTests("libstatic", staticVariant)
+
+ // Static library that never uses fuzzer.
+ libNoFuzzer := result.ModuleForTests("libnofuzzer", staticVariant)
+
+ // Static library that specifies fuzzer
+ libStaticFuzzer := result.ModuleForTests("libstatic_fuzzer", staticFuzzerVariant)
+ libStaticFuzzerNoFuzzerVariant := result.ModuleForTests("libstatic_fuzzer", staticVariant)
+
+ expectSharedLinkDep(t, ctx, binWithFuzzer, libSharedFuzzer)
+ expectSharedLinkDep(t, ctx, binWithFuzzer, libFuzzer)
+ expectSharedLinkDep(t, ctx, libSharedFuzzer, libTransitiveFuzzer)
+ expectSharedLinkDep(t, ctx, libFuzzer, libTransitiveFuzzer)
+
+ expectStaticLinkDep(t, ctx, binWithFuzzer, libStaticFuzzerVariant)
+ expectStaticLinkDep(t, ctx, binWithFuzzer, libNoFuzzer)
+ expectStaticLinkDep(t, ctx, binWithFuzzer, libStaticFuzzer)
+
+ expectSharedLinkDep(t, ctx, binNoFuzzer, libShared)
+ expectSharedLinkDep(t, ctx, binNoFuzzer, libFuzzer)
+ expectSharedLinkDep(t, ctx, libShared, libTransitive)
+ expectSharedLinkDep(t, ctx, libFuzzer, libTransitiveFuzzer)
+
+ expectStaticLinkDep(t, ctx, binNoFuzzer, libStaticNoFuzzerVariant)
+ expectStaticLinkDep(t, ctx, binNoFuzzer, libNoFuzzer)
+ expectStaticLinkDep(t, ctx, binNoFuzzer, libStaticFuzzerNoFuzzerVariant)
+ }
+
t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
}
+func TestUbsan(t *testing.T) {
+ t.Parallel()
+ if runtime.GOOS != "linux" {
+ t.Skip("requires linux")
+ }
+
+ bp := `
+ cc_binary {
+ name: "bin_with_ubsan",
+ host_supported: true,
+ shared_libs: [
+ "libshared",
+ ],
+ static_libs: [
+ "libstatic",
+ "libnoubsan",
+ ],
+ sanitize: {
+ undefined: true,
+ }
+ }
+
+ cc_binary {
+ name: "bin_depends_ubsan_static",
+ host_supported: true,
+ shared_libs: [
+ "libshared",
+ ],
+ static_libs: [
+ "libstatic",
+ "libubsan",
+ "libnoubsan",
+ ],
+ }
+
+ cc_binary {
+ name: "bin_depends_ubsan_shared",
+ host_supported: true,
+ shared_libs: [
+ "libsharedubsan",
+ ],
+ }
+
+ cc_binary {
+ name: "bin_no_ubsan",
+ host_supported: true,
+ shared_libs: [
+ "libshared",
+ ],
+ static_libs: [
+ "libstatic",
+ "libnoubsan",
+ ],
+ }
+
+ cc_library_shared {
+ name: "libshared",
+ host_supported: true,
+ shared_libs: ["libtransitive"],
+ }
+
+ cc_library_shared {
+ name: "libtransitive",
+ host_supported: true,
+ }
+
+ cc_library_shared {
+ name: "libsharedubsan",
+ host_supported: true,
+ sanitize: {
+ undefined: true,
+ }
+ }
+
+ cc_library_static {
+ name: "libubsan",
+ host_supported: true,
+ sanitize: {
+ undefined: true,
+ }
+ }
+
+ cc_library_static {
+ name: "libstatic",
+ host_supported: true,
+ }
+
+ cc_library_static {
+ name: "libnoubsan",
+ host_supported: true,
+ }
+ `
+
+ preparer := android.GroupFixturePreparers(
+ prepareForCcTest,
+ )
+ buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()
+
+ check := func(t *testing.T, variant string, preparer android.FixturePreparer) {
+ result := preparer.RunTestWithBp(t, bp)
+ staticVariant := variant + "_static"
+ sharedVariant := variant + "_shared"
+
+ minimalRuntime := result.ModuleForTests("libclang_rt.ubsan_minimal", staticVariant)
+
+ // The binaries, one with ubsan and one without
+ binWithUbsan := result.ModuleForTests("bin_with_ubsan", variant)
+ binDependsUbsan := result.ModuleForTests("bin_depends_ubsan_static", variant)
+ libSharedUbsan := result.ModuleForTests("libsharedubsan", sharedVariant)
+ binDependsUbsanShared := result.ModuleForTests("bin_depends_ubsan_shared", variant)
+ binNoUbsan := result.ModuleForTests("bin_no_ubsan", variant)
+
+ android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in bin_with_ubsan static libs",
+ strings.Split(binWithUbsan.Rule("ld").Args["libFlags"], " "),
+ minimalRuntime.OutputFiles(t, "")[0].String())
+
+ android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in bin_depends_ubsan_static static libs",
+ strings.Split(binDependsUbsan.Rule("ld").Args["libFlags"], " "),
+ minimalRuntime.OutputFiles(t, "")[0].String())
+
+ android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in libsharedubsan static libs",
+ strings.Split(libSharedUbsan.Rule("ld").Args["libFlags"], " "),
+ minimalRuntime.OutputFiles(t, "")[0].String())
+
+ android.AssertStringListDoesNotContain(t, "unexpected libclang_rt.ubsan_minimal in bin_depends_ubsan_shared static libs",
+ strings.Split(binDependsUbsanShared.Rule("ld").Args["libFlags"], " "),
+ minimalRuntime.OutputFiles(t, "")[0].String())
+
+ android.AssertStringListDoesNotContain(t, "unexpected libclang_rt.ubsan_minimal in bin_no_ubsan static libs",
+ strings.Split(binNoUbsan.Rule("ld").Args["libFlags"], " "),
+ minimalRuntime.OutputFiles(t, "")[0].String())
+
+ android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in bin_with_ubsan",
+ strings.Split(binWithUbsan.Rule("ld").Args["ldFlags"], " "),
+ "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+
+ android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in bin_depends_ubsan_static static libs",
+ strings.Split(binDependsUbsan.Rule("ld").Args["ldFlags"], " "),
+ "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+
+ android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in libsharedubsan static libs",
+ strings.Split(libSharedUbsan.Rule("ld").Args["ldFlags"], " "),
+ "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+
+ android.AssertStringListDoesNotContain(t, "unexpected -Wl,--exclude-libs for minimal runtime in bin_depends_ubsan_shared static libs",
+ strings.Split(binDependsUbsanShared.Rule("ld").Args["ldFlags"], " "),
+ "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+
+ android.AssertStringListDoesNotContain(t, "unexpected -Wl,--exclude-libs for minimal runtime in bin_no_ubsan static libs",
+ strings.Split(binNoUbsan.Rule("ld").Args["ldFlags"], " "),
+ "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+ }
+
+ t.Run("host", func(t *testing.T) { check(t, buildOS, preparer) })
+ t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", preparer) })
+ t.Run("host musl", func(t *testing.T) {
+ check(t, "linux_musl_x86_64", android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
+ })
+}
+
type MemtagNoteType int
const (
@@ -224,19 +842,13 @@
func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
t.Helper()
- note_async := "note_memtag_heap_async"
- note_sync := "note_memtag_heap_sync"
found := None
- implicits := m.Rule("ld").Implicits
- for _, lib := range implicits {
- if strings.Contains(lib.Rel(), note_async) {
- found = Async
- break
- } else if strings.Contains(lib.Rel(), note_sync) {
- found = Sync
- break
- }
+ ldFlags := m.Rule("ld").Args["ldFlags"]
+ if strings.Contains(ldFlags, "-fsanitize-memtag-mode=async") {
+ found = Async
+ } else if strings.Contains(ldFlags, "-fsanitize-memtag-mode=sync") {
+ found = Sync
}
if found != expected {
@@ -332,6 +944,7 @@
)
func TestSanitizeMemtagHeap(t *testing.T) {
+ t.Parallel()
variant := "android_arm64_armv8-a"
result := android.GroupFixturePreparers(
@@ -404,6 +1017,7 @@
}
func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
+ t.Parallel()
variant := "android_arm64_armv8-a"
result := android.GroupFixturePreparers(
@@ -478,6 +1092,7 @@
}
func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
+ t.Parallel()
variant := "android_arm64_armv8-a"
result := android.GroupFixturePreparers(
@@ -551,3 +1166,83 @@
checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
}
+
+func TestCfi(t *testing.T) {
+ t.Parallel()
+
+ bp := `
+ cc_library_shared {
+ name: "shared_with_cfi",
+ static_libs: [
+ "static_dep_with_cfi",
+ "static_dep_no_cfi",
+ ],
+ sanitize: {
+ cfi: true,
+ },
+ }
+
+ cc_library_shared {
+ name: "shared_no_cfi",
+ static_libs: [
+ "static_dep_with_cfi",
+ "static_dep_no_cfi",
+ ],
+ }
+
+ cc_library_static {
+ name: "static_dep_with_cfi",
+ sanitize: {
+ cfi: true,
+ },
+ }
+
+ cc_library_static {
+ name: "static_dep_no_cfi",
+ }
+
+ cc_library_shared {
+ name: "shared_rdep_no_cfi",
+ static_libs: ["static_dep_with_cfi_2"],
+ }
+
+ cc_library_static {
+ name: "static_dep_with_cfi_2",
+ sanitize: {
+ cfi: true,
+ },
+ }
+`
+ preparer := android.GroupFixturePreparers(
+ prepareForCcTest,
+ )
+ result := preparer.RunTestWithBp(t, bp)
+ ctx := result.TestContext
+
+ buildOs := "android_arm64_armv8-a"
+ shared_suffix := "_shared"
+ cfi_suffix := "_cfi"
+ static_suffix := "_static"
+
+ sharedWithCfiLib := result.ModuleForTests("shared_with_cfi", buildOs+shared_suffix+cfi_suffix)
+ sharedNoCfiLib := result.ModuleForTests("shared_no_cfi", buildOs+shared_suffix)
+ staticWithCfiLib := result.ModuleForTests("static_dep_with_cfi", buildOs+static_suffix)
+ staticWithCfiLibCfiVariant := result.ModuleForTests("static_dep_with_cfi", buildOs+static_suffix+cfi_suffix)
+ staticNoCfiLib := result.ModuleForTests("static_dep_no_cfi", buildOs+static_suffix)
+ staticNoCfiLibCfiVariant := result.ModuleForTests("static_dep_no_cfi", buildOs+static_suffix+cfi_suffix)
+ sharedRdepNoCfi := result.ModuleForTests("shared_rdep_no_cfi", buildOs+shared_suffix)
+ staticDepWithCfi2Lib := result.ModuleForTests("static_dep_with_cfi_2", buildOs+static_suffix)
+
+ // Confirm assumptions about propagation of CFI enablement
+ expectStaticLinkDep(t, ctx, sharedWithCfiLib, staticWithCfiLibCfiVariant)
+ expectStaticLinkDep(t, ctx, sharedNoCfiLib, staticWithCfiLib)
+ expectStaticLinkDep(t, ctx, sharedWithCfiLib, staticNoCfiLibCfiVariant)
+ expectStaticLinkDep(t, ctx, sharedNoCfiLib, staticNoCfiLib)
+ expectStaticLinkDep(t, ctx, sharedRdepNoCfi, staticDepWithCfi2Lib)
+
+ // Confirm that non-CFI variants do not add CFI flags
+ bazLibCflags := staticWithCfiLib.Rule("cc").Args["cFlags"]
+ if strings.Contains(bazLibCflags, "-fsanitize-cfi-cross-dso") {
+ t.Errorf("non-CFI variant of baz not expected to contain CFI flags ")
+ }
+}
diff --git a/cc/sdk.go b/cc/sdk.go
index a83e5ad..4f361eb 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -31,6 +31,7 @@
switch m := ctx.Module().(type) {
case LinkableInterface:
+ ccModule, isCcModule := ctx.Module().(*Module)
if m.AlwaysSdk() {
if !m.UseSdk() && !m.SplitPerApiLevel() {
ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?")
@@ -46,23 +47,52 @@
// Mark the SDK variant.
modules[1].(*Module).Properties.IsSdkVariant = true
+ // SDK variant is not supposed to be installed
+ modules[1].(*Module).Properties.PreventInstall = true
if ctx.Config().UnbundledBuildApps() {
// For an unbundled apps build, hide the platform variant from Make.
modules[0].(*Module).Properties.HideFromMake = true
- modules[0].(*Module).Properties.PreventInstall = true
} else {
// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
// exposed to Make.
modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
- modules[1].(*Module).Properties.PreventInstall = true
}
ctx.AliasVariation("")
+ } else if isCcModule && ccModule.isImportedApiLibrary() {
+ apiLibrary, _ := ccModule.linker.(*apiLibraryDecorator)
+ if apiLibrary.hasNDKStubs() && ccModule.canUseSdk() {
+ variations := []string{"sdk"}
+ if apiLibrary.hasApexStubs() {
+ variations = append(variations, "")
+ }
+ // Handle cc_api_library module with NDK stubs and variants only which can use SDK
+ modules := ctx.CreateVariations(variations...)
+ // Mark the SDK variant.
+ modules[0].(*Module).Properties.IsSdkVariant = true
+ if ctx.Config().UnbundledBuildApps() {
+ if apiLibrary.hasApexStubs() {
+ // For an unbundled apps build, hide the platform variant from Make.
+ modules[1].(*Module).Properties.HideFromMake = true
+ modules[1].(*Module).Properties.PreventInstall = true
+ }
+ } else {
+ // For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
+ // exposed to Make.
+ modules[0].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
+ // SDK variant is not supposed to be installed
+ modules[0].(*Module).Properties.PreventInstall = true
+ }
+ } else {
+ ccModule.Properties.Sdk_version = nil
+ ctx.CreateVariations("")
+ ctx.AliasVariation("")
+ }
} else {
- if m, ok := ctx.Module().(*Module); ok {
+ if isCcModule {
// Clear the sdk_version property for modules that don't have an SDK variant so
// later code doesn't get confused by it.
- m.Properties.Sdk_version = nil
+ ccModule.Properties.Sdk_version = nil
}
ctx.CreateVariations("")
ctx.AliasVariation("")
@@ -78,5 +108,12 @@
}
case *snapshotModule:
ctx.CreateVariations("")
+ case *CcApiVariant:
+ ccApiVariant, _ := ctx.Module().(*CcApiVariant)
+ if String(ccApiVariant.properties.Variant) == "ndk" {
+ ctx.CreateVariations("sdk")
+ } else {
+ ctx.CreateVariations("")
+ }
}
}
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
index 61925e3..790440c 100644
--- a/cc/sdk_test.go
+++ b/cc/sdk_test.go
@@ -101,3 +101,95 @@
assertDep(t, libsdkNDK, libcxxNDK)
assertDep(t, libsdkPlatform, libcxxPlatform)
}
+
+func TestMakeModuleNameForSdkVariant(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libfoo",
+ srcs: ["main_test.cpp"],
+ sdk_version: "current",
+ stl: "none",
+ }
+ `
+ platformVariant := "android_arm64_armv8-a_shared"
+ sdkVariant := "android_arm64_armv8-a_sdk_shared"
+ testCases := []struct {
+ name string
+ unbundledApps []string
+ variant string
+ skipInstall bool // soong skips install
+ hideFromMake bool // no make entry
+ makeUninstallable bool // make skips install
+ makeModuleName string
+ }{
+ {
+ name: "platform variant in normal builds",
+ unbundledApps: nil,
+ variant: platformVariant,
+ // installable in soong
+ skipInstall: false,
+ // visiable in Make as "libfoo"
+ hideFromMake: false,
+ makeModuleName: "libfoo",
+ // installable in Make
+ makeUninstallable: false,
+ },
+ {
+ name: "sdk variant in normal builds",
+ unbundledApps: nil,
+ variant: sdkVariant,
+ // soong doesn't install
+ skipInstall: true,
+ // visible in Make as "libfoo.sdk"
+ hideFromMake: false,
+ makeModuleName: "libfoo.sdk",
+ // but not installed
+ makeUninstallable: true,
+ },
+ {
+ name: "platform variant in unbunded builds",
+ unbundledApps: []string{"bar"},
+ variant: platformVariant,
+ // installable in soong
+ skipInstall: false,
+ // hidden from make
+ hideFromMake: true,
+ },
+ {
+ name: "sdk variant in unbunded builds",
+ unbundledApps: []string{"bar"},
+ variant: sdkVariant,
+ // soong doesn't install
+ skipInstall: true,
+ // visible in Make as "libfoo"
+ hideFromMake: false,
+ makeModuleName: "libfoo",
+ // but not installed
+ makeUninstallable: true,
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ fixture := android.GroupFixturePreparers(prepareForCcTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Unbundled_build_apps = tc.unbundledApps
+ }),
+ )
+ ctx := fixture.RunTestWithBp(t, bp).TestContext
+ module := ctx.ModuleForTests("libfoo", tc.variant).Module().(*Module)
+ android.AssertBoolEquals(t, "IsSkipInstall", tc.skipInstall, module.IsSkipInstall())
+ android.AssertBoolEquals(t, "HideFromMake", tc.hideFromMake, module.HiddenFromMake())
+ if !tc.hideFromMake {
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+ android.AssertStringEquals(t, "LOCAL_MODULE",
+ tc.makeModuleName, entries.EntryMap["LOCAL_MODULE"][0])
+ actualUninstallable := false
+ if actual, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok {
+ actualUninstallable = "true" == actual[0]
+ }
+ android.AssertBoolEquals(t, "LOCAL_UNINSTALLABLE_MODULE",
+ tc.makeUninstallable, actualUninstallable)
+ }
+ })
+ }
+}
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 792ffe3..bb6e257 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -18,6 +18,7 @@
// snapshot mutators and snapshot information maps which are also defined in this file.
import (
+ "fmt"
"strings"
"android/soong/android"
@@ -399,8 +400,10 @@
}
type snapshotSanitizer interface {
- isSanitizerEnabled(t SanitizerType) bool
+ isSanitizerAvailable(t SanitizerType) bool
setSanitizerVariation(t SanitizerType, enabled bool)
+ isSanitizerEnabled(t SanitizerType) bool
+ isUnsanitizedVariant() bool
}
type snapshotLibraryDecorator struct {
@@ -408,10 +411,13 @@
*libraryDecorator
properties SnapshotLibraryProperties
sanitizerProperties struct {
- CfiEnabled bool `blueprint:"mutated"`
+ SanitizerVariation SanitizerType `blueprint:"mutated"`
// Library flags for cfi variant.
Cfi SnapshotLibraryProperties `android:"arch_variant"`
+
+ // Library flags for hwasan variant.
+ Hwasan SnapshotLibraryProperties `android:"arch_variant"`
}
}
@@ -450,8 +456,10 @@
return p.libraryDecorator.link(ctx, flags, deps, objs)
}
- if p.sanitizerProperties.CfiEnabled {
+ if p.isSanitizerEnabled(cfi) {
p.properties = p.sanitizerProperties.Cfi
+ } else if p.isSanitizerEnabled(Hwasan) {
+ p.properties = p.sanitizerProperties.Hwasan
}
if !p.MatchesWithDevice(ctx.DeviceConfig()) {
@@ -514,25 +522,36 @@
return false
}
-func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
+var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
+
+func (p *snapshotLibraryDecorator) isSanitizerAvailable(t SanitizerType) bool {
switch t {
case cfi:
return p.sanitizerProperties.Cfi.Src != nil
+ case Hwasan:
+ return p.sanitizerProperties.Hwasan.Src != nil
default:
return false
}
}
func (p *snapshotLibraryDecorator) setSanitizerVariation(t SanitizerType, enabled bool) {
- if !enabled {
+ if !enabled || p.isSanitizerEnabled(t) {
return
}
- switch t {
- case cfi:
- p.sanitizerProperties.CfiEnabled = true
- default:
- return
+ if !p.isUnsanitizedVariant() {
+ panic(fmt.Errorf("snapshot Sanitizer must be one of Cfi or Hwasan but not both"))
}
+ p.sanitizerProperties.SanitizerVariation = t
+}
+
+func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
+ return p.sanitizerProperties.SanitizerVariation == t
+}
+
+func (p *snapshotLibraryDecorator) isUnsanitizedVariant() bool {
+ return !p.isSanitizerEnabled(Asan) &&
+ !p.isSanitizerEnabled(Hwasan)
}
func snapshotLibraryFactory(image SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
@@ -627,8 +646,6 @@
return module.Init()
}
-var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
-
// Module definitions for snapshots of executable binaries.
//
// Modules (vendor|recovery)_snapshot_binary are defined here. They have their prebuilt executable
@@ -784,6 +801,10 @@
prebuilt.Init(module, VendorSnapshotImageSingleton, snapshotObjectSuffix)
module.AddProperties(&prebuilt.properties)
+
+ // vendor_snapshot_object module does not provide sanitizer variants
+ module.sanitize.Properties.Sanitize.Never = BoolPtr(true)
+
return module.Init()
}
diff --git a/cc/stl.go b/cc/stl.go
index 0f2a878..f1433ef 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -25,12 +25,22 @@
return family
}
+func deduplicateStlInput(stl string) string {
+ switch stl {
+ case "c++_shared":
+ return "libc++"
+ case "c++_static":
+ return "libc++_static"
+ }
+ return stl
+}
+
func getNdkStlFamilyAndLinkType(m LinkableInterface) (string, string) {
stl := m.SelectedStl()
switch stl {
- case "ndk_libc++_shared":
+ case "ndk_libc++_shared", "libc++":
return "libc++", "shared"
- case "ndk_libc++_static":
+ case "ndk_libc++_static", "libc++_static":
return "libc++", "static"
case "ndk_system":
return "system", "shared"
@@ -66,18 +76,19 @@
} else if ctx.header() {
s = "none"
}
- if ctx.useSdk() && ctx.Device() {
+ if s == "none" {
+ return ""
+ }
+ s = deduplicateStlInput(s)
+ archHasNDKStl := ctx.Arch().ArchType != android.Riscv64
+ if ctx.useSdk() && ctx.Device() && archHasNDKStl {
switch s {
case "", "system":
return "ndk_system"
- case "c++_shared", "c++_static":
- return "ndk_lib" + s
case "libc++":
return "ndk_libc++_shared"
case "libc++_static":
return "ndk_libc++_static"
- case "none":
- return ""
default:
ctx.ModuleErrorf("stl: %q is not a supported STL with sdk_version set", s)
return ""
@@ -87,8 +98,6 @@
case "libc++", "libc++_static", "":
// Only use static libc++ for Windows.
return "libc++_static"
- case "none":
- return ""
default:
ctx.ModuleErrorf("stl: %q is not a supported STL for windows", s)
return ""
@@ -97,12 +106,6 @@
switch s {
case "libc++", "libc++_static":
return s
- case "c++_shared":
- return "libc++"
- case "c++_static":
- return "libc++_static"
- case "none":
- return ""
case "", "system":
if ctx.static() {
return "libc++_static"
@@ -137,6 +140,8 @@
return "libunwind"
}
+// Should be kept up to date with
+// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/cc/stl.bzl;l=46;drc=21771b671ae08565033768a6d3d151c54f887fa2
func (stl *stl) deps(ctx BaseModuleContext, deps Deps) Deps {
switch stl.Properties.SelectedStl {
case "libstdc++":
@@ -192,6 +197,8 @@
return deps
}
+// Should be kept up to date with
+// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/cc/stl.bzl;l=94;drc=5bc8e39d2637927dc57dd0850210d43d348a1341
func (stl *stl) flags(ctx ModuleContext, flags Flags) Flags {
switch stl.Properties.SelectedStl {
case "libc++", "libc++_static":
diff --git a/cc/stub_library.go b/cc/stub_library.go
index 76da782..f324dcc 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -73,7 +73,7 @@
func (s *stubLibraries) MakeVars(ctx android.MakeVarsContext) {
// Convert stub library file names into Makefile variable.
- ctx.Strict("STUB_LIBRARIES", strings.Join(android.SortedStringKeys(s.stubLibraryMap), " "))
+ ctx.Strict("STUB_LIBRARIES", strings.Join(android.SortedKeys(s.stubLibraryMap), " "))
// Export the list of API XML files to Make.
sort.Strings(s.apiListCoverageXmlPaths)
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index f8d1841..94c8567 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -41,6 +41,7 @@
ALL_ARCHITECTURES = (
Arch('arm'),
Arch('arm64'),
+ Arch('riscv64'),
Arch('x86'),
Arch('x86_64'),
)
@@ -78,12 +79,17 @@
@property
def has_mode_tags(self) -> bool:
"""Returns True if any mode tags (apex, llndk, etc) are set."""
- return self.has_apex_tags or self.has_llndk_tags
+ return self.has_apex_tags or self.has_llndk_tags or self.has_systemapi_tags
@property
def has_apex_tags(self) -> bool:
"""Returns True if any APEX tags are set."""
- return 'apex' in self.tags or 'systemapi' in self.tags
+ return 'apex' in self.tags
+
+ @property
+ def has_systemapi_tags(self) -> bool:
+ """Returns True if any APEX tags are set."""
+ return 'systemapi' in self.tags
@property
def has_llndk_tags(self) -> bool:
@@ -198,51 +204,65 @@
"""
return split_tag(tag)[1]
-
-def _should_omit_tags(tags: Tags, arch: Arch, api: int, llndk: bool,
- apex: bool) -> bool:
- """Returns True if the tagged object should be omitted.
-
- This defines the rules shared between version tagging and symbol tagging.
+class Filter:
+ """A filter encapsulates a condition that tells whether a version or a
+ symbol should be omitted or not
"""
- # The apex and llndk tags will only exclude APIs from other modes. If in
- # APEX or LLNDK mode and neither tag is provided, we fall back to the
- # default behavior because all NDK symbols are implicitly available to APEX
- # and LLNDK.
- if tags.has_mode_tags:
- if not apex and not llndk:
+
+ def __init__(self, arch: Arch, api: int, llndk: bool = False, apex: bool = False, systemapi:
+ bool = False, ndk: bool = True):
+ self.arch = arch
+ self.api = api
+ self.llndk = llndk
+ self.apex = apex
+ self.systemapi = systemapi
+ self.ndk = ndk
+
+ def _should_omit_tags(self, tags: Tags) -> bool:
+ """Returns True if the tagged object should be omitted.
+
+ This defines the rules shared between version tagging and symbol tagging.
+ """
+ # The apex and llndk tags will only exclude APIs from other modes. If in
+ # APEX or LLNDK mode and neither tag is provided, we fall back to the
+ # default behavior because all NDK symbols are implicitly available to
+ # APEX and LLNDK.
+ if tags.has_mode_tags:
+ if self.apex and tags.has_apex_tags:
+ return False
+ if self.llndk and tags.has_llndk_tags:
+ return False
+ if self.systemapi and tags.has_systemapi_tags:
+ return False
return True
- if apex and not tags.has_apex_tags:
+ if not symbol_in_arch(tags, self.arch):
return True
- if llndk and not tags.has_llndk_tags:
+ if not symbol_in_api(tags, self.arch, self.api):
return True
- if not symbol_in_arch(tags, arch):
- return True
- if not symbol_in_api(tags, arch, api):
- return True
- return False
+ return False
+ def should_omit_version(self, version: Version) -> bool:
+ """Returns True if the version section should be omitted.
-def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
- apex: bool) -> bool:
- """Returns True if the version section should be omitted.
+ We want to omit any sections that do not have any symbols we'll have in
+ the stub library. Sections that contain entirely future symbols or only
+ symbols for certain architectures.
+ """
+ if version.is_private:
+ return True
+ if version.tags.has_platform_only_tags:
+ return True
+ return self._should_omit_tags(version.tags)
- We want to omit any sections that do not have any symbols we'll have in the
- stub library. Sections that contain entirely future symbols or only symbols
- for certain architectures.
- """
- if version.is_private:
- return True
- if version.tags.has_platform_only_tags:
- return True
- return _should_omit_tags(version.tags, arch, api, llndk, apex)
+ def should_omit_symbol(self, symbol: Symbol) -> bool:
+ """Returns True if the symbol should be omitted."""
+ if not symbol.tags.has_mode_tags and not self.ndk:
+ # Symbols that don't have mode tags are NDK. They are usually
+ # included, but have to be omitted if NDK symbols are explicitly
+ # filtered-out
+ return True
-
-def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
- apex: bool) -> bool:
- """Returns True if the symbol should be omitted."""
- return _should_omit_tags(symbol.tags, arch, api, llndk, apex)
-
+ return self._should_omit_tags(symbol.tags)
def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
"""Returns true if the symbol is present for the given architecture."""
@@ -316,14 +336,10 @@
class SymbolFileParser:
"""Parses NDK symbol files."""
- def __init__(self, input_file: TextIO, api_map: ApiMap, arch: Arch,
- api: int, llndk: bool, apex: bool) -> None:
+ def __init__(self, input_file: TextIO, api_map: ApiMap, filt: Filter) -> None:
self.input_file = input_file
self.api_map = api_map
- self.arch = arch
- self.api = api
- self.llndk = llndk
- self.apex = apex
+ self.filter = filt
self.current_line: Optional[str] = None
def parse(self) -> List[Version]:
@@ -352,13 +368,11 @@
symbol_names = set()
multiply_defined_symbols = set()
for version in versions:
- if should_omit_version(version, self.arch, self.api, self.llndk,
- self.apex):
+ if self.filter.should_omit_version(version):
continue
for symbol in version.symbols:
- if should_omit_symbol(symbol, self.arch, self.api, self.llndk,
- self.apex):
+ if self.filter.should_omit_symbol(symbol):
continue
if symbol.name in symbol_names:
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index c1e8219..856b9d7 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -19,7 +19,8 @@
import unittest
import symbolfile
-from symbolfile import Arch, Tag, Tags, Version
+from symbolfile import Arch, Tag, Tags, Version, Symbol, Filter
+from copy import copy
# pylint: disable=missing-docstring
@@ -202,178 +203,202 @@
class OmitVersionTest(unittest.TestCase):
+ def setUp(self) -> None:
+ self.filter = Filter(arch = Arch('arm'), api = 9)
+ self.version = Version('foo', None, Tags(), [])
+
+ def assertOmit(self, f: Filter, v: Version) -> None:
+ self.assertTrue(f.should_omit_version(v))
+
+ def assertInclude(self, f: Filter, v: Version) -> None:
+ self.assertFalse(f.should_omit_version(v))
+
def test_omit_private(self) -> None:
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
- False, False))
+ f = self.filter
+ v = self.version
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo_PRIVATE', None, Tags(), []),
- Arch('arm'), 9, False, False))
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo_PLATFORM', None, Tags(), []),
- Arch('arm'), 9, False, False))
+ self.assertInclude(f, v)
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None,
- Tags.from_strs(['platform-only']), []),
- Arch('arm'), 9, False, False))
+ v.name = 'foo_PRIVATE'
+ self.assertOmit(f, v)
+
+ v.name = 'foo_PLATFORM'
+ self.assertOmit(f, v)
+
+ v.name = 'foo'
+ v.tags = Tags.from_strs(['platform-only'])
+ self.assertOmit(f, v)
def test_omit_llndk(self) -> None:
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
- Arch('arm'), 9, False, False))
+ f = self.filter
+ v = self.version
+ v_llndk = copy(v)
+ v_llndk.tags = Tags.from_strs(['llndk'])
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
- True, False))
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
- Arch('arm'), 9, True, False))
+ self.assertOmit(f, v_llndk)
+
+ f.llndk = True
+ self.assertInclude(f, v)
+ self.assertInclude(f, v_llndk)
def test_omit_apex(self) -> None:
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
- Arch('arm'), 9, False, False))
+ f = self.filter
+ v = self.version
+ v_apex = copy(v)
+ v_apex.tags = Tags.from_strs(['apex'])
+ v_systemapi = copy(v)
+ v_systemapi.tags = Tags.from_strs(['systemapi'])
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
- False, True))
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
- Arch('arm'), 9, False, True))
+ self.assertOmit(f, v_apex)
+
+ f.apex = True
+ self.assertInclude(f, v)
+ self.assertInclude(f, v_apex)
+ self.assertOmit(f, v_systemapi)
def test_omit_systemapi(self) -> None:
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
- []), Arch('arm'), 9, False, False))
+ f = self.filter
+ v = self.version
+ v_apex = copy(v)
+ v_apex.tags = Tags.from_strs(['apex'])
+ v_systemapi = copy(v)
+ v_systemapi.tags = Tags.from_strs(['systemapi'])
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
- False, True))
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
- []), Arch('arm'), 9, False, True))
+ self.assertOmit(f, v_systemapi)
+
+ f.systemapi = True
+ self.assertInclude(f, v)
+ self.assertInclude(f, v_systemapi)
+ self.assertOmit(f, v_apex)
def test_omit_arch(self) -> None:
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
- False, False))
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags.from_strs(['arm']), []),
- Arch('arm'), 9, False, False))
+ f_arm = self.filter
+ v_none = self.version
+ self.assertInclude(f_arm, v_none)
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags.from_strs(['x86']), []),
- Arch('arm'), 9, False, False))
+ v_arm = copy(v_none)
+ v_arm.tags = Tags.from_strs(['arm'])
+ self.assertInclude(f_arm, v_arm)
+
+ v_x86 = copy(v_none)
+ v_x86.tags = Tags.from_strs(['x86'])
+ self.assertOmit(f_arm, v_x86)
def test_omit_api(self) -> None:
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
- False, False))
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None,
- Tags.from_strs(['introduced=9']), []),
- Arch('arm'), 9, False, False))
+ f_api9 = self.filter
+ v_none = self.version
+ self.assertInclude(f_api9, v_none)
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None,
- Tags.from_strs(['introduced=14']), []),
- Arch('arm'), 9, False, False))
+ v_api9 = copy(v_none)
+ v_api9.tags = Tags.from_strs(['introduced=9'])
+ self.assertInclude(f_api9, v_api9)
+
+ v_api14 = copy(v_none)
+ v_api14.tags = Tags.from_strs(['introduced=14'])
+ self.assertOmit(f_api9, v_api14)
class OmitSymbolTest(unittest.TestCase):
- def test_omit_llndk(self) -> None:
- self.assertTrue(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
- Arch('arm'), 9, False, False))
+ def setUp(self) -> None:
+ self.filter = Filter(arch = Arch('arm'), api = 9)
- self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
- Arch('arm'), 9, True, False))
- self.assertFalse(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
- Arch('arm'), 9, True, False))
+ def assertOmit(self, f: Filter, s: Symbol) -> None:
+ self.assertTrue(f.should_omit_symbol(s))
+
+ def assertInclude(self, f: Filter, s: Symbol) -> None:
+ self.assertFalse(f.should_omit_symbol(s))
+
+ def test_omit_ndk(self) -> None:
+ f_ndk = self.filter
+ f_nondk = copy(f_ndk)
+ f_nondk.ndk = False
+ f_nondk.apex = True
+
+ s_ndk = Symbol('foo', Tags())
+ s_nonndk = Symbol('foo', Tags.from_strs(['apex']))
+
+ self.assertInclude(f_ndk, s_ndk)
+ self.assertOmit(f_ndk, s_nonndk)
+ self.assertOmit(f_nondk, s_ndk)
+ self.assertInclude(f_nondk, s_nonndk)
+
+ def test_omit_llndk(self) -> None:
+ f_none = self.filter
+ f_llndk = copy(f_none)
+ f_llndk.llndk = True
+
+ s_none = Symbol('foo', Tags())
+ s_llndk = Symbol('foo', Tags.from_strs(['llndk']))
+
+ self.assertOmit(f_none, s_llndk)
+ self.assertInclude(f_llndk, s_none)
+ self.assertInclude(f_llndk, s_llndk)
def test_omit_apex(self) -> None:
- self.assertTrue(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
- Arch('arm'), 9, False, False))
+ f_none = self.filter
+ f_apex = copy(f_none)
+ f_apex.apex = True
- self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
- Arch('arm'), 9, False, True))
- self.assertFalse(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
- Arch('arm'), 9, False, True))
+ s_none = Symbol('foo', Tags())
+ s_apex = Symbol('foo', Tags.from_strs(['apex']))
+ s_systemapi = Symbol('foo', Tags.from_strs(['systemapi']))
+
+ self.assertOmit(f_none, s_apex)
+ self.assertInclude(f_apex, s_none)
+ self.assertInclude(f_apex, s_apex)
+ self.assertOmit(f_apex, s_systemapi)
def test_omit_systemapi(self) -> None:
- self.assertTrue(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
- Arch('arm'), 9, False, False))
+ f_none = self.filter
+ f_systemapi = copy(f_none)
+ f_systemapi.systemapi = True
- self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
- Arch('arm'), 9, False, True))
- self.assertFalse(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
- Arch('arm'), 9, False, True))
+ s_none = Symbol('foo', Tags())
+ s_apex = Symbol('foo', Tags.from_strs(['apex']))
+ s_systemapi = Symbol('foo', Tags.from_strs(['systemapi']))
+
+ self.assertOmit(f_none, s_systemapi)
+ self.assertInclude(f_systemapi, s_none)
+ self.assertInclude(f_systemapi, s_systemapi)
+ self.assertOmit(f_systemapi, s_apex)
+
+ def test_omit_apex_and_systemapi(self) -> None:
+ f = self.filter
+ f.systemapi = True
+ f.apex = True
+
+ s_none = Symbol('foo', Tags())
+ s_apex = Symbol('foo', Tags.from_strs(['apex']))
+ s_systemapi = Symbol('foo', Tags.from_strs(['systemapi']))
+ self.assertInclude(f, s_none)
+ self.assertInclude(f, s_apex)
+ self.assertInclude(f, s_systemapi)
def test_omit_arch(self) -> None:
- self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
- Arch('arm'), 9, False, False))
- self.assertFalse(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['arm'])), Arch('arm'),
- 9, False, False))
+ f_arm = self.filter
+ s_none = Symbol('foo', Tags())
+ s_arm = Symbol('foo', Tags.from_strs(['arm']))
+ s_x86 = Symbol('foo', Tags.from_strs(['x86']))
- self.assertTrue(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['x86'])), Arch('arm'),
- 9, False, False))
+ self.assertInclude(f_arm, s_none)
+ self.assertInclude(f_arm, s_arm)
+ self.assertOmit(f_arm, s_x86)
def test_omit_api(self) -> None:
- self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
- Arch('arm'), 9, False, False))
- self.assertFalse(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['introduced=9'])),
- Arch('arm'), 9, False, False))
+ f_api9 = self.filter
+ s_none = Symbol('foo', Tags())
+ s_api9 = Symbol('foo', Tags.from_strs(['introduced=9']))
+ s_api14 = Symbol('foo', Tags.from_strs(['introduced=14']))
- self.assertTrue(
- symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
- Arch('arm'), 9, False, False))
+ self.assertInclude(f_api9, s_none)
+ self.assertInclude(f_api9, s_api9)
+ self.assertOmit(f_api9, s_api14)
class SymbolFileParseTest(unittest.TestCase):
+ def setUp(self) -> None:
+ self.filter = Filter(arch = Arch('arm'), api = 16)
+
def test_next_line(self) -> None:
input_file = io.StringIO(textwrap.dedent("""\
foo
@@ -382,8 +407,7 @@
# baz
qux
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
self.assertIsNone(parser.current_line)
self.assertEqual('foo', parser.next_line().strip())
@@ -409,8 +433,7 @@
VERSION_2 {
} VERSION_1; # asdf
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
parser.next_line()
version = parser.parse_version()
@@ -419,8 +442,8 @@
self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags)
expected_symbols = [
- symbolfile.Symbol('baz', Tags()),
- symbolfile.Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
+ Symbol('baz', Tags()),
+ Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
]
self.assertEqual(expected_symbols, version.symbols)
@@ -434,8 +457,7 @@
input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
parser.next_line()
with self.assertRaises(symbolfile.ParseError):
parser.parse_version()
@@ -446,8 +468,7 @@
foo:
}
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
parser.next_line()
with self.assertRaises(symbolfile.ParseError):
parser.parse_version()
@@ -457,8 +478,7 @@
foo;
bar; # baz qux
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
parser.next_line()
symbol = parser.parse_symbol()
@@ -476,8 +496,7 @@
*;
};
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
parser.next_line()
with self.assertRaises(symbolfile.ParseError):
parser.parse_version()
@@ -489,8 +508,7 @@
*;
};
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
parser.next_line()
version = parser.parse_version()
self.assertEqual([], version.symbols)
@@ -501,8 +519,7 @@
foo
};
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
parser.next_line()
with self.assertRaises(symbolfile.ParseError):
parser.parse_version()
@@ -510,8 +527,7 @@
def test_parse_fails_invalid_input(self) -> None:
with self.assertRaises(symbolfile.ParseError):
input_file = io.StringIO('foo')
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
- 16, False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
parser.parse()
def test_parse(self) -> None:
@@ -532,19 +548,18 @@
qwerty;
} VERSION_1;
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, False)
+ parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
versions = parser.parse()
expected = [
symbolfile.Version('VERSION_1', None, Tags(), [
- symbolfile.Symbol('foo', Tags()),
- symbolfile.Symbol('bar', Tags.from_strs(['baz'])),
+ Symbol('foo', Tags()),
+ Symbol('bar', Tags.from_strs(['baz'])),
]),
symbolfile.Version(
'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [
- symbolfile.Symbol('woodly', Tags()),
- symbolfile.Symbol('doodly', Tags.from_strs(['asdf'])),
+ Symbol('woodly', Tags()),
+ Symbol('doodly', Tags.from_strs(['asdf'])),
]),
]
@@ -559,8 +574,9 @@
qux; # apex
};
"""))
- parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
- False, True)
+ f = copy(self.filter)
+ f.llndk = True
+ parser = symbolfile.SymbolFileParser(input_file, {}, f)
parser.next_line()
version = parser.parse_version()
@@ -568,10 +584,10 @@
self.assertIsNone(version.base)
expected_symbols = [
- symbolfile.Symbol('foo', Tags()),
- symbolfile.Symbol('bar', Tags.from_strs(['llndk'])),
- symbolfile.Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
- symbolfile.Symbol('qux', Tags.from_strs(['apex'])),
+ Symbol('foo', Tags()),
+ Symbol('bar', Tags.from_strs(['llndk'])),
+ Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
+ Symbol('qux', Tags.from_strs(['apex'])),
]
self.assertEqual(expected_symbols, version.symbols)
diff --git a/cc/sysprop.go b/cc/sysprop.go
index f578b50..0df290a 100644
--- a/cc/sysprop.go
+++ b/cc/sysprop.go
@@ -14,56 +14,64 @@
package cc
-// This file contains a map to redirect dependencies towards sysprop_library.
-// As sysprop_library has to support both Java and C++, sysprop_library internally
-// generates cc_library and java_library. For example, the following sysprop_library
-//
-// sysprop_library {
-// name: "foo",
-// }
-//
-// will internally generate with prefix "lib"
-//
-// cc_library {
-// name: "libfoo",
-// }
-//
-// When a cc module links against "foo", build system will redirect the
-// dependency to "libfoo". To do that, SyspropMutator gathers all sysprop_library,
-// records their cc implementation library names to a map. The map will be used in
-// cc.Module.DepsMutator.
-
import (
- "sync"
-
"android/soong/android"
+ "android/soong/bazel"
)
-type syspropLibraryInterface interface {
- BaseModuleName() string
- CcImplementationModuleName() string
+// TODO(b/240463568): Additional properties will be added for API validation
+type bazelSyspropLibraryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Tags bazel.StringListAttribute
}
-var (
- syspropImplLibrariesKey = android.NewOnceKey("syspropImplLibirares")
- syspropImplLibrariesLock sync.Mutex
-)
-
-func syspropImplLibraries(config android.Config) map[string]string {
- return config.Once(syspropImplLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
+type bazelCcSyspropLibraryAttributes struct {
+ Dep bazel.LabelAttribute
+ Min_sdk_version *string
+ Tags bazel.StringListAttribute
}
-// gather list of sysprop libraries
-func SyspropMutator(mctx android.BottomUpMutatorContext) {
- if m, ok := mctx.Module().(syspropLibraryInterface); ok {
- syspropImplLibraries := syspropImplLibraries(mctx.Config())
- syspropImplLibrariesLock.Lock()
- defer syspropImplLibrariesLock.Unlock()
+type SyspropLibraryLabels struct {
+ SyspropLibraryLabel string
+ SharedLibraryLabel string
+ StaticLibraryLabel string
+}
- // BaseModuleName is the name of sysprop_library
- // CcImplementationModuleName is the name of cc_library generated by sysprop_library
- syspropImplLibraries[m.BaseModuleName()] = m.CcImplementationModuleName()
+func Bp2buildSysprop(ctx android.Bp2buildMutatorContext, labels SyspropLibraryLabels, srcs bazel.LabelListAttribute, minSdkVersion *string) {
+ apexAvailableTags := android.ApexAvailableTags(ctx.Module())
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "sysprop_library",
+ Bzl_load_location: "//build/bazel/rules/sysprop:sysprop_library.bzl",
+ },
+ android.CommonAttributes{Name: labels.SyspropLibraryLabel},
+ &bazelSyspropLibraryAttributes{
+ Srcs: srcs,
+ Tags: apexAvailableTags,
+ },
+ )
+
+ attrs := &bazelCcSyspropLibraryAttributes{
+ Dep: *bazel.MakeLabelAttribute(":" + labels.SyspropLibraryLabel),
+ Min_sdk_version: minSdkVersion,
+ Tags: apexAvailableTags,
}
+
+ if labels.SharedLibraryLabel != "" {
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_sysprop_library_shared",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_sysprop_library.bzl",
+ },
+ android.CommonAttributes{Name: labels.SharedLibraryLabel},
+ attrs)
+ }
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_sysprop_library_static",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_sysprop_library.bzl",
+ },
+ android.CommonAttributes{Name: labels.StaticLibraryLabel},
+ attrs)
}
diff --git a/cc/test.go b/cc/test.go
index ead7877..27de06b 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -22,6 +22,8 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
+ "android/soong/bazel/cquery"
"android/soong/tradefed"
)
@@ -30,7 +32,8 @@
// 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
}
@@ -42,6 +45,8 @@
// Test option struct.
type TestOptions struct {
+ android.CommonTestOptions
+
// The UID that you want to run the test as on a device.
Run_test_as *string
@@ -51,9 +56,6 @@
// a list of extra test configuration files that should be installed with the module.
Extra_test_configs []string `android:"path,arch_variant"`
- // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
- Unit_test *bool
-
// Add ShippingApiLevelModuleController to auto generated test config. If the device properties
// for the shipping api level is less than the min_shipping_api_level, skip this module.
Min_shipping_api_level *int64
@@ -133,7 +135,8 @@
// specific functionality on a device. The executable binary gets an implicit
// static_libs dependency on libgtests unless the gtest flag is set to false.
func TestFactory() android.Module {
- module := NewTest(android.HostAndDeviceSupported)
+ module := NewTest(android.HostAndDeviceSupported, true)
+ module.bazelHandler = &ccTestBazelHandler{module: module}
return module.Init()
}
@@ -156,7 +159,7 @@
// cc_test_host compiles a test host binary.
func TestHostFactory() android.Module {
- module := NewTest(android.HostSupported)
+ module := NewTest(android.HostSupported, true)
return module.Init()
}
@@ -204,6 +207,10 @@
test.binaryDecorator.Properties.Stem = StringPtr("")
}
+func (test *testBinary) testBinary() bool {
+ return true
+}
+
var _ testPerSrc = (*testBinary)(nil)
func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
@@ -256,10 +263,11 @@
return BoolDefault(test.LinkerProperties.Gtest, true)
}
-func (test *testDecorator) testBinary() bool {
- return true
+func (test *testDecorator) isolated(ctx BaseModuleContext) bool {
+ return BoolDefault(test.LinkerProperties.Isolated, false)
}
+// NOTE: Keep this in sync with cc/cc_test.bzl#gtest_copts
func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
if !test.gtest() {
return flags
@@ -288,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.LinkerProperties.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
@@ -303,23 +311,6 @@
return deps
}
-func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
- // 1. Add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
- // find out/host/linux-x86/lib[64]/library.so
- // 2. Add ../../../lib[64] to rpath so that out/host/linux-x86/testcases/<test dir>/<CPU>/<test> can
- // also find out/host/linux-x86/lib[64]/library.so
- runpaths := []string{"../../lib", "../../../lib"}
- for _, runpath := range runpaths {
- if ctx.toolchain().Is64Bit() {
- runpath += "64"
- }
- linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
- }
-
- // add "" to rpath so that test binaries can find libraries in their own test directory
- linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "")
-}
-
func (test *testDecorator) linkerProps() []interface{} {
return []interface{}{&test.LinkerProperties}
}
@@ -348,11 +339,6 @@
return props
}
-func (test *testBinary) linkerInit(ctx BaseModuleContext) {
- test.testDecorator.linkerInit(ctx, test.binaryDecorator.baseLinker)
- test.binaryDecorator.linkerInit(ctx)
-}
-
func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
deps = test.testDecorator.linkerDeps(ctx, deps)
deps = test.binaryDecorator.linkerDeps(ctx, deps)
@@ -372,12 +358,6 @@
}
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"
- if ctx.inVendor() || ctx.useVndk() {
- testInstallBase = "/data/local/tests/vendor"
- }
-
dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
for _, dataSrcPath := range dataSrcPaths {
@@ -409,52 +389,20 @@
}
})
- var configs []tradefed.Config
- for _, module := range test.Properties.Test_mainline_modules {
- configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
- }
- if Bool(test.Properties.Require_root) {
- configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
- } else {
- var options []tradefed.Option
- options = append(options, tradefed.Option{Name: "force-root", Value: "false"})
- configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
- }
- if Bool(test.Properties.Disable_framework) {
- var options []tradefed.Option
- configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.StopServicesSetup", options})
- }
- if Bool(test.testDecorator.LinkerProperties.Isolated) {
- configs = append(configs, tradefed.Option{Name: "not-shardable", Value: "true"})
- }
- if test.Properties.Test_options.Run_test_as != nil {
- configs = append(configs, tradefed.Option{Name: "run-test-as", Value: String(test.Properties.Test_options.Run_test_as)})
- }
- for _, tag := range test.Properties.Test_options.Test_suite_tag {
- configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: tag})
- }
- if test.Properties.Test_options.Min_shipping_api_level != nil {
- if test.Properties.Test_options.Vsr_min_shipping_api_level != nil {
- ctx.PropertyErrorf("test_options.min_shipping_api_level", "must not be set at the same time as 'vsr_min_shipping_api_level'.")
- }
- var options []tradefed.Option
- options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Min_shipping_api_level), 10)})
- configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
- }
- if test.Properties.Test_options.Vsr_min_shipping_api_level != nil {
- var options []tradefed.Option
- options = append(options, tradefed.Option{Name: "vsr-min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Vsr_min_shipping_api_level), 10)})
- configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
- }
- if test.Properties.Test_options.Min_vndk_version != nil {
- var options []tradefed.Option
- options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Min_vndk_version), 10)})
- options = append(options, tradefed.Option{Name: "api-level-prop", Value: "ro.vndk.version"})
- configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.MinApiLevelModuleController", options})
- }
+ useVendor := ctx.inVendor() || ctx.useVndk()
+ testInstallBase := getTestInstallBase(useVendor)
+ configs := getTradefedConfigOptions(ctx, &test.Properties, test.isolated(ctx))
- test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
- test.Properties.Test_config_template, test.testDecorator.InstallerProperties.Test_suites, configs, test.Properties.Auto_gen_config, testInstallBase)
+ test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: test.Properties.Test_config,
+ TestConfigTemplateProp: test.Properties.Test_config_template,
+ TestSuites: test.testDecorator.InstallerProperties.Test_suites,
+ Config: configs,
+ AutoGenConfig: test.Properties.Auto_gen_config,
+ TestInstallBase: testInstallBase,
+ DeviceTemplate: "${NativeTestConfigTemplate}",
+ HostTemplate: "${NativeHostTestConfigTemplate}",
+ })
test.extraTestConfigs = android.PathsForModuleSrc(ctx, test.Properties.Test_options.Extra_test_configs)
@@ -473,8 +421,66 @@
test.binaryDecorator.baseInstaller.install(ctx, file)
}
-func NewTest(hod android.HostOrDeviceSupported) *Module {
- module, binary := newBinary(hod, false)
+func getTestInstallBase(useVendor bool) string {
+ // TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base.
+ testInstallBase := "/data/local/tmp"
+ if useVendor {
+ testInstallBase = "/data/local/tests/vendor"
+ }
+ return testInstallBase
+}
+
+func getTradefedConfigOptions(ctx android.EarlyModuleContext, properties *TestBinaryProperties, isolated bool) []tradefed.Config {
+ var configs []tradefed.Config
+
+ for _, module := range properties.Test_mainline_modules {
+ configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
+ }
+ if Bool(properties.Require_root) {
+ configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
+ } else {
+ var options []tradefed.Option
+ options = append(options, tradefed.Option{Name: "force-root", Value: "false"})
+ configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
+ }
+ if Bool(properties.Disable_framework) {
+ var options []tradefed.Option
+ configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.StopServicesSetup", options})
+ }
+ if isolated {
+ configs = append(configs, tradefed.Option{Name: "not-shardable", Value: "true"})
+ }
+ if properties.Test_options.Run_test_as != nil {
+ configs = append(configs, tradefed.Option{Name: "run-test-as", Value: String(properties.Test_options.Run_test_as)})
+ }
+ for _, tag := range properties.Test_options.Test_suite_tag {
+ configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: tag})
+ }
+ if properties.Test_options.Min_shipping_api_level != nil {
+ if properties.Test_options.Vsr_min_shipping_api_level != nil {
+ ctx.PropertyErrorf("test_options.min_shipping_api_level", "must not be set at the same time as 'vsr_min_shipping_api_level'.")
+ }
+ var options []tradefed.Option
+ options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*properties.Test_options.Min_shipping_api_level), 10)})
+ configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
+ }
+ if properties.Test_options.Vsr_min_shipping_api_level != nil {
+ var options []tradefed.Option
+ options = append(options, tradefed.Option{Name: "vsr-min-api-level", Value: strconv.FormatInt(int64(*properties.Test_options.Vsr_min_shipping_api_level), 10)})
+ configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
+ }
+ if properties.Test_options.Min_vndk_version != nil {
+ var options []tradefed.Option
+ options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*properties.Test_options.Min_vndk_version), 10)})
+ options = append(options, tradefed.Option{Name: "api-level-prop", Value: "ro.vndk.version"})
+ configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.MinApiLevelModuleController", options})
+ }
+ return configs
+}
+
+func NewTest(hod android.HostOrDeviceSupported, bazelable bool) *Module {
+ module, binary := newBinary(hod, bazelable)
+ module.bazelable = bazelable
module.multilib = android.MultilibBoth
binary.baseInstaller = NewTestInstaller()
@@ -497,17 +503,16 @@
*libraryDecorator
}
+func (test *testLibrary) testLibrary() bool {
+ return true
+}
+
func (test *testLibrary) linkerProps() []interface{} {
var props []interface{}
props = append(props, test.testDecorator.linkerProps()...)
return append(props, test.libraryDecorator.linkerProps()...)
}
-func (test *testLibrary) linkerInit(ctx BaseModuleContext) {
- test.testDecorator.linkerInit(ctx, test.libraryDecorator.baseLinker)
- test.libraryDecorator.linkerInit(ctx)
-}
-
func (test *testLibrary) linkerDeps(ctx DepsContext, deps Deps) Deps {
deps = test.testDecorator.linkerDeps(ctx, deps)
deps = test.libraryDecorator.linkerDeps(ctx, deps)
@@ -536,6 +541,7 @@
}
module.linker = test
module.installer = test
+ module.bazelable = true
return module
}
@@ -577,15 +583,6 @@
return true
}
-func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
- runpath := "../../lib"
- if ctx.toolchain().Is64Bit() {
- runpath += "64"
- }
- benchmark.baseLinker.dynamicProperties.RunPaths = append(benchmark.baseLinker.dynamicProperties.RunPaths, runpath)
- benchmark.binaryDecorator.linkerInit(ctx)
-}
-
func (benchmark *benchmarkDecorator) linkerProps() []interface{} {
props := benchmark.binaryDecorator.linkerProps()
props = append(props, &benchmark.Properties)
@@ -605,8 +602,15 @@
if Bool(benchmark.Properties.Require_root) {
configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
}
- benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
- benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs, benchmark.Properties.Auto_gen_config)
+ benchmark.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: benchmark.Properties.Test_config,
+ TestConfigTemplateProp: benchmark.Properties.Test_config_template,
+ TestSuites: benchmark.Properties.Test_suites,
+ Config: configs,
+ AutoGenConfig: benchmark.Properties.Auto_gen_config,
+ DeviceTemplate: "${NativeBenchmarkTestConfigTemplate}",
+ HostTemplate: "${NativeBenchmarkTestConfigTemplate}",
+ })
benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
@@ -625,3 +629,116 @@
module.installer = benchmark
return module
}
+
+type ccTestBazelHandler struct {
+ module *Module
+}
+
+var _ BazelHandler = (*ccTestBazelHandler)(nil)
+
+func (handler *ccTestBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(label, cquery.GetCcUnstrippedInfo, android.GetConfigKey(ctx))
+}
+
+func (handler *ccTestBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ bazelCtx := ctx.Config().BazelContext
+ info, err := bazelCtx.GetCcUnstrippedInfo(label, android.GetConfigKey(ctx))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
+ }
+
+ var outputFilePath android.Path = android.PathForBazelOut(ctx, info.OutputFile)
+ if len(info.TidyFiles) > 0 {
+ handler.module.tidyFiles = android.PathsForBazelOut(ctx, info.TidyFiles)
+ outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+ }
+ handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+ handler.module.linker.(*testBinary).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+ handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
+}
+
+// binaryAttributes contains Bazel attributes corresponding to a cc test
+type testBinaryAttributes struct {
+ binaryAttributes
+
+ Gtest bool
+ Isolated bool
+
+ tidyAttributes
+ tradefed.TestConfigAttributes
+}
+
+// testBinaryBp2build is the bp2build converter for cc_test modules. A cc_test's
+// dependency graph and compilation/linking steps are functionally similar to a
+// cc_binary, but has additional dependencies on test deps like gtest, and
+// produces additional runfiles like XML plans for Tradefed orchestration
+//
+// TODO(b/244432609): handle `isolated` property.
+// TODO(b/244432134): handle custom runpaths for tests that assume runfile layouts not
+// default to bazel. (see linkerInit function)
+func testBinaryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+ var testBinaryAttrs testBinaryAttributes
+ testBinaryAttrs.binaryAttributes = binaryBp2buildAttrs(ctx, m)
+
+ var data bazel.LabelListAttribute
+ var tags bazel.StringListAttribute
+
+ testBinaryProps := m.GetArchVariantProperties(ctx, &TestBinaryProperties{})
+ for axis, configToProps := range testBinaryProps {
+ for config, props := range configToProps {
+ if p, ok := props.(*TestBinaryProperties); ok {
+ // Combine data, data_bins and data_libs into a single 'data' attribute.
+ var combinedData bazel.LabelList
+ combinedData.Append(android.BazelLabelForModuleSrc(ctx, p.Data))
+ combinedData.Append(android.BazelLabelForModuleDeps(ctx, p.Data_bins))
+ combinedData.Append(android.BazelLabelForModuleDeps(ctx, p.Data_libs))
+ data.SetSelectValue(axis, config, combinedData)
+ tags.SetSelectValue(axis, config, p.Test_options.Tags)
+ }
+ }
+ }
+
+ m.convertTidyAttributes(ctx, &testBinaryAttrs.tidyAttributes)
+
+ for _, propIntf := range m.GetProperties() {
+ if testLinkerProps, ok := propIntf.(*TestLinkerProperties); ok {
+ testBinaryAttrs.Gtest = proptools.BoolDefault(testLinkerProps.Gtest, true)
+ testBinaryAttrs.Isolated = proptools.BoolDefault(testLinkerProps.Isolated, true)
+ break
+ }
+ }
+
+ for _, testProps := range m.GetProperties() {
+ if p, ok := testProps.(*TestBinaryProperties); ok {
+ useVendor := false // TODO Bug: 262914724
+ testInstallBase := getTestInstallBase(useVendor)
+ testConfigAttributes := tradefed.GetTestConfigAttributes(
+ ctx,
+ p.Test_config,
+ p.Test_options.Extra_test_configs,
+ p.Auto_gen_config,
+ p.Test_options.Test_suite_tag,
+ p.Test_config_template,
+ getTradefedConfigOptions(ctx, p, testBinaryAttrs.Isolated),
+ &testInstallBase,
+ )
+ testBinaryAttrs.TestConfigAttributes = testConfigAttributes
+ }
+ }
+
+ // TODO (b/262914724): convert to tradefed_cc_test and tradefed_cc_test_host
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_test",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_test.bzl",
+ },
+ android.CommonAttributes{
+ Name: m.Name(),
+ Data: data,
+ Tags: tags,
+ },
+ &testBinaryAttrs)
+}
diff --git a/cc/testing.go b/cc/testing.go
index 32f7c60..ced0929 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -15,11 +15,13 @@
package cc
import (
+ "encoding/json"
"path/filepath"
"testing"
"android/soong/android"
"android/soong/genrule"
+ "android/soong/multitree"
"android/soong/snapshot"
)
@@ -29,14 +31,17 @@
RegisterBinaryBuildComponents(ctx)
RegisterLibraryBuildComponents(ctx)
RegisterLibraryHeadersBuildComponents(ctx)
+ RegisterLibraryStubBuildComponents(ctx)
+
+ multitree.RegisterApiImportsModule(ctx)
ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
ctx.RegisterModuleType("cc_object", ObjectFactory)
ctx.RegisterModuleType("cc_genrule", GenRuleFactory)
ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
ctx.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
- ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
+ ctx.RegisterModuleType("ndk_headers", ndkHeadersFactory)
}
func GatherRequiredDepsForTest(oses ...android.OsType) string {
@@ -64,6 +69,7 @@
return `
cc_defaults {
name: "toolchain_libs_defaults",
+ host_supported: true,
vendor_available: true,
product_available: true,
recovery_available: true,
@@ -72,7 +78,6 @@
nocrt: true,
system_shared_libs: [],
stl: "none",
- srcs: [""],
check_elf_files: false,
sanitize: {
never: true,
@@ -83,6 +88,7 @@
name: "libcompiler_rt-extras",
defaults: ["toolchain_libs_defaults"],
vendor_ramdisk_available: true,
+ srcs: [""],
}
cc_prebuilt_library_static {
@@ -92,11 +98,13 @@
vendor_available: true,
vendor_ramdisk_available: true,
native_bridge_supported: true,
+ srcs: [""],
}
cc_prebuilt_library_shared {
name: "libclang_rt.hwasan",
defaults: ["toolchain_libs_defaults"],
+ srcs: [""],
}
cc_prebuilt_library_static {
@@ -107,6 +115,7 @@
],
vendor_ramdisk_available: true,
native_bridge_supported: true,
+ srcs: [""],
}
cc_prebuilt_library_static {
@@ -115,17 +124,46 @@
"linux_bionic_supported",
"toolchain_libs_defaults",
],
+ srcs: [""],
}
// Needed for sanitizer
cc_prebuilt_library_shared {
name: "libclang_rt.ubsan_standalone",
defaults: ["toolchain_libs_defaults"],
+ srcs: [""],
+ }
+
+ cc_prebuilt_library_static {
+ name: "libclang_rt.ubsan_standalone.static",
+ defaults: ["toolchain_libs_defaults"],
+ srcs: [""],
}
cc_prebuilt_library_static {
name: "libclang_rt.ubsan_minimal",
defaults: ["toolchain_libs_defaults"],
+ host_supported: true,
+ target: {
+ android_arm64: {
+ srcs: ["libclang_rt.ubsan_minimal.android_arm64.a"],
+ },
+ android_arm: {
+ srcs: ["libclang_rt.ubsan_minimal.android_arm.a"],
+ },
+ linux_glibc_x86_64: {
+ srcs: ["libclang_rt.ubsan_minimal.x86_64.a"],
+ },
+ linux_glibc_x86: {
+ srcs: ["libclang_rt.ubsan_minimal.x86.a"],
+ },
+ linux_musl_x86_64: {
+ srcs: ["libclang_rt.ubsan_minimal.x86_64.a"],
+ },
+ linux_musl_x86: {
+ srcs: ["libclang_rt.ubsan_minimal.x86.a"],
+ },
+ },
}
cc_library {
@@ -173,7 +211,6 @@
native_coverage: false,
system_shared_libs: [],
stl: "none",
- notice: "custom_notice",
}
cc_library {
name: "libprofile-clang-extras",
@@ -184,7 +221,6 @@
native_coverage: false,
system_shared_libs: [],
stl: "none",
- notice: "custom_notice",
}
cc_library {
name: "libprofile-extras_ndk",
@@ -193,7 +229,6 @@
native_coverage: false,
system_shared_libs: [],
stl: "none",
- notice: "custom_notice",
sdk_version: "current",
}
cc_library {
@@ -203,7 +238,6 @@
native_coverage: false,
system_shared_libs: [],
stl: "none",
- notice: "custom_notice",
sdk_version: "current",
}
@@ -513,7 +547,7 @@
android.PrepareForTestWithAndroidBuildComponents,
android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
+ ctx.RegisterModuleType("cc_fuzz", LibFuzzFactory)
ctx.RegisterModuleType("cc_test", TestFactory)
ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
@@ -545,6 +579,11 @@
"defaults/cc/common/crtend_so.c": nil,
"defaults/cc/common/crtend.c": nil,
"defaults/cc/common/crtbrand.c": nil,
+
+ "defaults/cc/common/libclang_rt.ubsan_minimal.android_arm64.a": nil,
+ "defaults/cc/common/libclang_rt.ubsan_minimal.android_arm.a": nil,
+ "defaults/cc/common/libclang_rt.ubsan_minimal.x86_64.a": nil,
+ "defaults/cc/common/libclang_rt.ubsan_minimal.x86.a": nil,
}.AddToFixture(),
// Place the default cc test modules that are common to all platforms in a location that will not
@@ -592,6 +631,51 @@
}),
)
+// PrepareForTestWithHostMusl sets the host configuration to musl libc instead of glibc. It also disables the test
+// on mac, which doesn't support musl libc, and adds musl modules.
+var PrepareForTestWithHostMusl = android.GroupFixturePreparers(
+ android.FixtureModifyConfig(android.ModifyTestConfigForMusl),
+ android.PrepareForSkipTestOnMac,
+ android.FixtureAddTextFile("external/musl/Android.bp", `
+ cc_defaults {
+ name: "libc_musl_crt_defaults",
+ host_supported: true,
+ device_supported: false,
+ }
+
+ cc_object {
+ name: "libc_musl_crtbegin_so",
+ defaults: ["libc_musl_crt_defaults"],
+ }
+
+ cc_object {
+ name: "libc_musl_crtend_so",
+ defaults: ["libc_musl_crt_defaults"],
+ }
+
+ cc_object {
+ name: "libc_musl_crtbegin_dynamic",
+ defaults: ["libc_musl_crt_defaults"],
+ }
+
+ cc_object {
+ name: "libc_musl_crtbegin_static",
+ defaults: ["libc_musl_crt_defaults"],
+ }
+
+ cc_object {
+ name: "libc_musl_crtend",
+ defaults: ["libc_musl_crt_defaults"],
+ }
+ `),
+)
+
+// PrepareForTestWithFdoProfile registers module types to test with fdo_profile
+var PrepareForTestWithFdoProfile = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("soong_namespace", android.NamespaceFactory)
+ ctx.RegisterModuleType("fdo_profile", fdoProfileFactory)
+})
+
// TestConfig is the legacy way of creating a test Config for testing cc modules.
//
// See testCc for an explanation as to how to stop using this deprecated method.
@@ -622,7 +706,7 @@
func CreateTestContext(config android.Config) *android.TestContext {
ctx := android.NewTestArchContext(config)
genrule.RegisterGenruleBuildComponents(ctx)
- ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
+ ctx.RegisterModuleType("cc_fuzz", LibFuzzFactory)
ctx.RegisterModuleType("cc_test", TestFactory)
ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
@@ -710,3 +794,22 @@
t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
}
}
+
+func checkOverrides(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, jsonPath string, expected []string) {
+ t.Helper()
+ out := singleton.MaybeOutput(jsonPath)
+ content := android.ContentFromFileRuleForTests(t, out)
+
+ var flags snapshotJsonFlags
+ if err := json.Unmarshal([]byte(content), &flags); err != nil {
+ t.Errorf("Error while unmarshalling json %q: %s", jsonPath, err.Error())
+ return
+ }
+
+ for _, moduleName := range expected {
+ if !android.InList(moduleName, flags.Overrides) {
+ t.Errorf("expected %q to be in %q: %q", moduleName, flags.Overrides, content)
+ return
+ }
+ }
+}
diff --git a/cc/tidy.go b/cc/tidy.go
index 750e9de..bbcaece 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -15,6 +15,7 @@
package cc
import (
+ "fmt"
"path/filepath"
"regexp"
"strings"
@@ -62,6 +63,12 @@
return []interface{}{&tidy.Properties}
}
+// Set this const to true when all -warnings-as-errors in tidy_flags
+// are replaced with tidy_checks_as_errors.
+// Then, that old style usage will be obsolete and an error.
+const NoWarningsAsErrorsInTidyFlags = true
+
+// keep this up to date with https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/cc/clang_tidy.bzl
func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags {
CheckBadTidyFlags(ctx, "tidy_flags", tidy.Properties.Tidy_flags)
CheckBadTidyChecks(ctx, "tidy_checks", tidy.Properties.Tidy_checks)
@@ -70,15 +77,24 @@
if tidy.Properties.Tidy != nil && !*tidy.Properties.Tidy {
return flags
}
-
+ // Some projects like external/* and vendor/* have clang-tidy disabled by default,
+ // unless they are enabled explicitly with the "tidy:true" property or
+ // when TIDY_EXTERNAL_VENDOR is set to true.
+ if !proptools.Bool(tidy.Properties.Tidy) &&
+ config.NoClangTidyForDir(
+ ctx.Config().IsEnvTrue("TIDY_EXTERNAL_VENDOR"),
+ ctx.ModuleDir()) {
+ return flags
+ }
// If not explicitly disabled, set flags.Tidy to generate .tidy rules.
// Note that libraries and binaries will depend on .tidy files ONLY if
// 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,10 +111,15 @@
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)
}
@@ -115,19 +136,7 @@
flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before=-fno-caret-diagnostics")
}
- extraArgFlags := []string{
- // We might be using the static analyzer through clang tidy.
- // https://bugs.llvm.org/show_bug.cgi?id=32914
- "-D__clang_analyzer__",
-
- // A recent change in clang-tidy (r328258) enabled destructor inlining, which
- // appears to cause a number of false positives. Until that's resolved, this turns
- // off the effects of r328258.
- // https://bugs.llvm.org/show_bug.cgi?id=37459
- "-Xclang", "-analyzer-config", "-Xclang", "c++-temp-dtor-inlining=false",
- }
-
- for _, f := range extraArgFlags {
+ for _, f := range config.TidyExtraArgFlags() {
flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before="+f)
}
@@ -138,61 +147,54 @@
tidyChecks += config.TidyChecksForDir(ctx.ModuleDir())
}
if len(tidy.Properties.Tidy_checks) > 0 {
- tidyChecks = tidyChecks + "," + strings.Join(esc(ctx, "tidy_checks",
- config.ClangRewriteTidyChecks(tidy.Properties.Tidy_checks)), ",")
+ // If Tidy_checks contains "-*", ignore all checks before "-*".
+ localChecks := tidy.Properties.Tidy_checks
+ ignoreGlobalChecks := false
+ for n, check := range tidy.Properties.Tidy_checks {
+ if check == "-*" {
+ ignoreGlobalChecks = true
+ localChecks = tidy.Properties.Tidy_checks[n:]
+ }
+ }
+ if ignoreGlobalChecks {
+ tidyChecks = "-checks=" + strings.Join(esc(ctx, "tidy_checks",
+ config.ClangRewriteTidyChecks(localChecks)), ",")
+ } else {
+ tidyChecks = tidyChecks + "," + strings.Join(esc(ctx, "tidy_checks",
+ config.ClangRewriteTidyChecks(localChecks)), ",")
+ }
}
+ tidyChecks = tidyChecks + config.TidyGlobalNoChecks()
if ctx.Windows() {
// https://b.corp.google.com/issues/120614316
// mingw32 has cert-dcl16-c warning in NO_ERROR,
// which is used in many Android files.
- tidyChecks = tidyChecks + ",-cert-dcl16-c"
+ tidyChecks += ",-cert-dcl16-c"
}
- // https://b.corp.google.com/issues/153464409
- // many local projects enable cert-* checks, which
- // trigger bugprone-reserved-identifier.
- tidyChecks = tidyChecks + ",-bugprone-reserved-identifier*,-cert-dcl51-cpp,-cert-dcl37-c"
- // http://b/153757728
- tidyChecks = tidyChecks + ",-readability-qualified-auto"
- // http://b/155034563
- tidyChecks = tidyChecks + ",-bugprone-signed-char-misuse"
- // http://b/155034972
- tidyChecks = tidyChecks + ",-bugprone-branch-clone"
- // http://b/193716442
- tidyChecks = tidyChecks + ",-bugprone-implicit-widening-of-multiplication-result"
- // Too many existing functions trigger this rule, and fixing it requires large code
- // refactoring. The cost of maintaining this tidy rule outweighs the benefit it brings.
- tidyChecks = tidyChecks + ",-bugprone-easily-swappable-parameters"
- // http://b/216364337 - TODO: Follow-up after compiler update to
- // disable or fix individual instances.
- tidyChecks = tidyChecks + ",-cert-err33-c"
+
flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
- if ctx.Config().IsEnvTrue("WITH_TIDY") {
- // WITH_TIDY=1 enables clang-tidy globally. There could be many unexpected
- // warnings from new checks and many local tidy_checks_as_errors and
- // -warnings-as-errors can break a global build.
- // So allow all clang-tidy warnings.
- inserted := false
- for i, s := range flags.TidyFlags {
- if strings.Contains(s, "-warnings-as-errors=") {
- // clang-tidy accepts only one -warnings-as-errors
- // replace the old one
- re := regexp.MustCompile(`'?-?-warnings-as-errors=[^ ]* *`)
- newFlag := re.ReplaceAllString(s, "")
- if newFlag == "" {
- flags.TidyFlags[i] = "-warnings-as-errors=-*"
- } else {
- flags.TidyFlags[i] = newFlag + " -warnings-as-errors=-*"
- }
- inserted = true
- break
+ // Embedding -warnings-as-errors in tidy_flags is error-prone.
+ // It should be replaced with the tidy_checks_as_errors list.
+ for i, s := range flags.TidyFlags {
+ if strings.Contains(s, "-warnings-as-errors=") {
+ if NoWarningsAsErrorsInTidyFlags {
+ ctx.PropertyErrorf("tidy_flags", "should not contain "+s+"; use tidy_checks_as_errors instead.")
+ } else {
+ fmt.Printf("%s: warning: module %s's tidy_flags should not contain %s, which is replaced with -warnings-as-errors=-*; use tidy_checks_as_errors for your own as-error warnings instead.\n",
+ ctx.BlueprintsFile(), ctx.ModuleName(), s)
+ flags.TidyFlags[i] = "-warnings-as-errors=-*"
}
+ break // there is at most one -warnings-as-errors
}
- if !inserted {
- flags.TidyFlags = append(flags.TidyFlags, "-warnings-as-errors=-*")
- }
- } else if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
- tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(ctx, "tidy_checks_as_errors", tidy.Properties.Tidy_checks_as_errors), ",")
+ }
+ // Default clang-tidy flags does not contain -warning-as-errors.
+ // If a module has tidy_checks_as_errors, add the list to -warnings-as-errors
+ // and then append the TidyGlobalNoErrorChecks.
+ if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
+ tidyChecksAsErrors := "-warnings-as-errors=" +
+ strings.Join(esc(ctx, "tidy_checks_as_errors", tidy.Properties.Tidy_checks_as_errors), ",") +
+ config.TidyGlobalNoErrorChecks()
flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
}
return flags
diff --git a/cc/tidy_test.go b/cc/tidy_test.go
new file mode 100644
index 0000000..7036ecb
--- /dev/null
+++ b/cc/tidy_test.go
@@ -0,0 +1,246 @@
+// 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"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestTidyFlagsWarningsAsErrors(t *testing.T) {
+ // The "tidy_flags" property should not contain -warnings-as-errors.
+ type testCase struct {
+ libName, bp string
+ errorMsg string // a negative test; must have error message
+ flags []string // must have substrings in tidyFlags
+ noFlags []string // must not have substrings in tidyFlags
+ }
+
+ testCases := []testCase{
+ {
+ "libfoo1",
+ `cc_library_shared { // no warnings-as-errors, good tidy_flags
+ name: "libfoo1",
+ srcs: ["foo.c"],
+ tidy_flags: ["-header-filter=dir1/"],
+ }`,
+ "",
+ []string{"-header-filter=dir1/"},
+ []string{"-warnings-as-errors"},
+ },
+ {
+ "libfoo2",
+ `cc_library_shared { // good use of tidy_checks_as_errors
+ name: "libfoo2",
+ srcs: ["foo.c"],
+ tidy_checks_as_errors: ["xyz-*", "abc"],
+ }`,
+ "",
+ []string{
+ "-header-filter=^", // there is a default header filter
+ "-warnings-as-errors='xyz-*',abc,${config.TidyGlobalNoErrorChecks}",
+ },
+ []string{},
+ },
+ }
+ if NoWarningsAsErrorsInTidyFlags {
+ testCases = append(testCases, testCase{
+ "libfoo3",
+ `cc_library_shared { // bad use of -warnings-as-errors in tidy_flags
+ name: "libfoo3",
+ srcs: ["foo.c"],
+ tidy_flags: [
+ "-header-filters=.*",
+ "-warnings-as-errors=xyz-*",
+ ],
+ }`,
+ `module "libfoo3" .*: tidy_flags: should not contain .*;` +
+ ` use tidy_checks_as_errors instead`,
+ []string{},
+ []string{},
+ })
+ }
+ for _, test := range testCases {
+ if test.errorMsg != "" {
+ testCcError(t, test.errorMsg, test.bp)
+ continue
+ }
+ variant := "android_arm64_armv8-a_shared"
+ ctx := testCc(t, test.bp)
+ t.Run("caseTidyFlags", func(t *testing.T) {
+ flags := ctx.ModuleForTests(test.libName, variant).Rule("clangTidy").Args["tidyFlags"]
+ for _, flag := range test.flags {
+ if !strings.Contains(flags, flag) {
+ t.Errorf("tidyFlags %v for %s does not contain %s.", flags, test.libName, flag)
+ }
+ }
+ for _, flag := range test.noFlags {
+ if strings.Contains(flags, flag) {
+ t.Errorf("tidyFlags %v for %s should not contain %s.", flags, test.libName, flag)
+ }
+ }
+ })
+ }
+}
+
+func TestTidyChecks(t *testing.T) {
+ // The "tidy_checks" property defines additional checks appended
+ // to global default. But there are some checks disabled after
+ // the local tidy_checks.
+ bp := `
+ cc_library_shared { // has global checks + extraGlobalChecks
+ name: "libfoo_1",
+ srcs: ["foo.c"],
+ }
+ cc_library_shared { // has only local checks + extraGlobalChecks
+ name: "libfoo_2",
+ srcs: ["foo.c"],
+ tidy_checks: ["-*", "xyz-*"],
+ }
+ cc_library_shared { // has global checks + local checks + extraGlobalChecks
+ name: "libfoo_3",
+ srcs: ["foo.c"],
+ tidy_checks: ["-abc*", "xyz-*", "mycheck"],
+ }
+ cc_library_shared { // has only local checks after "-*" + extraGlobalChecks
+ name: "libfoo_4",
+ srcs: ["foo.c"],
+ tidy_checks: ["-abc*", "xyz-*", "mycheck", "-*", "xyz-*"],
+ }`
+ ctx := testCc(t, bp)
+
+ globalChecks := "-checks=${config.TidyDefaultGlobalChecks},"
+ firstXyzChecks := "-checks='-*','xyz-*',"
+ localXyzChecks := "'-*','xyz-*'"
+ localAbcChecks := "'-abc*','xyz-*',mycheck"
+ extraGlobalChecks := ",${config.TidyGlobalNoChecks}"
+ testCases := []struct {
+ libNumber int // 1,2,3,...
+ checks []string // must have substrings in -checks
+ noChecks []string // must not have substrings in -checks
+ }{
+ {1, []string{globalChecks, extraGlobalChecks}, []string{localXyzChecks, localAbcChecks}},
+ {2, []string{firstXyzChecks, extraGlobalChecks}, []string{globalChecks, localAbcChecks}},
+ {3, []string{globalChecks, localAbcChecks, extraGlobalChecks}, []string{localXyzChecks}},
+ {4, []string{firstXyzChecks, extraGlobalChecks}, []string{globalChecks, localAbcChecks}},
+ }
+ t.Run("caseTidyChecks", func(t *testing.T) {
+ variant := "android_arm64_armv8-a_shared"
+ for _, test := range testCases {
+ libName := fmt.Sprintf("libfoo_%d", test.libNumber)
+ flags := ctx.ModuleForTests(libName, variant).Rule("clangTidy").Args["tidyFlags"]
+ splitFlags := strings.Split(flags, " ")
+ foundCheckFlag := false
+ for _, flag := range splitFlags {
+ if strings.HasPrefix(flag, "-checks=") {
+ foundCheckFlag = true
+ for _, check := range test.checks {
+ if !strings.Contains(flag, check) {
+ t.Errorf("tidyFlags for %s does not contain %s.", libName, check)
+ }
+ }
+ for _, check := range test.noChecks {
+ if strings.Contains(flag, check) {
+ t.Errorf("tidyFlags for %s should not contain %s.", libName, check)
+ }
+ }
+ break
+ }
+ }
+ if !foundCheckFlag {
+ t.Errorf("tidyFlags for %s does not contain -checks=.", libName)
+ }
+ }
+ })
+}
+
+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..6d8ac43 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)
@@ -127,17 +100,6 @@
"ln -sf " + target + " " + filepath.Join(dir, linkName)
}
-func combineNoticesRule(ctx android.SingletonContext, paths android.Paths, out string) android.OutputPath {
- outPath := android.PathForOutput(ctx, out)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cat,
- Inputs: paths,
- Output: outPath,
- Description: "combine notices for " + out,
- })
- return outPath
-}
-
// Dump a map to a list file as:
//
// {key1} {value1}
@@ -145,7 +107,7 @@
// ...
func installMapListFileRule(ctx android.SingletonContext, m map[string]string, path string) android.OutputPath {
var txtBuilder strings.Builder
- for idx, k := range android.SortedStringKeys(m) {
+ for idx, k := range android.SortedKeys(m) {
if idx > 0 {
txtBuilder.WriteString("\n")
}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 77e6f6f..d2531c0 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -93,17 +93,18 @@
// Libraries
if sanitizable, ok := m.(PlatformSanitizeable); ok && sanitizable.IsSnapshotLibrary() {
if sanitizable.SanitizePropDefined() {
- // scs and hwasan export both sanitized and unsanitized variants for static and header
- // Always use unsanitized variants of them.
- for _, t := range []SanitizerType{scs, Hwasan} {
- if !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(t) {
- return false
- }
+ // scs exports both sanitized and unsanitized variants for static and header
+ // Always use unsanitized variant of it.
+ if !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(scs) {
+ return false
}
- // cfi also exports both variants. But for static, we capture both.
+ // cfi and hwasan also export both variants. But for static, we capture both.
// This is because cfi static libraries can't be linked from non-cfi modules,
- // and vice versa. This isn't the case for scs and hwasan sanitizers.
- if !sanitizable.Static() && !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(cfi) {
+ // and vice versa.
+ // hwasan is captured as well to support hwasan build.
+ if !sanitizable.Static() &&
+ !sanitizable.Shared() &&
+ (sanitizable.IsSanitizerEnabled(cfi) || sanitizable.IsSanitizerEnabled(Hwasan)) {
return false
}
}
@@ -156,9 +157,10 @@
// extra config files
InitRc []string `json:",omitempty"`
VintfFragments []string `json:",omitempty"`
+ MinSdkVersion string `json:",omitempty"`
}
-var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
+var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) snapshot.SnapshotPaths {
/*
Vendor snapshot zipped artifacts directory structure for cc modules:
{SNAPSHOT_ARCH}/
@@ -193,10 +195,10 @@
*/
var snapshotOutputs android.Paths
+ var snapshotNotices android.Paths
includeDir := filepath.Join(snapshotArchDir, "include")
configsDir := filepath.Join(snapshotArchDir, "configs")
- noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
installedNotices := make(map[string]bool)
installedConfigs := make(map[string]bool)
@@ -226,7 +228,7 @@
prop := snapshotJsonFlags{}
// Common properties among snapshots.
- prop.ModuleName = ctx.ModuleName(m)
+ prop.InitBaseSnapshotPropsWithName(m, ctx.ModuleName(m))
if supportsVndkExt(s.Image) && m.IsVndkExt() {
// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
if m.IsVndkSp() {
@@ -239,12 +241,20 @@
}
prop.RuntimeLibs = m.SnapshotRuntimeLibs()
prop.Required = m.RequiredModuleNames()
+ if o, ok := m.(overridable); ok {
+ prop.Overrides = o.overriddenModules()
+ }
for _, path := range m.InitRc() {
prop.InitRc = append(prop.InitRc, filepath.Join("configs", path.Base()))
}
for _, path := range m.VintfFragments() {
prop.VintfFragments = append(prop.VintfFragments, filepath.Join("configs", path.Base()))
}
+ if m.IsPrebuilt() {
+ prop.MinSdkVersion = "apex_inherit"
+ } else {
+ prop.MinSdkVersion = m.MinSdkVersion()
+ }
// install config files. ignores any duplicates.
for _, path := range append(m.InitRc(), m.VintfFragments()...) {
@@ -300,23 +310,31 @@
libPath := m.OutputFile().Path()
stem = libPath.Base()
if sanitizable, ok := m.(PlatformSanitizeable); ok {
- if (sanitizable.Static() || sanitizable.Rlib()) && sanitizable.SanitizePropDefined() && sanitizable.IsSanitizerEnabled(cfi) {
- // both cfi and non-cfi variant for static libraries can exist.
- // attach .cfi to distinguish between cfi and non-cfi.
- // e.g. libbase.a -> libbase.cfi.a
- ext := filepath.Ext(stem)
- stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext
- prop.Sanitize = "cfi"
- prop.ModuleName += ".cfi"
+ if (sanitizable.Static() || sanitizable.Rlib()) && sanitizable.SanitizePropDefined() {
+ if sanitizable.IsSanitizerEnabled(cfi) {
+ // both cfi and non-cfi variant for static libraries can exist.
+ // attach .cfi to distinguish between cfi and non-cfi.
+ // e.g. libbase.a -> libbase.cfi.a
+ ext := filepath.Ext(stem)
+ stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext
+ prop.Sanitize = "cfi"
+ prop.ModuleName += ".cfi"
+ } else if sanitizable.IsSanitizerEnabled(Hwasan) {
+ // Same for the hwasan
+ ext := filepath.Ext(stem)
+ stem = strings.TrimSuffix(stem, ext) + ".hwasan" + ext
+ prop.Sanitize = "hwasan"
+ prop.ModuleName += ".hwasan"
+ }
}
}
- snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
+ snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem)
ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
} else {
stem = ctx.ModuleName(m)
}
- propOut = filepath.Join(snapshotArchDir, targetArch, libType, stem+".json")
+ propOut = filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem+".json")
} else if m.Binary() {
// binary flags
prop.Symlinks = m.Symlinks()
@@ -392,13 +410,10 @@
headers = append(headers, m.SnapshotHeaders()...)
}
- if len(m.EffectiveLicenseFiles()) > 0 {
- noticeName := ctx.ModuleName(m) + ".txt"
- noticeOut := filepath.Join(noticeDir, noticeName)
- // skip already copied notice file
- if !installedNotices[noticeOut] {
- installedNotices[noticeOut] = true
- snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.EffectiveLicenseFiles(), noticeOut))
+ for _, notice := range m.EffectiveLicenseFiles() {
+ if _, ok := installedNotices[notice.String()]; !ok {
+ installedNotices[notice.String()] = true
+ snapshotNotices = append(snapshotNotices, notice)
}
}
})
@@ -408,7 +423,7 @@
snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), s.Fake))
}
- return snapshotOutputs
+ return snapshot.SnapshotPaths{OutputFiles: snapshotOutputs, NoticeFiles: snapshotNotices}
}
func init() {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 2bb43ab..c5431b3 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -23,6 +23,17 @@
"testing"
)
+func checkJsonContents(t *testing.T, ctx android.TestingSingleton, jsonPath string, key string, value string) {
+ jsonOut := ctx.MaybeOutput(jsonPath)
+ if jsonOut.Rule == nil {
+ t.Errorf("%q expected but not found", jsonPath)
+ return
+ }
+ if !strings.Contains(jsonOut.Args["content"], fmt.Sprintf("%q:%q", key, value)) {
+ t.Errorf("%q must include %q:%q but it only has %v", jsonPath, key, value, jsonOut.Args["content"])
+ }
+}
+
func TestVendorSnapshotCapture(t *testing.T) {
bp := `
cc_library {
@@ -42,9 +53,17 @@
}
cc_library {
+ name: "libvendor_override",
+ vendor: true,
+ nocrt: true,
+ overrides: ["libvendor"],
+ }
+
+ cc_library {
name: "libvendor_available",
vendor_available: true,
nocrt: true,
+ min_sdk_version: "29",
}
cc_library_headers {
@@ -65,6 +84,13 @@
nocrt: true,
}
+ cc_binary {
+ name: "vendor_bin_override",
+ vendor: true,
+ nocrt: true,
+ overrides: ["vendor_bin"],
+ }
+
cc_prebuilt_library_static {
name: "libb",
vendor_available: true,
@@ -141,6 +167,9 @@
filepath.Join(staticDir, "libvendor_available.a.json"),
filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
+ checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libb.a.json"), "MinSdkVersion", "apex_inherit")
+ checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libvendor_available.a.json"), "MinSdkVersion", "29")
+
// For binary executables, all vendor:true and vendor_available modules are captured.
if archType == "arm64" {
binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
@@ -150,6 +179,8 @@
jsonFiles = append(jsonFiles,
filepath.Join(binaryDir, "vendor_bin.json"),
filepath.Join(binaryDir, "vendor_available_bin.json"))
+
+ checkOverrides(t, ctx, snapshotSingleton, filepath.Join(binaryDir, "vendor_bin_override.json"), []string{"vendor_bin"})
}
// For header libraries, all vendor:true and vendor_available modules are captured.
@@ -161,6 +192,8 @@
objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
CheckSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+
+ checkOverrides(t, ctx, snapshotSingleton, filepath.Join(sharedDir, "libvendor_override.so.json"), []string{"libvendor"})
}
for _, jsonFile := range jsonFiles {
@@ -506,11 +539,13 @@
],
shared_libs: [
"libvendor",
+ "libvendor_override",
"libvendor_available",
"lib64",
],
binaries: [
"bin",
+ "bin_override",
],
},
arm: {
@@ -526,6 +561,7 @@
],
shared_libs: [
"libvendor",
+ "libvendor_override",
"libvendor_available",
"lib32",
],
@@ -577,6 +613,30 @@
},
}
+ vendor_snapshot_shared {
+ name: "libvendor_override",
+ version: "31",
+ target_arch: "arm64",
+ compile_multilib: "both",
+ vendor: true,
+ overrides: ["libvendor"],
+ shared_libs: [
+ "libvendor_without_snapshot",
+ "libvendor_available",
+ "libvndk",
+ ],
+ arch: {
+ arm64: {
+ src: "override/libvendor.so",
+ export_include_dirs: ["include/libvendor"],
+ },
+ arm: {
+ src: "override/libvendor.so",
+ export_include_dirs: ["include/libvendor"],
+ },
+ },
+ }
+
vendor_snapshot_static {
name: "lib32",
version: "31",
@@ -745,6 +805,21 @@
}
vendor_snapshot_binary {
+ name: "bin_override",
+ version: "31",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ overrides: ["bin"],
+ arch: {
+ arm64: {
+ src: "override/bin",
+ },
+ },
+ symlinks: ["binfoo", "binbar"],
+ }
+
+ vendor_snapshot_binary {
name: "bin32",
version: "31",
target_arch: "arm64",
@@ -793,6 +868,7 @@
"framework/symbol.txt": nil,
"vendor/Android.bp": []byte(vendorProprietaryBp),
"vendor/bin": nil,
+ "vendor/override/bin": nil,
"vendor/bin32": nil,
"vendor/bin.cpp": nil,
"vendor/client.cpp": nil,
@@ -806,6 +882,7 @@
"vendor/libvendor.a": nil,
"vendor/libvendor.cfi.a": nil,
"vendor/libvendor.so": nil,
+ "vendor/override/libvendor.so": nil,
"vendor/lib32.a": nil,
"vendor/lib32.so": nil,
"vendor/lib64.a": nil,
@@ -958,6 +1035,23 @@
if inList(binaryVariant, binVariants) {
t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
}
+
+ // test overrides property
+ binOverrideModule := ctx.ModuleForTests("bin_override.vendor_binary.31.arm64", binaryVariant)
+ binOverrideModule.Output("bin")
+ binOverrideMkEntries := android.AndroidMkEntriesForTest(t, ctx, binOverrideModule.Module())
+ binOverrideEntry := binOverrideMkEntries[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+ if !inList("bin", binOverrideEntry) {
+ t.Errorf("bin_override must override bin but was %q\n", binOverrideEntry)
+ }
+
+ libvendorOverrideModule := ctx.ModuleForTests("libvendor_override.vendor_shared.31.arm64", sharedVariant)
+ libvendorOverrideModule.Output("libvendor.so")
+ libvendorOverrideMkEntries := android.AndroidMkEntriesForTest(t, ctx, libvendorOverrideModule.Module())
+ libvendorOverrideEntry := libvendorOverrideMkEntries[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+ if !inList("libvendor", libvendorOverrideEntry) {
+ t.Errorf("libvendor_override must override libvendor but was %q\n", libvendorOverrideEntry)
+ }
}
func TestVendorSnapshotSanitizer(t *testing.T) {
@@ -971,9 +1065,16 @@
"libsnapshot",
"note_memtag_heap_sync",
],
+ objects: [
+ "snapshot_object",
+ ],
+ vndk_libs: [
+ "libclang_rt.hwasan",
+ ],
},
},
}
+
vendor_snapshot_static {
name: "libsnapshot",
vendor: true,
@@ -984,7 +1085,10 @@
src: "libsnapshot.a",
cfi: {
src: "libsnapshot.cfi.a",
- }
+ },
+ hwasan: {
+ src: "libsnapshot.hwasan.a",
+ },
},
},
}
@@ -1001,6 +1105,35 @@
},
}
+ vndk_prebuilt_shared {
+ name: "libclang_rt.hwasan",
+ version: "28",
+ target_arch: "arm64",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ arch: {
+ arm64: {
+ srcs: ["libclang_rt.hwasan.so"],
+ },
+ },
+ }
+
+ vendor_snapshot_object {
+ name: "snapshot_object",
+ vendor: true,
+ target_arch: "arm64",
+ version: "28",
+ arch: {
+ arm64: {
+ src: "snapshot_object.o",
+ },
+ },
+ stl: "none",
+ }
+
cc_test {
name: "vstest",
gtest: false,
@@ -1017,25 +1150,44 @@
mockFS := map[string][]byte{
"vendor/Android.bp": []byte(bp),
"vendor/libc++demangle.a": nil,
+ "vendor/libclang_rt.hwasan.so": nil,
"vendor/libsnapshot.a": nil,
"vendor/libsnapshot.cfi.a": nil,
+ "vendor/libsnapshot.hwasan.a": nil,
"vendor/note_memtag_heap_sync.a": nil,
+ "vendor/snapshot_object.o": nil,
}
config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
config.TestProductVariables.DeviceVndkVersion = StringPtr("28")
config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ config.TestProductVariables.SanitizeDevice = []string{"hwaddress"}
ctx := testCcWithConfig(t, config)
- // Check non-cfi and cfi variant.
+ // Check non-cfi, cfi and hwasan variant.
staticVariant := "android_vendor.28_arm64_armv8-a_static"
staticCfiVariant := "android_vendor.28_arm64_armv8-a_static_cfi"
+ staticHwasanVariant := "android_vendor.28_arm64_armv8-a_static_hwasan"
+ staticHwasanCfiVariant := "android_vendor.28_arm64_armv8-a_static_hwasan_cfi"
staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticVariant).Module().(*Module)
assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticCfiVariant).Module().(*Module)
assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
+
+ staticHwasanModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanVariant).Module().(*Module)
+ assertString(t, staticHwasanModule.outputFile.Path().Base(), "libsnapshot.hwasan.a")
+
+ staticHwasanCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanCfiVariant).Module().(*Module)
+ if !staticHwasanCfiModule.HiddenFromMake() || !staticHwasanCfiModule.PreventInstall() {
+ t.Errorf("Hwasan and Cfi cannot enabled at the same time.")
+ }
+
+ snapshotObjModule := ctx.ModuleForTests("snapshot_object.vendor_object.28.arm64", "android_vendor.28_arm64_armv8-a").Module()
+ snapshotObjMkEntries := android.AndroidMkEntriesForTest(t, ctx, snapshotObjModule)
+ // snapshot object must not add ".hwasan" suffix
+ assertString(t, snapshotObjMkEntries[0].EntryMap["LOCAL_MODULE"][0], "snapshot_object")
}
func TestVendorSnapshotExclude(t *testing.T) {
@@ -1520,3 +1672,69 @@
}
}
}
+
+func TestSnapshotInRelativeInstallPath(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libvendor_available",
+ vendor_available: true,
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libvendor_available_var",
+ vendor_available: true,
+ stem: "libvendor_available",
+ relative_install_path: "var",
+ nocrt: true,
+ }
+`
+
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ ctx := testCcWithConfig(t, config)
+
+ // Check Vendor snapshot output.
+
+ snapshotDir := "vendor-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+ var jsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ []string{"arm", "armv7-a-neon"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ // For shared libraries, only non-VNDK vendor_available modules are captured
+ sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ sharedDirVar := filepath.Join(sharedDir, "var")
+ CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+ CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available_var", "libvendor_available.so", sharedDirVar, sharedVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(sharedDir, "libvendor_available.so.json"),
+ filepath.Join(sharedDirVar, "libvendor_available.so.json"))
+ }
+
+ for _, jsonFile := range jsonFiles {
+ // verify all json files exist
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("%q expected but not found", jsonFile)
+ }
+ }
+
+ // fake snapshot should have all outputs in the normal snapshot.
+ fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
+ for _, output := range snapshotSingleton.AllOutputs() {
+ fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
+ if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
+ t.Errorf("%q expected but not found", fakeOutput)
+ }
+ }
+}
diff --git a/cc/vndk.go b/cc/vndk.go
index bf6148b..9b70004 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -241,7 +241,7 @@
func vndkModuleLister(predicate func(*Module) bool) moduleListerFunc {
return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
ctx.VisitAllModules(func(m android.Module) {
- if c, ok := m.(*Module); ok && predicate(c) {
+ if c, ok := m.(*Module); ok && predicate(c) && !c.IsVndkPrebuiltLibrary() {
filename, err := getVndkFileName(c)
if err != nil {
ctx.ModuleErrorf(m, "%s", err)
@@ -368,7 +368,10 @@
}
return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp() && !m.IsVndkExt()
}
-
+ // VNDK APEX doesn't need stub variants
+ if lib.buildStubs() {
+ return false
+ }
useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
@@ -399,6 +402,11 @@
m.VendorProperties.IsVNDKPrivate = Bool(prebuiltLib.Properties.Llndk.Private)
}
+ if m.IsVndkPrebuiltLibrary() && !m.IsVndk() {
+ m.VendorProperties.IsLLNDK = true
+ // TODO(b/280697209): copy "llndk.private" flag to vndk_prebuilt_shared
+ }
+
if (isLib && lib.buildShared()) || (isPrebuiltLib && prebuiltLib.buildShared()) {
if m.vndkdep != nil && m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
processVndkLibrary(mctx, m)
@@ -701,28 +709,38 @@
snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "shared", vndkType, libPath.Base())
ret = append(ret, snapshot.CopyFileRule(pctx, ctx, libPath, snapshotLibOut))
+ // json struct to export snapshot information
+ prop := struct {
+ MinSdkVersion string `json:",omitempty"`
+ LicenseKinds []string `json:",omitempty"`
+ LicenseTexts []string `json:",omitempty"`
+ ExportedDirs []string `json:",omitempty"`
+ ExportedSystemDirs []string `json:",omitempty"`
+ ExportedFlags []string `json:",omitempty"`
+ RelativeInstallPath string `json:",omitempty"`
+ }{}
+
+ prop.LicenseKinds = m.EffectiveLicenseKinds()
+ prop.LicenseTexts = m.EffectiveLicenseFiles().Strings()
+ prop.MinSdkVersion = m.MinSdkVersion()
+
if ctx.Config().VndkSnapshotBuildArtifacts() {
- prop := struct {
- ExportedDirs []string `json:",omitempty"`
- ExportedSystemDirs []string `json:",omitempty"`
- ExportedFlags []string `json:",omitempty"`
- RelativeInstallPath string `json:",omitempty"`
- }{}
exportedInfo := ctx.ModuleProvider(m, FlagExporterInfoProvider).(FlagExporterInfo)
prop.ExportedFlags = exportedInfo.Flags
prop.ExportedDirs = exportedInfo.IncludeDirs.Strings()
prop.ExportedSystemDirs = exportedInfo.SystemIncludeDirs.Strings()
prop.RelativeInstallPath = m.RelativeInstallPath()
-
- propOut := snapshotLibOut + ".json"
-
- j, err := json.Marshal(prop)
- if err != nil {
- ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
- return nil, false
- }
- ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
}
+
+ propOut := snapshotLibOut + ".json"
+
+ j, err := json.Marshal(prop)
+ if err != nil {
+ ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
+ return nil, false
+ }
+ ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
+
return ret, true
}
@@ -762,13 +780,11 @@
moduleNames[stem] = ctx.ModuleName(m)
modulePaths[stem] = ctx.ModuleDir(m)
- if len(m.NoticeFiles()) > 0 {
- noticeName := stem + ".txt"
- // skip already copied notice file
- if _, ok := noticeBuilt[noticeName]; !ok {
- noticeBuilt[noticeName] = true
- snapshotOutputs = append(snapshotOutputs, combineNoticesRule(
- ctx, m.NoticeFiles(), filepath.Join(noticeDir, noticeName)))
+ for _, notice := range m.EffectiveLicenseFiles() {
+ if _, ok := noticeBuilt[notice.String()]; !ok {
+ noticeBuilt[notice.String()] = true
+ snapshotOutputs = append(snapshotOutputs, snapshot.CopyFileRule(
+ pctx, ctx, notice, filepath.Join(noticeDir, notice.String())))
}
}
@@ -887,7 +903,7 @@
})
ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES",
- strings.Join(android.SortedStringKeys(movedToApexLlndkLibraries), " "))
+ strings.Join(android.SortedKeys(movedToApexLlndkLibraries), " "))
ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
ctx.Strict("SOONG_VNDK_SNAPSHOT_ZIP", c.vndkSnapshotZipFile.String())
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
index 1cf64de..2e71fe1 100644
--- a/cmd/extract_apks/main.go
+++ b/cmd/extract_apks/main.go
@@ -29,6 +29,7 @@
"google.golang.org/protobuf/proto"
+ "android/soong/cmd/extract_apks/bundle_proto"
android_bundle_proto "android/soong/cmd/extract_apks/bundle_proto"
"android/soong/third_party/zip"
)
@@ -40,6 +41,7 @@
abis map[android_bundle_proto.Abi_AbiAlias]int
allowPrereleased bool
stem string
+ skipSdkCheck bool
}
// An APK set is a zip archive. An entry 'toc.pb' describes its contents.
@@ -75,7 +77,7 @@
return nil, err
}
bytes := make([]byte, tocFile.FileHeader.UncompressedSize64)
- if _, err := rc.Read(bytes); err != io.EOF {
+ if _, err := rc.Read(bytes); err != nil && err != io.EOF {
return nil, err
}
rc.Close()
@@ -131,21 +133,21 @@
*android_bundle_proto.ApkDescription
}
-func (m apkDescriptionMatcher) matches(config TargetConfig) bool {
- return m.ApkDescription == nil || (apkTargetingMatcher{m.Targeting}).matches(config)
+func (m apkDescriptionMatcher) matches(config TargetConfig, allAbisMustMatch bool) bool {
+ return m.ApkDescription == nil || (apkTargetingMatcher{m.Targeting}).matches(config, allAbisMustMatch)
}
type apkTargetingMatcher struct {
*android_bundle_proto.ApkTargeting
}
-func (m apkTargetingMatcher) matches(config TargetConfig) bool {
+func (m apkTargetingMatcher) matches(config TargetConfig, allAbisMustMatch bool) bool {
return m.ApkTargeting == nil ||
(abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
languageTargetingMatcher{m.LanguageTargeting}.matches(config) &&
screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
- multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config))
+ multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config, allAbisMustMatch))
}
type languageTargetingMatcher struct {
@@ -197,38 +199,97 @@
*android_bundle_proto.MultiAbiTargeting
}
-func (t multiAbiTargetingMatcher) matches(config TargetConfig) bool {
+type multiAbiValue []*bundle_proto.Abi
+
+func (m multiAbiValue) compare(other multiAbiValue) int {
+ min := func(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+ }
+
+ sortAbis := func(abiSlice multiAbiValue) func(i, j int) bool {
+ return func(i, j int) bool {
+ // sort priorities greatest to least
+ return multiAbiPriorities[abiSlice[i].Alias] > multiAbiPriorities[abiSlice[j].Alias]
+ }
+ }
+
+ sortedM := append(multiAbiValue{}, m...)
+ sort.Slice(sortedM, sortAbis(sortedM))
+ sortedOther := append(multiAbiValue{}, other...)
+ sort.Slice(sortedOther, sortAbis(sortedOther))
+
+ for i := 0; i < min(len(sortedM), len(sortedOther)); i++ {
+ if multiAbiPriorities[sortedM[i].Alias] > multiAbiPriorities[sortedOther[i].Alias] {
+ return 1
+ }
+ if multiAbiPriorities[sortedM[i].Alias] < multiAbiPriorities[sortedOther[i].Alias] {
+ return -1
+ }
+ }
+
+ return len(sortedM) - len(sortedOther)
+}
+
+// this logic should match the logic in bundletool at
+// https://github.com/google/bundletool/blob/ae0fc0162fd80d92ef8f4ef4527c066f0106942f/src/main/java/com/android/tools/build/bundletool/device/MultiAbiMatcher.java#L43
+// (note link is the commit at time of writing; but logic should always match the latest)
+func (t multiAbiTargetingMatcher) matches(config TargetConfig, allAbisMustMatch bool) bool {
if t.MultiAbiTargeting == nil {
return true
}
if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok {
return true
}
- // Find the one with the highest priority.
- highestPriority := 0
- for _, v := range t.GetValue() {
- for _, a := range v.GetAbi() {
- if _, ok := config.abis[a.Alias]; ok {
- if highestPriority < multiAbiPriorities[a.Alias] {
- highestPriority = multiAbiPriorities[a.Alias]
- }
+
+ multiAbiIsValid := func(m multiAbiValue) bool {
+ numValid := 0
+ for _, abi := range m {
+ if _, ok := config.abis[abi.Alias]; ok {
+ numValid += 1
}
}
+ if numValid == 0 {
+ return false
+ } else if numValid > 0 && !allAbisMustMatch {
+ return true
+ } else {
+ return numValid == len(m)
+ }
}
- if highestPriority == 0 {
+
+ // ensure that the current value is valid for our config
+ valueSetContainsViableAbi := false
+ multiAbiSet := t.GetValue()
+ for _, multiAbi := range multiAbiSet {
+ if multiAbiIsValid(multiAbi.GetAbi()) {
+ valueSetContainsViableAbi = true
+ break
+ }
+ }
+
+ if !valueSetContainsViableAbi {
return false
}
+
// See if there are any matching alternatives with a higher priority.
- for _, v := range t.GetAlternatives() {
- for _, a := range v.GetAbi() {
- if _, ok := config.abis[a.Alias]; ok {
- if highestPriority < multiAbiPriorities[a.Alias] {
- // There's a better one. Skip this one.
- return false
- }
+ for _, altMultiAbi := range t.GetAlternatives() {
+ if !multiAbiIsValid(altMultiAbi.GetAbi()) {
+ continue
+ }
+
+ for _, multiAbi := range multiAbiSet {
+ valueAbis := multiAbiValue(multiAbi.GetAbi())
+ altAbis := multiAbiValue(altMultiAbi.GetAbi())
+ if valueAbis.compare(altAbis) < 0 {
+ // An alternative has a higher priority, don't use this one
+ return false
}
}
}
+
return true
}
@@ -262,6 +323,12 @@
func (m sdkVersionTargetingMatcher) matches(config TargetConfig) bool {
const preReleaseVersion = 10000
+ // TODO (b274518686) This check should only be used while SHA based targeting is active
+ // Once we have switched to an SDK version, this can be changed to throw an error if
+ // it was accidentally set
+ if config.skipSdkCheck == true {
+ return true
+ }
if m.SdkVersionTargeting == nil {
return true
}
@@ -304,13 +371,13 @@
*android_bundle_proto.VariantTargeting
}
-func (m variantTargetingMatcher) matches(config TargetConfig) bool {
+func (m variantTargetingMatcher) matches(config TargetConfig, allAbisMustMatch bool) bool {
if m.VariantTargeting == nil {
return true
}
return sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
- multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config) &&
+ multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config, allAbisMustMatch) &&
screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
textureCompressionFormatTargetingMatcher{m.TextureCompressionFormatTargeting}.matches(config)
}
@@ -322,30 +389,42 @@
// Return all entries matching target configuration
func selectApks(toc Toc, targetConfig TargetConfig) SelectionResult {
- var result SelectionResult
- for _, variant := range (*toc).GetVariant() {
- if !(variantTargetingMatcher{variant.GetTargeting()}.matches(targetConfig)) {
- continue
- }
- for _, as := range variant.GetApkSet() {
- if !(moduleMetadataMatcher{as.ModuleMetadata}.matches(targetConfig)) {
+ checkMatching := func(allAbisMustMatch bool) SelectionResult {
+ var result SelectionResult
+ for _, variant := range (*toc).GetVariant() {
+ if !(variantTargetingMatcher{variant.GetTargeting()}.matches(targetConfig, allAbisMustMatch)) {
continue
}
- for _, apkdesc := range as.GetApkDescription() {
- if (apkDescriptionMatcher{apkdesc}).matches(targetConfig) {
- result.entries = append(result.entries, apkdesc.GetPath())
- // TODO(asmundak): As it turns out, moduleName which we get from
- // the ModuleMetadata matches the module names of the generated
- // entry paths just by coincidence, only for the split APKs. We
- // need to discuss this with bundletool folks.
- result.moduleName = as.GetModuleMetadata().GetName()
+ for _, as := range variant.GetApkSet() {
+ if !(moduleMetadataMatcher{as.ModuleMetadata}.matches(targetConfig)) {
+ continue
+ }
+ for _, apkdesc := range as.GetApkDescription() {
+ if (apkDescriptionMatcher{apkdesc}).matches(targetConfig, allAbisMustMatch) {
+ result.entries = append(result.entries, apkdesc.GetPath())
+ // TODO(asmundak): As it turns out, moduleName which we get from
+ // the ModuleMetadata matches the module names of the generated
+ // entry paths just by coincidence, only for the split APKs. We
+ // need to discuss this with bundletool folks.
+ result.moduleName = as.GetModuleMetadata().GetName()
+ }
+ }
+ // we allow only a single module, so bail out here if we found one
+ if result.moduleName != "" {
+ return result
}
}
- // we allow only a single module, so bail out here if we found one
- if result.moduleName != "" {
- return result
- }
}
+ return result
+ }
+ result := checkMatching(true)
+ if result.moduleName == "" {
+ // if there are no matches where all of the ABIs are available in the
+ // TargetConfig, then search again with a looser requirement of at
+ // least one matching ABI
+ // NOTE(b/260130686): this logic diverges from the logic in bundletool
+ // https://github.com/google/bundletool/blob/ae0fc0162fd80d92ef8f4ef4527c066f0106942f/src/main/java/com/android/tools/build/bundletool/device/MultiAbiMatcher.java#L43
+ result = checkMatching(false)
}
return result
}
@@ -500,7 +579,7 @@
func processArgs() {
flag.Usage = func() {
fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> [-zip <output-zip-file>] `+
- `-sdk-version value -abis value `+
+ `-sdk-version value -abis value [-skip-sdk-check]`+
`-screen-densities value {-stem value | -extract-single} [-allow-prereleased] `+
`[-apkcerts <apkcerts output file> -partition <partition>] <APK set>`)
flag.PrintDefaults()
@@ -513,6 +592,7 @@
"'all' or comma-separated list of screen density names (NODPI LDPI MDPI TVDPI HDPI XHDPI XXHDPI XXXHDPI)")
flag.BoolVar(&targetConfig.allowPrereleased, "allow-prereleased", false,
"allow prereleased")
+ flag.BoolVar(&targetConfig.skipSdkCheck, "skip-sdk-check", false, "Skip the SDK version check")
flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file")
flag.Parse()
if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 ||
diff --git a/cmd/extract_apks/main_test.go b/cmd/extract_apks/main_test.go
index f5e4046..9f52877 100644
--- a/cmd/extract_apks/main_test.go
+++ b/cmd/extract_apks/main_test.go
@@ -420,6 +420,374 @@
}
}
+func TestSelectApks_ApexSet_Variants(t *testing.T) {
+ testCases := []testDesc{
+ {
+ protoText: `
+variant {
+ targeting {
+ sdk_version_targeting {value {min {value: 29}}}
+ multi_abi_targeting {
+ value {abi {alias: ARMEABI_V7A}}
+ alternatives {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: ARM64_V8A}}
+ alternatives {abi {alias: X86}}
+ alternatives {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ }
+ }
+ apk_set {
+ module_metadata {
+ name: "base"
+ delivery_type: INSTALL_TIME
+ }
+ apk_description {
+ targeting {
+ multi_abi_targeting {
+ value {abi {alias: ARMEABI_V7A}}
+ alternatives {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: ARM64_V8A}}
+ alternatives {abi {alias: X86}}
+ alternatives {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ }
+ }
+ path: "standalones/standalone-armeabi_v7a.apex"
+ }
+ }
+ variant_number: 0
+}
+variant {
+ targeting {
+ sdk_version_targeting {value {min {value: 29}}}
+ multi_abi_targeting {
+ value {abi {alias: ARM64_V8A}}
+ alternatives {abi {alias: ARMEABI_V7A}}
+ alternatives {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: X86}}
+ alternatives {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ }
+ }
+ apk_set {
+ module_metadata {
+ name: "base"
+ delivery_type: INSTALL_TIME
+ }
+ apk_description {
+ targeting {
+ multi_abi_targeting {
+ value {abi {alias: ARM64_V8A}}
+ alternatives {abi {alias: ARMEABI_V7A}}
+ alternatives {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: X86}}
+ alternatives {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ }
+ }
+ path: "standalones/standalone-arm64_v8a.apex"
+ }
+ }
+ variant_number: 1
+}
+variant {
+ targeting {
+ sdk_version_targeting {value {min {value: 29}}}
+ multi_abi_targeting {
+ value {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: ARMEABI_V7A}}
+ alternatives {abi {alias: ARM64_V8A}}
+ alternatives {abi {alias: X86}}
+ alternatives {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ }
+ }
+ apk_set {
+ module_metadata {
+ name: "base"
+ delivery_type: INSTALL_TIME
+ }
+ apk_description {
+ targeting {
+ multi_abi_targeting {
+ value {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: ARMEABI_V7A}}
+ alternatives {abi {alias: ARM64_V8A}}
+ alternatives {abi {alias: X86}}
+ alternatives {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ }
+ }
+ path: "standalones/standalone-armeabi_v7a.arm64_v8a.apex"
+ }
+ }
+ variant_number: 2
+}
+variant {
+ targeting {
+ sdk_version_targeting {value {min {value: 29}}}
+ multi_abi_targeting {
+ value {abi {alias: X86}}
+ alternatives {abi {alias: ARMEABI_V7A}}
+ alternatives {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: ARM64_V8A}}
+ alternatives {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ }
+ }
+ apk_set {
+ module_metadata {
+ name: "base"
+ delivery_type: INSTALL_TIME
+ }
+ apk_description {
+ targeting {
+ multi_abi_targeting {
+ value {abi {alias: X86}}
+ alternatives {abi {alias: ARMEABI_V7A}}
+ alternatives {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: ARM64_V8A}}
+ alternatives {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ }
+ }
+ path: "standalones/standalone-x86.apex"
+ }
+ }
+ variant_number: 3
+}
+variant {
+ targeting {
+ sdk_version_targeting {value {min {value: 29}}}
+ multi_abi_targeting {
+ value {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ alternatives {abi {alias: ARMEABI_V7A}}
+ alternatives {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: ARM64_V8A}}
+ alternatives {abi {alias: X86}}
+ }
+ }
+ apk_set {
+ module_metadata {
+ name: "base"
+ delivery_type: INSTALL_TIME
+ }
+ apk_description {
+ targeting {
+ multi_abi_targeting {
+ value {
+ abi {alias: X86}
+ abi {alias: X86_64}
+ }
+ alternatives {abi {alias: ARMEABI_V7A}}
+ alternatives {
+ abi {alias: ARMEABI_V7A}
+ abi {alias: ARM64_V8A}
+ }
+ alternatives {abi {alias: ARM64_V8A}}
+ alternatives {abi {alias: X86}}
+ }
+ }
+ path: "standalones/standalone-x86.x86_64.apex"
+ }
+ }
+ variant_number: 4
+}
+`,
+ configs: []testConfigDesc{
+ {
+ name: "multi-variant multi-target ARM",
+ targetConfig: TargetConfig{
+ sdkVersion: 33,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARM64_V8A: 0,
+ bp.Abi_ARMEABI_V7A: 1,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-armeabi_v7a.arm64_v8a.apex",
+ },
+ },
+ },
+ {
+ name: "multi-variant single-target arm",
+ targetConfig: TargetConfig{
+ sdkVersion: 33,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARMEABI_V7A: 0,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-armeabi_v7a.apex",
+ },
+ },
+ },
+ {
+ name: "multi-variant single-target arm64",
+ targetConfig: TargetConfig{
+ sdkVersion: 33,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARM64_V8A: 0,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-arm64_v8a.apex",
+ },
+ },
+ },
+ {
+ name: "multi-variant multi-target x86",
+ targetConfig: TargetConfig{
+ sdkVersion: 33,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_X86: 0,
+ bp.Abi_X86_64: 1,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-x86.x86_64.apex",
+ },
+ },
+ },
+ {
+ name: "multi-variant single-target x86",
+ targetConfig: TargetConfig{
+ sdkVersion: 33,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_X86: 0,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-x86.apex",
+ },
+ },
+ },
+ {
+ name: "multi-variant single-target x86_64",
+ targetConfig: TargetConfig{
+ sdkVersion: 33,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_X86_64: 0,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-x86.x86_64.apex",
+ }},
+ },
+ {
+ name: "multi-variant multi-target cross-target",
+ targetConfig: TargetConfig{
+ sdkVersion: 33,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARM64_V8A: 0,
+ bp.Abi_X86_64: 1,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-arm64_v8a.apex",
+ },
+ },
+ },
+ },
+ },
+ }
+ for _, testCase := range testCases {
+ var toc bp.BuildApksResult
+ if err := prototext.Unmarshal([]byte(testCase.protoText), &toc); err != nil {
+ t.Fatal(err)
+ }
+ for _, config := range testCase.configs {
+ t.Run(config.name, func(t *testing.T) {
+ actual := selectApks(&toc, config.targetConfig)
+ if !reflect.DeepEqual(config.expected, actual) {
+ t.Errorf("expected %v, got %v", config.expected, actual)
+ }
+ })
+ }
+ }
+}
+
type testZip2ZipWriter struct {
entries map[string]string
}
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 712c7fc..e3d1179 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -359,7 +359,8 @@
}
for _, file := range inputZip.Entries() {
pyPkg := getPackage(file.Name)
- if filepath.Base(file.Name) == "__init__.py" {
+ baseName := filepath.Base(file.Name)
+ if baseName == "__init__.py" || baseName == "__init__.pyc" {
if _, found := initedPackages[pyPkg]; found {
panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q", file.Name))
}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 7cb8ab7..0212075 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -48,6 +48,10 @@
var outDir = flag.String("out", "", "path to store output directories (defaults to tmpdir under $OUT when empty)")
var alternateResultDir = flag.Bool("dist", false, "write select results to $DIST_DIR (or <out>/dist when empty)")
+var bazelMode = flag.Bool("bazel-mode", false, "use bazel for analysis of certain modules")
+var bazelModeStaging = flag.Bool("bazel-mode-staging", false, "use bazel for analysis of certain near-ready modules")
+var bazelModeDev = flag.Bool("bazel-mode-dev", false, "use bazel for analysis of a large number of modules (less stable)")
+
var onlyConfig = flag.Bool("only-config", false, "Only run product config (not Soong or Kati)")
var onlySoong = flag.Bool("only-soong", false, "Only run product config and Soong (not Kati)")
@@ -214,6 +218,31 @@
return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
}
+func getBazelArg() string {
+ count := 0
+ str := ""
+ if *bazelMode {
+ count++
+ str = "--bazel-mode"
+ }
+ if *bazelModeStaging {
+ count++
+ str = "--bazel-mode-staging"
+ }
+ if *bazelModeDev {
+ count++
+ str = "--bazel-mode-dev"
+ }
+
+ if count > 1 {
+ // Can't set more than one
+ fmt.Errorf("Only one bazel mode is permitted to be set.")
+ os.Exit(1)
+ }
+
+ return str
+}
+
func main() {
stdio := terminal.StdioImpl{}
@@ -292,7 +321,7 @@
jobs = runtime.NumCPU() / 4
ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
- if ramJobs := ramGb / 30; ramGb > 0 && jobs > ramJobs {
+ if ramJobs := ramGb / 40; ramGb > 0 && jobs > ramJobs {
jobs = ramJobs
}
@@ -472,6 +501,11 @@
args = append(args, "--soong-only")
}
+ bazelStr := getBazelArg()
+ if bazelStr != "" {
+ args = append(args, bazelStr)
+ }
+
cmd := exec.Command(mpctx.SoongUi, args...)
cmd.Stdout = consoleLogWriter
cmd.Stderr = consoleLogWriter
@@ -481,7 +515,8 @@
"TARGET_BUILD_VARIANT="+*buildVariant,
"TARGET_BUILD_TYPE=release",
"TARGET_BUILD_APPS=",
- "TARGET_BUILD_UNBUNDLED=")
+ "TARGET_BUILD_UNBUNDLED=",
+ "USE_RBE=false") // Disabling RBE saves ~10 secs per product
if *alternateResultDir {
cmd.Env = append(cmd.Env,
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 91e3540..fc56dd5 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -390,7 +390,7 @@
}
}
- const maxErrors = 10
+ const maxErrors = 25
if len(incorrectOutputDirectoryErrors) > 0 {
errorMessage := ""
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 7c500b7..2c35d76 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -15,15 +15,17 @@
package main
import (
+ "bytes"
"flag"
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"android/soong/android"
+ "android/soong/android/allowlists"
+ "android/soong/bazel"
"android/soong/bp2build"
"android/soong/shared"
"android/soong/ui/metrics/bp2build_metrics_proto"
@@ -36,36 +38,26 @@
var (
topDir string
- outDir string
- soongOutDir string
availableEnvFile string
usedEnvFile string
- runGoTests bool
-
globFile string
globListDir string
delveListen string
delvePath string
- moduleGraphFile string
- moduleActionsFile string
- docFile string
- bazelQueryViewDir string
- bp2buildMarker string
-
- cmdlineArgs bootstrap.Args
+ cmdlineArgs android.CmdArgs
)
func init() {
// Flags that make sense in every mode
flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
- flag.StringVar(&soongOutDir, "soong_out", "", "Soong output directory (usually $TOP/out/soong)")
+ flag.StringVar(&cmdlineArgs.SoongOutDir, "soong_out", "", "Soong output directory (usually $TOP/out/soong)")
flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
- flag.StringVar(&outDir, "out", "", "the ninja builddir directory")
+ flag.StringVar(&cmdlineArgs.OutDir, "out", "", "the ninja builddir directory")
flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
// Debug flags
@@ -77,17 +69,26 @@
flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
// Flags representing various modes soong_build can run in
- flag.StringVar(&moduleGraphFile, "module_graph_file", "", "JSON module graph file to output")
- flag.StringVar(&moduleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules")
- flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
- flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
- flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
+ flag.StringVar(&cmdlineArgs.ModuleGraphFile, "module_graph_file", "", "JSON module graph file to output")
+ flag.StringVar(&cmdlineArgs.ModuleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules")
+ flag.StringVar(&cmdlineArgs.DocFile, "soong_docs", "", "build documentation file to output")
+ flag.StringVar(&cmdlineArgs.BazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
+ flag.StringVar(&cmdlineArgs.BazelApiBp2buildDir, "bazel_api_bp2build_dir", "", "path to the bazel api_bp2build directory relative to --top")
+ flag.StringVar(&cmdlineArgs.Bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
+ flag.StringVar(&cmdlineArgs.SymlinkForestMarker, "symlink_forest_marker", "", "If set, create the bp2build symlink forest, touch the specified marker file, then exit")
flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
+ flag.StringVar(&cmdlineArgs.BazelForceEnabledModules, "bazel-force-enabled-modules", "", "additional modules to build with Bazel. Comma-delimited")
flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
+ flag.BoolVar(&cmdlineArgs.MultitreeBuild, "multitree-build", false, "this is a multitree build")
+ flag.BoolVar(&cmdlineArgs.BazelMode, "bazel-mode", false, "use bazel for analysis of certain modules")
+ flag.BoolVar(&cmdlineArgs.BazelModeStaging, "bazel-mode-staging", false, "use bazel for analysis of certain near-ready modules")
+ flag.BoolVar(&cmdlineArgs.BazelModeDev, "bazel-mode-dev", false, "use bazel for analysis of a large number of modules (less stable)")
+ flag.BoolVar(&cmdlineArgs.UseBazelProxy, "use-bazel-proxy", false, "communicate with bazel using unix socket proxy instead of spawning subprocesses")
+ flag.BoolVar(&cmdlineArgs.BuildFromTextStub, "build-from-text-stub", false, "build Java stubs from API text files instead of source files")
- // Flags that probably shouldn't be flags of soong_build but we haven't found
+ // Flags that probably shouldn't be flags of soong_build, but we haven't found
// the time to remove them yet
- flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap")
+ flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
// Disable deterministic randomization in the protobuf package, so incremental
// builds with unrelated Soong changes don't trigger large rebuilds (since we
@@ -102,195 +103,269 @@
func newContext(configuration android.Config) *android.Context {
ctx := android.NewContext(configuration)
- ctx.Register()
ctx.SetNameInterface(newNameResolver(configuration))
ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
+ ctx.AddIncludeTags(configuration.IncludeTags()...)
+ ctx.AddSourceRootDirs(configuration.SourceRootDirs()...)
return ctx
}
-func newConfig(availableEnv map[string]string) android.Config {
- configuration, err := android.NewConfig(cmdlineArgs.ModuleListFile, runGoTests, outDir, soongOutDir, availableEnv)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s", err)
- os.Exit(1)
+// 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(ctx *android.Context, extraNinjaDeps []string) string {
+ ctx.EventHandler.Begin("mixed_build")
+ defer ctx.EventHandler.End("mixed_build")
+
+ bazelHook := func() error {
+ return ctx.Config().BazelContext.InvokeBazel(ctx.Config(), ctx)
}
- 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) {
- firstCtx.EventHandler.Begin("mixed_build")
- defer firstCtx.EventHandler.End("mixed_build")
-
- firstCtx.EventHandler.Begin("prepare")
- bootstrap.RunBlueprint(cmdlineArgs, bootstrap.StopBeforeWriteNinja, firstCtx.Context, configuration)
- firstCtx.EventHandler.End("prepare")
-
- firstCtx.EventHandler.Begin("bazel")
- // 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)
- }
- // 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)
- }
- firstCtx.EventHandler.End("bazel")
-
- secondCtx := newContext(secondConfig)
- secondCtx.EventHandler = firstCtx.EventHandler
- secondCtx.EventHandler.Begin("analyze")
- ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, secondCtx.Context, secondConfig)
+ ctx.SetBeforePrepareBuildActionsHook(bazelHook)
+ ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs.Args, bootstrap.DoEverything, ctx.Context, ctx.Config())
ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- secondCtx.EventHandler.End("analyze")
- globListFiles := writeBuildGlobsNinjaFile(secondCtx, configuration.SoongOutDir(), configuration)
- ninjaDeps = append(ninjaDeps, globListFiles...)
+ bazelPaths, err := readFileLines(ctx.Config().Getenv("BAZEL_DEPS_FILE"))
+ if err != nil {
+ panic("Bazel deps file not found: " + err.Error())
+ }
+ ninjaDeps = append(ninjaDeps, bazelPaths...)
+ ninjaDeps = append(ninjaDeps, writeBuildGlobsNinjaFile(ctx)...)
- writeDepFile(cmdlineArgs.OutFile, *secondCtx.EventHandler, ninjaDeps)
+ writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
+ return cmdlineArgs.OutFile
}
// Run the code-generation phase to convert BazelTargetModules to BUILD files.
-func runQueryView(queryviewDir, queryviewMarker string, configuration android.Config, ctx *android.Context) {
+func runQueryView(queryviewDir, queryviewMarker string, 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 {
- fmt.Fprintf(os.Stderr, "%s", err)
- os.Exit(1)
- }
-
+ codegenContext := bp2build.NewCodegenContext(ctx.Config(), ctx, bp2build.QueryView, topDir)
+ err := createBazelWorkspace(codegenContext, shared.JoinPath(topDir, queryviewDir), false)
+ maybeQuit(err, "")
touch(shared.JoinPath(topDir, queryviewMarker))
}
-func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler, metricsDir string) {
+// Run the code-generation phase to convert API contributions to BUILD files.
+// Return marker file for the new synthetic workspace
+func runApiBp2build(ctx *android.Context, extraNinjaDeps []string) string {
+ ctx.EventHandler.Begin("api_bp2build")
+ defer ctx.EventHandler.End("api_bp2build")
+ // api_bp2build does not run the typical pipeline of soong mutators.
+ // Hoevever, it still runs the defaults mutator which can create dependencies.
+ // These dependencies might not always exist (e.g. in tests)
+ ctx.SetAllowMissingDependencies(ctx.Config().AllowMissingDependencies())
+ ctx.RegisterForApiBazelConversion()
+
+ // Register the Android.bp files in the tree
+ // Add them to the workspace's .d file
+ ctx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+ if paths, err := ctx.ListModulePaths("."); err == nil {
+ extraNinjaDeps = append(extraNinjaDeps, paths...)
+ } else {
+ panic(err)
+ }
+
+ // Run the loading and analysis phase
+ ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs.Args,
+ bootstrap.StopBeforePrepareBuildActions,
+ ctx.Context,
+ ctx.Config())
+ ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+
+ // Add the globbed dependencies
+ ninjaDeps = append(ninjaDeps, writeBuildGlobsNinjaFile(ctx)...)
+
+ // Run codegen to generate BUILD files
+ codegenContext := bp2build.NewCodegenContext(ctx.Config(), ctx, bp2build.ApiBp2build, topDir)
+ absoluteApiBp2buildDir := shared.JoinPath(topDir, cmdlineArgs.BazelApiBp2buildDir)
+ // Always generate bp2build_all_srcs filegroups in api_bp2build.
+ // This is necessary to force each Android.bp file to create an equivalent BUILD file
+ // and prevent package boundray issues.
+ // e.g.
+ // Source
+ // f/b/Android.bp
+ // java_library{
+ // name: "foo",
+ // api: "api/current.txt",
+ // }
+ //
+ // f/b/api/Android.bp <- will cause package boundary issues
+ //
+ // Gen
+ // f/b/BUILD
+ // java_contribution{
+ // name: "foo.contribution",
+ // api: "//f/b/api:current.txt",
+ // }
+ //
+ // If we don't generate f/b/api/BUILD, foo.contribution will be unbuildable.
+ err := createBazelWorkspace(codegenContext, absoluteApiBp2buildDir, true)
+ maybeQuit(err, "")
+ ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
+
+ // Create soong_injection repository
+ soongInjectionFiles, err := bp2build.CreateSoongInjectionDirFiles(codegenContext, bp2build.CreateCodegenMetrics())
+ maybeQuit(err, "")
+ absoluteSoongInjectionDir := shared.JoinPath(topDir, ctx.Config().SoongOutDir(), bazel.SoongInjectionDirName)
+ for _, file := range soongInjectionFiles {
+ // The API targets in api_bp2build workspace do not have any dependency on api_bp2build.
+ // But we need to create these files to prevent errors during Bazel analysis.
+ // These need to be created in Read-Write mode.
+ // This is because the subsequent step (bp2build in api domain analysis) creates them in Read-Write mode
+ // to allow users to edit/experiment in the synthetic workspace.
+ writeReadWriteFile(absoluteSoongInjectionDir, file)
+ }
+
+ workspace := shared.JoinPath(ctx.Config().SoongOutDir(), "api_bp2build")
+ // Create the symlink forest
+ symlinkDeps, _, _ := bp2build.PlantSymlinkForest(
+ ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE"),
+ topDir,
+ workspace,
+ cmdlineArgs.BazelApiBp2buildDir,
+ apiBuildFileExcludes(ctx))
+ ninjaDeps = append(ninjaDeps, symlinkDeps...)
+
+ workspaceMarkerFile := workspace + ".marker"
+ writeDepFile(workspaceMarkerFile, ctx.EventHandler, ninjaDeps)
+ touch(shared.JoinPath(topDir, workspaceMarkerFile))
+ return workspaceMarkerFile
+}
+
+// With some exceptions, api_bp2build does not have any dependencies on the checked-in BUILD files
+// Exclude them from the generated workspace to prevent unrelated errors during the loading phase
+func apiBuildFileExcludes(ctx *android.Context) []string {
+ ret := bazelArtifacts()
+ srcs, err := getExistingBazelRelatedFiles(topDir)
+ maybeQuit(err, "Error determining existing Bazel-related files")
+ for _, src := range srcs {
+ // Exclude all src BUILD files
+ if src != "WORKSPACE" &&
+ src != "BUILD" &&
+ src != "BUILD.bazel" &&
+ !strings.HasPrefix(src, "build/bazel") &&
+ !strings.HasPrefix(src, "external/bazel-skylib") &&
+ !strings.HasPrefix(src, "prebuilts/clang") {
+ ret = append(ret, src)
+ }
+ }
+ // Android.bp files for api surfaces are mounted to out/, but out/ should not be a
+ // dep for api_bp2build. Otherwise, api_bp2build will be run every single time
+ ret = append(ret, ctx.Config().OutDir())
+ return ret
+}
+
+func writeNinjaHint(ctx *android.Context) error {
+ wantModules := make([]string, len(allowlists.HugeModulesMap))
+ i := 0
+ for k := range allowlists.HugeModulesMap {
+ wantModules[i] = k
+ i += 1
+ }
+ outputsMap := ctx.Context.GetOutputsFromModuleNames(wantModules)
+ var outputBuilder strings.Builder
+ for k, v := range allowlists.HugeModulesMap {
+ for _, output := range outputsMap[k] {
+ outputBuilder.WriteString(fmt.Sprintf("%s,%d\n", output, v))
+ }
+ }
+ weightListFile := filepath.Join(topDir, ctx.Config().OutDir(), ".ninja_weight_list")
+
+ err := os.WriteFile(weightListFile, []byte(outputBuilder.String()), 0644)
+ if err != nil {
+ return fmt.Errorf("could not write ninja weight list file %s", err)
+ }
+ return nil
+}
+
+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)
- }
+ maybeQuit(err, "error writing soong_build metrics %s", metricsFile)
}
-func writeJsonModuleGraphAndActions(ctx *android.Context, graphPath string, actionsPath string) {
- graphFile, graphErr := os.Create(shared.JoinPath(topDir, graphPath))
- actionsFile, actionsErr := os.Create(shared.JoinPath(topDir, actionsPath))
- if graphErr != nil || actionsErr != nil {
- fmt.Fprintf(os.Stderr, "Graph err: %s, actions err: %s", graphErr, actionsErr)
- os.Exit(1)
- }
-
+func writeJsonModuleGraphAndActions(ctx *android.Context, cmdArgs android.CmdArgs) {
+ graphFile, graphErr := os.Create(shared.JoinPath(topDir, cmdArgs.ModuleGraphFile))
+ maybeQuit(graphErr, "graph err")
defer graphFile.Close()
+ actionsFile, actionsErr := os.Create(shared.JoinPath(topDir, cmdArgs.ModuleActionsFile))
+ maybeQuit(actionsErr, "actions err")
defer actionsFile.Close()
ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile)
}
-func writeBuildGlobsNinjaFile(ctx *android.Context, buildDir string, config interface{}) []string {
+func writeBuildGlobsNinjaFile(ctx *android.Context) []string {
ctx.EventHandler.Begin("globs_ninja_file")
defer ctx.EventHandler.End("globs_ninja_file")
- globDir := bootstrap.GlobDirectory(buildDir, globListDir)
+ globDir := bootstrap.GlobDirectory(ctx.Config().SoongOutDir(), globListDir)
bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{
GlobLister: ctx.Globs,
GlobFile: globFile,
GlobDir: globDir,
SrcDir: ctx.SrcDir(),
- }, config)
+ }, ctx.Config())
return bootstrap.GlobFileListFiles(globDir)
}
-func writeDepFile(outputFile string, eventHandler metrics.EventHandler, 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 {
- fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", depFile, err)
- os.Exit(1)
- }
+ maybeQuit(err, "error writing depfile '%s'", depFile)
}
-// 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, logDir string) string {
- mixedModeBuild := configuration.BazelContext.BazelEnabled()
- generateBazelWorkspace := bp2buildMarker != ""
- generateQueryView := bazelQueryViewDir != ""
- generateModuleGraphFile := moduleGraphFile != ""
- generateDocFile := docFile != ""
+// runSoongOnlyBuild runs the standard Soong build in a number of different modes.
+func runSoongOnlyBuild(ctx *android.Context, extraNinjaDeps []string) string {
+ ctx.EventHandler.Begin("soong_build")
+ defer ctx.EventHandler.End("soong_build")
- if generateBazelWorkspace {
- // Run the alternate pipeline of bp2build mutators and singleton to convert
- // Blueprint to BUILD files before everything else.
- runBp2Build(configuration, extraNinjaDeps)
- return bp2buildMarker
+ var stopBefore bootstrap.StopBefore
+ switch ctx.Config().BuildMode {
+ case android.GenerateModuleGraph:
+ stopBefore = bootstrap.StopBeforeWriteNinja
+ case android.GenerateQueryView, android.GenerateDocFile:
+ stopBefore = bootstrap.StopBeforePrepareBuildActions
+ default:
+ stopBefore = bootstrap.DoEverything
}
- blueprintArgs := cmdlineArgs
+ ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs.Args, stopBefore, ctx.Context, ctx.Config())
+ ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- 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
- }
+ globListFiles := writeBuildGlobsNinjaFile(ctx)
+ ninjaDeps = append(ninjaDeps, globListFiles...)
- ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, stopBefore, ctx.Context, configuration)
- ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-
- 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, *ctx.EventHandler, ninjaDeps)
- return queryviewMarkerFile
- } else if generateModuleGraphFile {
- writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
- writeDepFile(moduleGraphFile, *ctx.EventHandler, ninjaDeps)
- return moduleGraphFile
- } else if generateDocFile {
- // TODO: we could make writeDocs() return the list of documentation files
- // written and add them to the .d file. Then soong_docs would be re-run
- // whenever one is deleted.
- if err := writeDocs(ctx, shared.JoinPath(topDir, docFile)); err != nil {
- fmt.Fprintf(os.Stderr, "error building Soong documentation: %s\n", err)
- os.Exit(1)
- }
- writeDepFile(docFile, *ctx.EventHandler, ninjaDeps)
- return docFile
- } else {
- // The actual output (build.ninja) was written in the RunBlueprint() call
- // above
- writeDepFile(cmdlineArgs.OutFile, *ctx.EventHandler, ninjaDeps)
- }
+ // Convert the Soong module graph into Bazel BUILD files.
+ switch ctx.Config().BuildMode {
+ case android.GenerateQueryView:
+ queryviewMarkerFile := cmdlineArgs.BazelQueryViewDir + ".marker"
+ runQueryView(cmdlineArgs.BazelQueryViewDir, queryviewMarkerFile, ctx)
+ writeDepFile(queryviewMarkerFile, ctx.EventHandler, ninjaDeps)
+ return queryviewMarkerFile
+ case android.GenerateModuleGraph:
+ writeJsonModuleGraphAndActions(ctx, cmdlineArgs)
+ writeDepFile(cmdlineArgs.ModuleGraphFile, ctx.EventHandler, ninjaDeps)
+ return cmdlineArgs.ModuleGraphFile
+ case android.GenerateDocFile:
+ // TODO: we could make writeDocs() return the list of documentation files
+ // written and add them to the .d file. Then soong_docs would be re-run
+ // whenever one is deleted.
+ err := writeDocs(ctx, shared.JoinPath(topDir, cmdlineArgs.DocFile))
+ maybeQuit(err, "error building Soong documentation")
+ writeDepFile(cmdlineArgs.DocFile, ctx.EventHandler, ninjaDeps)
+ return cmdlineArgs.DocFile
+ default:
+ // The actual output (build.ninja) was written in the RunBlueprint() call
+ // above
+ writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
+ return cmdlineArgs.OutFile
}
-
- writeMetrics(configuration, *ctx.EventHandler, logDir)
- return cmdlineArgs.OutFile
}
// soong_ui dumps the available environment variables to
@@ -311,13 +386,8 @@
fmt.Fprintf(os.Stderr, "--available_env not set\n")
os.Exit(1)
}
-
result, err := shared.EnvFromFile(shared.JoinPath(topDir, availableEnvFile))
- if err != nil {
- fmt.Fprintf(os.Stderr, "error reading available environment file '%s': %s\n", availableEnvFile, err)
- os.Exit(1)
- }
-
+ maybeQuit(err, "error reading available environment file '%s'", availableEnvFile)
return result
}
@@ -328,17 +398,13 @@
android.InitSandbox(topDir)
availableEnv := parseAvailableEnv()
-
- configuration := newConfig(availableEnv)
- extraNinjaDeps := []string{
- configuration.ProductVariablesFileName,
- usedEnvFile,
- }
-
+ configuration, err := android.NewConfig(cmdlineArgs, availableEnv)
+ maybeQuit(err, "")
if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
configuration.SetAllowMissingDependencies()
}
+ extraNinjaDeps := []string{configuration.ProductVariablesFileName, usedEnvFile}
if shared.IsDebugging() {
// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
// enabled even if it completed successfully.
@@ -347,119 +413,75 @@
// 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"]
+ metricsDir := availableEnv["LOG_DIR"]
- finalOutputFile := doChosenActivity(configuration, extraNinjaDeps, logDir)
+ ctx := newContext(configuration)
- writeUsedEnvironmentFile(configuration, finalOutputFile)
+ var finalOutputFile string
+
+ // Run Soong for a specific activity, like bp2build, queryview
+ // or the actual Soong build for the build.ninja file.
+ switch configuration.BuildMode {
+ case android.SymlinkForest:
+ finalOutputFile = runSymlinkForestCreation(ctx, extraNinjaDeps, metricsDir)
+ case android.Bp2build:
+ // Run the alternate pipeline of bp2build mutators and singleton to convert
+ // Blueprint to BUILD files before everything else.
+ finalOutputFile = runBp2Build(ctx, extraNinjaDeps, metricsDir)
+ case android.ApiBp2build:
+ finalOutputFile = runApiBp2build(ctx, extraNinjaDeps)
+ writeMetrics(configuration, ctx.EventHandler, metricsDir)
+ default:
+ ctx.Register()
+ if configuration.IsMixedBuildsEnabled() {
+ finalOutputFile = runMixedModeBuild(ctx, extraNinjaDeps)
+ } else {
+ finalOutputFile = runSoongOnlyBuild(ctx, extraNinjaDeps)
+ }
+ if ctx.Config().IsEnvTrue("SOONG_GENERATES_NINJA_HINT") {
+ writeNinjaHint(ctx)
+ }
+ writeMetrics(configuration, ctx.EventHandler, metricsDir)
+ }
+ writeUsedEnvironmentFile(configuration)
+
+ // Touch the output file so that it's the newest file created by soong_build.
+ // This is necessary because, if soong_build generated any files which
+ // are ninja inputs to the main output file, then ninja would superfluously
+ // rebuild this output file on the next build invocation.
+ touch(shared.JoinPath(topDir, finalOutputFile))
}
-func writeUsedEnvironmentFile(configuration android.Config, finalOutputFile string) {
+func writeUsedEnvironmentFile(configuration android.Config) {
if usedEnvFile == "" {
return
}
path := shared.JoinPath(topDir, usedEnvFile)
data, err := shared.EnvFileContents(configuration.EnvDeps())
- if err != nil {
- fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err)
- os.Exit(1)
- }
+ maybeQuit(err, "error writing used environment file '%s'\n", usedEnvFile)
- err = ioutil.WriteFile(path, data, 0666)
- if err != nil {
- fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err)
- os.Exit(1)
+ if preexistingData, err := os.ReadFile(path); err != nil {
+ if !os.IsNotExist(err) {
+ maybeQuit(err, "error reading used environment file '%s'", usedEnvFile)
+ }
+ } else if bytes.Equal(preexistingData, data) {
+ // used environment file is unchanged
+ return
}
-
- // Touch the output file so that it's not older than the file we just
- // wrote. We can't write the environment file earlier because one an access
- // new environment variables while writing it.
- touch(shared.JoinPath(topDir, finalOutputFile))
+ err = os.WriteFile(path, data, 0666)
+ maybeQuit(err, "error writing used environment file '%s'", usedEnvFile)
}
func touch(path string) {
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err)
- os.Exit(1)
- }
-
+ maybeQuit(err, "Error touching '%s'", path)
err = f.Close()
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err)
- os.Exit(1)
- }
+ maybeQuit(err, "Error touching '%s'", path)
currentTime := time.Now().Local()
err = os.Chtimes(path, currentTime, currentTime)
- if err != nil {
- fmt.Fprintf(os.Stderr, "error touching '%s': %s\n", path, err)
- os.Exit(1)
- }
-}
-
-// Find BUILD files in the srcDir which...
-//
-// - are not on the allow list (android/bazel.go#ShouldKeepExistingBuildFileForDir())
-//
-// - won't be overwritten by corresponding bp2build generated files
-//
-// And return their paths so they can be left out of the Bazel workspace dir (i.e. ignored)
-func getPathsToIgnoredBuildFiles(topDir string, generatedRoot string, srcDirBazelFiles []string, verbose bool) []string {
- paths := make([]string, 0)
-
- for _, srcDirBazelFileRelativePath := range srcDirBazelFiles {
- srcDirBazelFileFullPath := shared.JoinPath(topDir, srcDirBazelFileRelativePath)
- fileInfo, err := os.Stat(srcDirBazelFileFullPath)
- if err != nil {
- // Warn about error, but continue trying to check files
- fmt.Fprintf(os.Stderr, "WARNING: Error accessing path '%s', err: %s\n", srcDirBazelFileFullPath, err)
- continue
- }
- if fileInfo.IsDir() {
- // Don't ignore entire directories
- continue
- }
- if !(fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") {
- // Don't ignore this file - it is not a build file
- continue
- }
- srcDirBazelFileDir := filepath.Dir(srcDirBazelFileRelativePath)
- if android.ShouldKeepExistingBuildFileForDir(srcDirBazelFileDir) {
- // Don't ignore this existing build file
- continue
- }
- correspondingBp2BuildFile := shared.JoinPath(topDir, generatedRoot, srcDirBazelFileRelativePath)
- if _, err := os.Stat(correspondingBp2BuildFile); err == nil {
- // If bp2build generated an alternate BUILD file, don't exclude this workspace path
- // BUILD file clash resolution happens later in the symlink forest creation
- continue
- }
- if verbose {
- fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath)
- }
- paths = append(paths, srcDirBazelFileRelativePath)
- }
-
- return paths
-}
-
-// Returns temporary symlink forest excludes necessary for bazel build //external/... (and bazel build //frameworks/...) to work
-func getTemporaryExcludes() []string {
- excludes := make([]string, 0)
-
- // FIXME: 'autotest_lib' is a symlink back to external/autotest, and this causes an infinite symlink expansion error for Bazel
- excludes = append(excludes, "external/autotest/venv/autotest_lib")
-
- // FIXME: The external/google-fruit/extras/bazel_root/third_party/fruit dir is poison
- // It contains several symlinks back to real source dirs, and those source dirs contain BUILD files we want to ignore
- excludes = append(excludes, "external/google-fruit/extras/bazel_root/third_party/fruit")
-
- // FIXME: 'frameworks/compile/slang' has a filegroup error due to an escaping issue
- excludes = append(excludes, "frameworks/compile/slang")
-
- return excludes
+ maybeQuit(err, "error touching '%s'", path)
}
// Read the bazel.list file that the Soong Finder already dumped earlier (hopefully)
@@ -470,124 +492,215 @@
// Assume this was a relative path under topDir
bazelFinderFile = filepath.Join(topDir, bazelFinderFile)
}
- data, err := ioutil.ReadFile(bazelFinderFile)
- if err != nil {
- return nil, err
+ return readFileLines(bazelFinderFile)
+}
+
+func bazelArtifacts() []string {
+ return []string{
+ "bazel-bin",
+ "bazel-genfiles",
+ "bazel-out",
+ "bazel-testlogs",
+ "bazel-workspace",
+ "bazel-" + filepath.Base(topDir),
}
- files := strings.Split(strings.TrimSpace(string(data)), "\n")
- return files, nil
+}
+
+// This could in theory easily be separated into a binary that generically
+// merges two directories into a symlink tree. The main obstacle is that this
+// function currently depends on both Bazel-specific knowledge (the existence
+// of bazel-* symlinks) and configuration (the set of BUILD.bazel files that
+// should and should not be kept)
+//
+// Ideally, bp2build would write a file that contains instructions to the
+// symlink tree creation binary. Then the latter would not need to depend on
+// the very heavy-weight machinery of soong_build .
+func runSymlinkForestCreation(ctx *android.Context, extraNinjaDeps []string, metricsDir string) string {
+ var ninjaDeps []string
+ var mkdirCount, symlinkCount uint64
+
+ ctx.EventHandler.Do("symlink_forest", func() {
+ ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+ verbose := ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE")
+
+ // PlantSymlinkForest() returns all the directories that were readdir()'ed.
+ // Such a directory SHOULD be added to `ninjaDeps` so that a child directory
+ // or file created/deleted under it would trigger an update of the symlink forest.
+ generatedRoot := shared.JoinPath(ctx.Config().SoongOutDir(), "bp2build")
+ workspaceRoot := shared.JoinPath(ctx.Config().SoongOutDir(), "workspace")
+ var symlinkForestDeps []string
+ ctx.EventHandler.Do("plant", func() {
+ symlinkForestDeps, mkdirCount, symlinkCount = bp2build.PlantSymlinkForest(
+ verbose, topDir, workspaceRoot, generatedRoot, excludedFromSymlinkForest(ctx, verbose))
+ })
+ ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
+ })
+
+ writeDepFile(cmdlineArgs.SymlinkForestMarker, ctx.EventHandler, ninjaDeps)
+ touch(shared.JoinPath(topDir, cmdlineArgs.SymlinkForestMarker))
+ codegenMetrics := bp2build.ReadCodegenMetrics(metricsDir)
+ if codegenMetrics == nil {
+ m := bp2build.CreateCodegenMetrics()
+ codegenMetrics = &m
+ } else {
+ //TODO (usta) we cannot determine if we loaded a stale file, i.e. from an unrelated prior
+ //invocation of codegen. We should simply use a separate .pb file
+ }
+ codegenMetrics.SetSymlinkCount(symlinkCount)
+ codegenMetrics.SetMkDirCount(mkdirCount)
+ writeBp2BuildMetrics(codegenMetrics, ctx.EventHandler, metricsDir)
+ return cmdlineArgs.SymlinkForestMarker
+}
+
+func excludedFromSymlinkForest(ctx *android.Context, verbose bool) []string {
+ excluded := bazelArtifacts()
+ if cmdlineArgs.OutDir[0] != '/' {
+ excluded = append(excluded, cmdlineArgs.OutDir)
+ }
+
+ // Find BUILD files in the srcDir which are not in the allowlist
+ // (android.Bp2BuildConversionAllowlist#ShouldKeepExistingBuildFileForDir)
+ // and return their paths so they can be left out of the Bazel workspace dir (i.e. ignored)
+ existingBazelFiles, err := getExistingBazelRelatedFiles(topDir)
+ maybeQuit(err, "Error determining existing Bazel-related files")
+
+ for _, path := range existingBazelFiles {
+ fullPath := shared.JoinPath(topDir, path)
+ fileInfo, err2 := os.Stat(fullPath)
+ if err2 != nil {
+ // Warn about error, but continue trying to check files
+ fmt.Fprintf(os.Stderr, "WARNING: Error accessing path '%s', err: %s\n", fullPath, err2)
+ continue
+ }
+ // Exclude only files named 'BUILD' or 'BUILD.bazel' and unless forcibly kept
+ if fileInfo.IsDir() ||
+ (fileInfo.Name() != "BUILD" && fileInfo.Name() != "BUILD.bazel") ||
+ ctx.Config().Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(filepath.Dir(path)) {
+ // Don't ignore this existing build file
+ continue
+ }
+ if verbose {
+ fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", path)
+ }
+ excluded = append(excluded, path)
+ }
+
+ // Temporarily exclude stuff to make `bazel build //external/...` (and `bazel build //frameworks/...`) work
+ excluded = append(excluded,
+ // FIXME: 'autotest_lib' is a symlink back to external/autotest, and this causes an infinite
+ // symlink expansion error for Bazel
+ "external/autotest/venv/autotest_lib",
+ "external/autotest/autotest_lib",
+ "external/autotest/client/autotest_lib/client",
+
+ // FIXME: The external/google-fruit/extras/bazel_root/third_party/fruit dir is poison
+ // It contains several symlinks back to real source dirs, and those source dirs contain
+ // BUILD files we want to ignore
+ "external/google-fruit/extras/bazel_root/third_party/fruit",
+
+ // FIXME: 'frameworks/compile/slang' has a filegroup error due to an escaping issue
+ "frameworks/compile/slang",
+
+ // FIXME(b/260809113): 'prebuilts/clang/host/linux-x86/clang-dev' is a tool-generated symlink
+ // directory that contains a BUILD file. The bazel files finder code doesn't traverse into symlink dirs,
+ // and hence is not aware of this BUILD file and exclude it accordingly during symlink forest generation
+ // when checking against keepExistingBuildFiles allowlist.
+ //
+ // This is necessary because globs in //prebuilts/clang/host/linux-x86/BUILD
+ // currently assume no subpackages (keepExistingBuildFile is not recursive for that directory).
+ //
+ // This is a bandaid until we the symlink forest logic can intelligently exclude BUILD files found in
+ // source symlink dirs according to the keepExistingBuildFile allowlist.
+ "prebuilts/clang/host/linux-x86/clang-dev",
+ )
+ return excluded
}
// Run Soong in the bp2build mode. This creates a standalone context that registers
// an alternate pipeline of mutators and singletons specifically for generating
// Bazel BUILD files instead of Ninja files.
-func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
- eventHandler := metrics.EventHandler{}
- eventHandler.Begin("bp2build")
+func runBp2Build(ctx *android.Context, extraNinjaDeps []string, metricsDir string) string {
+ var codegenMetrics *bp2build.CodegenMetrics
+ ctx.EventHandler.Do("bp2build", func() {
- // 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 ctx without calling that method.
+ ctx.SetAllowMissingDependencies(ctx.Config().AllowMissingDependencies())
+ ctx.SetNameInterface(newNameResolver(ctx.Config()))
+ ctx.RegisterForBazelConversion()
+ ctx.SetModuleListFile(cmdlineArgs.ModuleListFile)
- // 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()
+ var ninjaDeps []string
+ ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- // 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()
+ // 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.
+ ctx.EventHandler.Do("bootstrap", func() {
+ blueprintArgs := cmdlineArgs
+ bootstrapDeps := bootstrap.RunBlueprint(blueprintArgs.Args,
+ bootstrap.StopBeforePrepareBuildActions, ctx.Context, ctx.Config())
+ ninjaDeps = append(ninjaDeps, bootstrapDeps...)
+ })
- // 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)
- }
+ globListFiles := writeBuildGlobsNinjaFile(ctx)
+ ninjaDeps = append(ninjaDeps, globListFiles...)
- extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
+ // Run the code-generation phase to convert BazelTargetModules to BUILD files
+ // and print conversion codegenMetrics to the user.
+ codegenContext := bp2build.NewCodegenContext(ctx.Config(), ctx, bp2build.Bp2Build, topDir)
+ ctx.EventHandler.Do("codegen", func() {
+ codegenMetrics = bp2build.Codegen(codegenContext)
+ })
- // 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...)
+ ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
- globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx, configuration.SoongOutDir(), configuration)
- ninjaDeps = append(ninjaDeps, globListFiles...)
-
- // 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)
-
- generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
- workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
-
- excludes := []string{
- "bazel-bin",
- "bazel-genfiles",
- "bazel-out",
- "bazel-testlogs",
- "bazel-" + filepath.Base(topDir),
- }
-
- if outDir[0] != '/' {
- excludes = append(excludes, outDir)
- }
-
- existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
- os.Exit(1)
- }
-
- pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
- excludes = append(excludes, pathsToIgnoredBuildFiles...)
-
- 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))
-
- eventHandler.End("bp2build")
+ writeDepFile(cmdlineArgs.Bp2buildMarker, ctx.EventHandler, ninjaDeps)
+ touch(shared.JoinPath(topDir, cmdlineArgs.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, eventHandler)
+ if ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE") {
+ codegenMetrics.Print()
+ }
+ writeBp2BuildMetrics(codegenMetrics, ctx.EventHandler, metricsDir)
+ return cmdlineArgs.Bp2buildMarker
}
// Write Bp2Build metrics into $LOG_DIR
-func writeBp2BuildMetrics(codegenMetrics *bp2build.CodegenMetrics,
- configuration android.Config, eventHandler metrics.EventHandler) {
+func writeBp2BuildMetrics(codegenMetrics *bp2build.CodegenMetrics, eventHandler *metrics.EventHandler, metricsDir string) {
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(),
- })
+ codegenMetrics.AddEvent(&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)
}
codegenMetrics.Write(metricsDir)
}
+
+func readFileLines(path string) ([]string, error) {
+ data, err := os.ReadFile(path)
+ if err == nil {
+ return strings.Split(strings.TrimSpace(string(data)), "\n"), nil
+ }
+ return nil, err
+
+}
+func maybeQuit(err error, format string, args ...interface{}) {
+ if err == nil {
+ return
+ }
+ if format != "" {
+ fmt.Fprintln(os.Stderr, fmt.Sprintf(format, args...)+": "+err.Error())
+ } else {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ os.Exit(1)
+}
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index d63ded5..ce32184 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -15,6 +15,7 @@
package main
import (
+ "io/fs"
"io/ioutil"
"os"
"path/filepath"
@@ -23,18 +24,25 @@
"android/soong/bp2build"
)
-func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error {
- os.RemoveAll(bazelQueryViewDir)
+// A helper function to generate a Read-only Bazel workspace in outDir
+func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string, generateFilegroups bool) error {
+ os.RemoveAll(outDir)
ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
- res, err := bp2build.GenerateBazelTargets(ctx, true)
+ res, err := bp2build.GenerateBazelTargets(ctx, generateFilegroups)
if err != nil {
panic(err)
}
- filesToWrite := bp2build.CreateBazelFiles(ruleShims, res.BuildDirToTargets(), bp2build.QueryView)
+ filesToWrite := bp2build.CreateBazelFiles(ctx.Config(), ruleShims, res.BuildDirToTargets(),
+ ctx.Mode())
+ bazelRcFiles, err2 := CopyBazelRcFiles()
+ if err2 != nil {
+ return err2
+ }
+ filesToWrite = append(filesToWrite, bazelRcFiles...)
for _, f := range filesToWrite {
- if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
+ if err := writeReadOnlyFile(outDir, f); err != nil {
return err
}
}
@@ -42,6 +50,32 @@
return nil
}
+// CopyBazelRcFiles creates BazelFiles for all the bazelrc files under
+// build/bazel. They're needed because the rc files are still read when running
+// queryview, so they have to be in the queryview workspace.
+func CopyBazelRcFiles() ([]bp2build.BazelFile, error) {
+ result := make([]bp2build.BazelFile, 0)
+ err := filepath.WalkDir(filepath.Join(topDir, "build/bazel"), func(path string, info fs.DirEntry, err error) error {
+ if filepath.Ext(path) == ".bazelrc" {
+ contents, err := os.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ path, err = filepath.Rel(topDir, path)
+ if err != nil {
+ return err
+ }
+ result = append(result, bp2build.BazelFile{
+ Dir: filepath.Dir(path),
+ Basename: filepath.Base(path),
+ Contents: string(contents),
+ })
+ }
+ return nil
+ })
+ return result, err
+}
+
// The auto-conversion directory should be read-only, sufficient for bazel query. The files
// are not intended to be edited by end users.
func writeReadOnlyFile(dir string, f bp2build.BazelFile) error {
@@ -57,6 +91,19 @@
return err
}
+func writeReadWriteFile(dir string, f bp2build.BazelFile) error {
+ dir = filepath.Join(dir, f.Dir)
+ if err := createDirectoryIfNonexistent(dir); err != nil {
+ return err
+ }
+ pathToFile := filepath.Join(dir, f.Basename)
+
+ // 0644 is read-write
+ err := ioutil.WriteFile(pathToFile, []byte(f.Contents), 0644)
+
+ return err
+}
+
func createDirectoryIfNonexistent(dir string) error {
if _, err := os.Stat(dir); os.IsNotExist(err) {
return os.MkdirAll(dir, os.ModePerm)
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index a03a86a..301246a 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -58,7 +58,7 @@
stdio func() terminal.StdioInterface
// run the command
- run func(ctx build.Context, config build.Config, args []string, logsDir string)
+ run func(ctx build.Context, config build.Config, args []string)
}
// list of supported commands (flags) supported by soong ui
@@ -66,11 +66,9 @@
{
flag: "--make-mode",
description: "build the modules by the target name (i.e. soong_docs)",
- config: func(ctx build.Context, args ...string) build.Config {
- return build.NewConfig(ctx, args...)
- },
- stdio: stdio,
- run: runMake,
+ config: build.NewConfig,
+ stdio: stdio,
+ run: runMake,
}, {
flag: "--dumpvar-mode",
description: "print the value of the legacy make variable VAR to stdout",
@@ -93,6 +91,15 @@
config: buildActionConfig,
stdio: stdio,
run: runMake,
+ }, {
+ flag: "--upload-metrics-only",
+ description: "upload metrics without building anything",
+ config: uploadOnlyConfig,
+ stdio: stdio,
+ // Upload-only mode mostly skips to the metrics-uploading phase of soong_ui.
+ // However, this invocation marks the true "end of the build", and thus we
+ // need to update the total runtime of the build to include this upload step.
+ run: updateTotalRealTime,
},
}
@@ -112,9 +119,18 @@
return indexList(s, list) != -1
}
+func deleteStaleMetrics(metricsFilePathSlice []string) error {
+ for _, metricsFilePath := range metricsFilePathSlice {
+ if err := os.Remove(metricsFilePath); err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("Failed to remove %s\nError message: %w", metricsFilePath, err)
+ }
+ }
+ return nil
+}
+
// Main execution of soong_ui. The command format is as follows:
//
-// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
+// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
//
// Command is the type of soong_ui execution. Only one type of
// execution is specified. The args are specific to the command.
@@ -134,8 +150,13 @@
build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
+ // Create and start a new metric record.
+ met := metrics.New()
+ met.SetBuildDateTime(buildStarted)
+ met.SetBuildCommand(os.Args)
+
// Attach a new logger instance to the terminal output.
- log := logger.New(output)
+ log := logger.NewWithMetrics(output, met)
defer log.Cleanup()
// Create a context to simplify the program termination process.
@@ -146,14 +167,9 @@
trace := tracer.New(log)
defer trace.Close()
- // Create and start a new metric record.
- met := metrics.New()
- met.SetBuildDateTime(buildStarted)
- met.SetBuildCommand(os.Args)
-
// Create a new Status instance, which manages action counts and event output channels.
stat := &status.Status{}
- defer stat.Finish()
+
// Hook up the terminal output and tracer to Status.
stat.AddOutput(output)
stat.AddOutput(trace.StatusTracer())
@@ -164,42 +180,86 @@
log.Cleanup()
stat.Finish()
})
-
+ criticalPath := status.NewCriticalPath()
buildCtx := build.Context{ContextImpl: &build.ContextImpl{
- Context: ctx,
- Logger: log,
- Metrics: met,
- Tracer: trace,
- Writer: output,
- Status: stat,
+ Context: ctx,
+ Logger: log,
+ Metrics: met,
+ Tracer: trace,
+ Writer: output,
+ Status: stat,
+ CriticalPath: criticalPath,
}}
config := c.config(buildCtx, args...)
+ config.SetLogsPrefix(c.logsPrefix)
+ logsDir := config.LogsDir()
+ buildStarted = config.BuildStartedTimeOrDefault(buildStarted)
- build.SetupOutDir(buildCtx, config)
+ buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
+ soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
+ rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
+ bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
+ bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
+ soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
- if config.UseBazel() && config.Dist() {
- defer populateExternalDistDir(buildCtx, config)
+ //the profile file generated by Bazel"
+ bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
+ metricsFiles := []string{
+ buildErrorFile, // build error strings
+ rbeMetricsFile, // high level metrics related to remote build execution.
+ bp2buildMetricsFile, // high level metrics related to bp2build.
+ soongMetricsFile, // high level metrics related to this build system.
+ bazelMetricsFile, // high level metrics related to bazel execution
+ soongBuildMetricsFile, // high level metrics related to soong build(except bp2build)
+ config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
}
- // Set up files to be outputted in the log directory.
+ os.MkdirAll(logsDir, 0777)
+
+ log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
+
+ trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
+
+ defer func() {
+ stat.Finish()
+ criticalPath.WriteToMetrics(met)
+ met.Dump(soongMetricsFile)
+ if !config.SkipMetricsUpload() {
+ build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
+ }
+ }()
+ c.run(buildCtx, config, args)
+
+}
+
+func logAndSymlinkSetup(buildCtx build.Context, config build.Config) {
+ log := buildCtx.ContextImpl.Logger
+ logsPrefix := config.GetLogsPrefix()
+ build.SetupOutDir(buildCtx, config)
logsDir := config.LogsDir()
// Common list of metric file definition.
- buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
- rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
- soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
+ buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
+ rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
+ soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
+ bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
+ soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
+
+ //Delete the stale metrics files
+ staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
+ if err := deleteStaleMetrics(staleFileSlice); err != nil {
+ log.Fatalln(err)
+ }
build.PrintOutDirWarning(buildCtx, config)
- os.MkdirAll(logsDir, 0777)
- log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
- trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
- stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
- stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
+ stat := buildCtx.Status
+ stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
+ stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
- stat.AddOutput(status.NewCriticalPath(log))
- stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
+ stat.AddOutput(status.NewCriticalPathLogger(log, buildCtx.CriticalPath))
+ stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
@@ -207,21 +267,7 @@
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
- // file. The soong metrics file is then next. Bazel profiles are written
- // before the uploadMetrics is invoked. The written files are then uploaded
- // if the uploading of the metrics is enabled.
- files := []string{
- buildErrorFile, // build error strings
- rbeMetricsFile, // high level metrics related to remote build execution.
- soongMetricsFile, // high level metrics related to this build system.
- config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
- }
- defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
- defer met.Dump(soongMetricsFile)
- }
+ defer build.CheckProdCreds(buildCtx, config)
// Read the time at the starting point.
if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
@@ -236,7 +282,7 @@
}
if executable, err := os.Executable(); err == nil {
- trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
+ buildCtx.ContextImpl.Tracer.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
}
}
@@ -249,8 +295,6 @@
f := build.NewSourceFinder(buildCtx, config)
defer f.Shutdown()
build.FindSources(buildCtx, config, f)
-
- c.run(buildCtx, config, args, logsDir)
}
func fixBadDanglingLink(ctx build.Context, name string) {
@@ -267,8 +311,11 @@
}
}
-func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
+func dumpVar(ctx build.Context, config build.Config, args []string) {
+ logAndSymlinkSetup(ctx, config)
flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
+ flags.SetOutput(ctx.Writer)
+
flags.Usage = func() {
fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
@@ -284,7 +331,7 @@
if flags.NArg() != 1 {
flags.Usage()
- os.Exit(1)
+ ctx.Fatalf("Invalid usage")
}
varName := flags.Arg(0)
@@ -317,8 +364,12 @@
}
}
-func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
+func dumpVars(ctx build.Context, config build.Config, args []string) {
+ logAndSymlinkSetup(ctx, config)
+
flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
+ flags.SetOutput(ctx.Writer)
+
flags.Usage = func() {
fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
@@ -342,7 +393,7 @@
if flags.NArg() != 0 {
flags.Usage()
- os.Exit(1)
+ ctx.Fatalf("Invalid usage")
}
vars := strings.Fields(*varsStr)
@@ -400,8 +451,18 @@
return build.NewConfig(ctx)
}
+// uploadOnlyConfig explicitly requires no arguments.
+func uploadOnlyConfig(ctx build.Context, args ...string) build.Config {
+ if len(args) > 0 {
+ fmt.Printf("--upload-only does not require arguments.")
+ }
+ return build.UploadOnlyConfig(ctx)
+}
+
func buildActionConfig(ctx build.Context, args ...string) build.Config {
flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
+ flags.SetOutput(ctx.Writer)
+
flags.Usage = func() {
fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
@@ -454,21 +515,32 @@
const numBuildActionFlags = 2
if len(args) < numBuildActionFlags {
flags.Usage()
- ctx.Fatalln("Improper build action arguments.")
+ ctx.Fatalln("Improper build action arguments: too few arguments")
}
- flags.Parse(args[0:numBuildActionFlags])
+ parseError := flags.Parse(args[0:numBuildActionFlags])
// The next block of code is to validate that exactly one build action is set and the dir flag
// is specified.
- buildActionCount := 0
+ buildActionFound := false
var buildAction build.BuildAction
- for _, flag := range buildActionFlags {
- if flag.set {
- buildActionCount++
- buildAction = flag.action
+ for _, f := range buildActionFlags {
+ if f.set {
+ if buildActionFound {
+ if parseError == nil {
+ //otherwise Parse() already called Usage()
+ flags.Usage()
+ }
+ ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
+ }
+ buildActionFound = true
+ buildAction = f.action
}
}
- if buildActionCount != 1 {
+ if !buildActionFound {
+ if parseError == nil {
+ //otherwise Parse() already called Usage()
+ flags.Usage()
+ }
ctx.Fatalln("Build action not defined.")
}
if *dir == "" {
@@ -480,7 +552,9 @@
return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
}
-func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
+func runMake(ctx build.Context, config build.Config, _ []string) {
+ logAndSymlinkSetup(ctx, config)
+ logsDir := config.LogsDir()
if config.IsVerbose() {
writer := ctx.Writer
fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
@@ -490,11 +564,7 @@
fmt.Fprintln(writer, "!")
fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
fmt.Fprintln(writer, "")
- select {
- case <-time.After(5 * time.Second):
- case <-ctx.Done():
- return
- }
+ ctx.Fatal("Invalid argument")
}
if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
@@ -505,7 +575,7 @@
fmt.Fprintln(writer, "!")
fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
fmt.Fprintln(writer, "")
- ctx.Fatal("done")
+ ctx.Fatal("Invalid environment")
}
build.Build(ctx, config)
@@ -514,8 +584,16 @@
// getCommand finds the appropriate command based on args[1] flag. args[0]
// is the soong_ui filename.
func getCommand(args []string) (*command, []string, error) {
+ listFlags := func() []string {
+ flags := make([]string, len(commands))
+ for i, c := range commands {
+ flags[i] = c.flag
+ }
+ return flags
+ }
+
if len(args) < 2 {
- return nil, nil, fmt.Errorf("Too few arguments: %q", args)
+ return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
}
for _, c := range commands {
@@ -523,13 +601,7 @@
return &c, args[2:], nil
}
}
-
- // command not found
- flags := make([]string, len(commands))
- for i, c := range commands {
- flags[i] = c.flag
- }
- return nil, nil, fmt.Errorf("Command not found: %q\nDid you mean one of these: %q", args, flags)
+ return nil, nil, fmt.Errorf("Command not found: %q\nDid you mean one of these: %q", args[1], listFlags())
}
// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
@@ -627,3 +699,19 @@
ctx.Println("Failed to increase file limit:", err)
}
}
+
+func updateTotalRealTime(ctx build.Context, config build.Config, args []string) {
+ soongMetricsFile := filepath.Join(config.LogsDir(), "soong_metrics")
+
+ //read file into proto
+ data, err := os.ReadFile(soongMetricsFile)
+ if err != nil {
+ ctx.Fatal(err)
+ }
+ met := ctx.ContextImpl.Metrics
+
+ err = met.UpdateTotalRealTime(data)
+ if err != nil {
+ ctx.Fatal(err)
+ }
+}
diff --git a/cmd/zip2zip/BUILD.bazel b/cmd/zip2zip/BUILD.bazel
new file mode 100644
index 0000000..1915a2d
--- /dev/null
+++ b/cmd/zip2zip/BUILD.bazel
@@ -0,0 +1,18 @@
+# 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.
+
+alias(
+ name = "zip2zip",
+ actual = "//prebuilts/build-tools:linux-x86/bin/zip2zip",
+)
diff --git a/compliance/OWNERS b/compliance/OWNERS
new file mode 100644
index 0000000..f52e201
--- /dev/null
+++ b/compliance/OWNERS
@@ -0,0 +1,8 @@
+# OSEP Build
+bbadour@google.com
+kanouche@google.com
+napier@google.com
+
+# Open Source Compliance Tools
+rtp@google.com
+austinyuan@google.com
diff --git a/compliance/build_license_metadata/build_license_metadata.go b/compliance/build_license_metadata/build_license_metadata.go
index 53d2407..fb4b784 100644
--- a/compliance/build_license_metadata/build_license_metadata.go
+++ b/compliance/build_license_metadata/build_license_metadata.go
@@ -66,6 +66,7 @@
packageName := flags.String("p", "", "license package name")
moduleType := newMultiString(flags, "mt", "module type")
+ moduleName := flags.String("mn", "", "module name")
kinds := newMultiString(flags, "k", "license kinds")
moduleClass := newMultiString(flags, "mc", "module class")
conditions := newMultiString(flags, "c", "license conditions")
@@ -83,6 +84,7 @@
metadata := license_metadata_proto.LicenseMetadata{}
metadata.PackageName = proto.String(*packageName)
+ metadata.ModuleName = proto.String(*moduleName)
metadata.ModuleTypes = *moduleType
metadata.ModuleClasses = *moduleClass
metadata.IsContainer = proto.Bool(*isContainer)
diff --git a/compliance/license_metadata_proto/license_metadata.pb.go b/compliance/license_metadata_proto/license_metadata.pb.go
index 44dbc78..69aa377 100644
--- a/compliance/license_metadata_proto/license_metadata.pb.go
+++ b/compliance/license_metadata_proto/license_metadata.pb.go
@@ -14,8 +14,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.27.1
-// protoc v3.19.0
+// protoc-gen-go v1.28.0
+// protoc v3.12.4
// source: license_metadata.proto
package license_metadata_proto
@@ -40,7 +40,9 @@
unknownFields protoimpl.UnknownFields
// package_name identifies the source package. License texts are named relative to the package name.
- PackageName *string `protobuf:"bytes,1,opt,name=package_name,json=packageName" json:"package_name,omitempty"`
+ PackageName *string `protobuf:"bytes,1,opt,name=package_name,json=packageName" json:"package_name,omitempty"`
+ // module_name identifies the target
+ ModuleName *string `protobuf:"bytes,14,opt,name=module_name,json=moduleName" json:"module_name,omitempty"`
ModuleTypes []string `protobuf:"bytes,2,rep,name=module_types,json=moduleTypes" json:"module_types,omitempty"`
ModuleClasses []string `protobuf:"bytes,3,rep,name=module_classes,json=moduleClasses" json:"module_classes,omitempty"`
// projects identifies the git project(s) containing the associated source code.
@@ -104,6 +106,13 @@
return ""
}
+func (x *LicenseMetadata) GetModuleName() string {
+ if x != nil && x.ModuleName != nil {
+ return *x.ModuleName
+ }
+ return ""
+}
+
func (x *LicenseMetadata) GetModuleTypes() []string {
if x != nil {
return x.ModuleTypes
@@ -311,52 +320,54 @@
0x0a, 0x16, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
- 0x22, 0x8a, 0x04, 0x0a, 0x0f, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61,
+ 0x22, 0xab, 0x04, 0x0a, 0x0f, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61,
0x64, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x63, 0x6b,
- 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
- 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x6d,
- 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x6f,
- 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03,
- 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65,
- 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x04, 0x20,
- 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x23, 0x0a,
- 0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, 0x05,
- 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4b, 0x69, 0x6e,
- 0x64, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x6f,
- 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11,
- 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
- 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x65, 0x78,
- 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73,
- 0x65, 0x54, 0x65, 0x78, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e,
- 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73,
- 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x75, 0x69,
- 0x6c, 0x74, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x12,
- 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x03,
- 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x12, 0x43, 0x0a,
- 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x0b, 0x20, 0x03,
- 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e,
- 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x49, 0x6e, 0x73, 0x74,
- 0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x4d,
- 0x61, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0c, 0x20,
- 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x04,
- 0x64, 0x65, 0x70, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x62, 0x75, 0x69,
- 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64,
- 0x61, 0x74, 0x61, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65, 0x70,
- 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x22, 0x50, 0x0a,
- 0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x66,
- 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
- 0x66, 0x72, 0x6f, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74,
- 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x74, 0x68, 0x22,
- 0x4b, 0x0a, 0x13, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65, 0x70, 0x65,
- 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x6e,
- 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
- 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x31, 0x5a, 0x2f,
- 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6f,
- 0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65,
- 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73,
+ 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x04,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x23,
+ 0x0a, 0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18,
+ 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4b, 0x69,
+ 0x6e, 0x64, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x63,
+ 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52,
+ 0x11, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x65,
+ 0x78, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e,
+ 0x73, 0x65, 0x54, 0x65, 0x78, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69,
+ 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x75,
+ 0x69, 0x6c, 0x74, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x74,
+ 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x12, 0x43,
+ 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x0b, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x49, 0x6e, 0x73,
+ 0x74, 0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c,
+ 0x4d, 0x61, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0c,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3f, 0x0a,
+ 0x04, 0x64, 0x65, 0x70, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x62, 0x75,
+ 0x69, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61,
+ 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65,
+ 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x22, 0x50,
+ 0x0a, 0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x12, 0x1b, 0x0a, 0x09,
+ 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x08, 0x66, 0x72, 0x6f, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e,
+ 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x74, 0x68,
+ 0x22, 0x4b, 0x0a, 0x13, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65, 0x70,
+ 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61,
+ 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
+ 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x31, 0x5a,
+ 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63,
+ 0x6f, 0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73,
+ 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
}
var (
diff --git a/compliance/license_metadata_proto/license_metadata.proto b/compliance/license_metadata_proto/license_metadata.proto
index 1b4f34f..61f8126 100644
--- a/compliance/license_metadata_proto/license_metadata.proto
+++ b/compliance/license_metadata_proto/license_metadata.proto
@@ -21,6 +21,9 @@
// package_name identifies the source package. License texts are named relative to the package name.
optional string package_name = 1;
+ // module_name identifies the target
+ optional string module_name = 14;
+
repeated string module_types = 2;
repeated string module_classes = 3;
@@ -54,6 +57,9 @@
// deps lists the license metadata files depended on.
repeated AnnotatedDependency deps = 13;
+
+ // next id: 15
+
}
// InstallMap messages describe the mapping from an input filesystem file to the path to the file
diff --git a/compliance/project_metadata_proto/Android.bp b/compliance/project_metadata_proto/Android.bp
new file mode 100644
index 0000000..56e76e7
--- /dev/null
+++ b/compliance/project_metadata_proto/Android.bp
@@ -0,0 +1,27 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "project_metadata_proto",
+ pkgPath: "android/soong/compliance/project_metadata_proto",
+ srcs: ["project_metadata.pb.go"],
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+}
diff --git a/compliance/project_metadata_proto/project_metadata.pb.go b/compliance/project_metadata_proto/project_metadata.pb.go
new file mode 100644
index 0000000..529159c
--- /dev/null
+++ b/compliance/project_metadata_proto/project_metadata.pb.go
@@ -0,0 +1,765 @@
+// 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.
+
+// A proto definition used to parse METADATA file in third party projects.
+
+// This proto will only contain fields and values used by android compliance.
+// It is not intended to be the formal definition of METADATA file.
+
+// See google3/third_party/metadata.proto if you need to add more stuff to
+// match upstream. Do not add new fields and values here. Add them upstream
+// when necessary, and copy them here.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.28.0
+// protoc v3.12.4
+// source: project_metadata.proto
+
+package project_metadata_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)
+)
+
+// License type that identifies how the packages may be used. See
+// go/thirdpartylicenses for full explanation of each license type.
+type LicenseType int32
+
+const (
+ LicenseType_BY_EXCEPTION_ONLY LicenseType = 1
+ LicenseType_NOTICE LicenseType = 2
+ LicenseType_PERMISSIVE LicenseType = 3
+ LicenseType_RECIPROCAL LicenseType = 4
+ LicenseType_RESTRICTED_IF_STATICALLY_LINKED LicenseType = 5
+ LicenseType_RESTRICTED LicenseType = 6
+ LicenseType_UNENCUMBERED LicenseType = 7
+)
+
+// Enum value maps for LicenseType.
+var (
+ LicenseType_name = map[int32]string{
+ 1: "BY_EXCEPTION_ONLY",
+ 2: "NOTICE",
+ 3: "PERMISSIVE",
+ 4: "RECIPROCAL",
+ 5: "RESTRICTED_IF_STATICALLY_LINKED",
+ 6: "RESTRICTED",
+ 7: "UNENCUMBERED",
+ }
+ LicenseType_value = map[string]int32{
+ "BY_EXCEPTION_ONLY": 1,
+ "NOTICE": 2,
+ "PERMISSIVE": 3,
+ "RECIPROCAL": 4,
+ "RESTRICTED_IF_STATICALLY_LINKED": 5,
+ "RESTRICTED": 6,
+ "UNENCUMBERED": 7,
+ }
+)
+
+func (x LicenseType) Enum() *LicenseType {
+ p := new(LicenseType)
+ *p = x
+ return p
+}
+
+func (x LicenseType) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (LicenseType) Descriptor() protoreflect.EnumDescriptor {
+ return file_project_metadata_proto_enumTypes[0].Descriptor()
+}
+
+func (LicenseType) Type() protoreflect.EnumType {
+ return &file_project_metadata_proto_enumTypes[0]
+}
+
+func (x LicenseType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *LicenseType) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = LicenseType(num)
+ return nil
+}
+
+// Deprecated: Use LicenseType.Descriptor instead.
+func (LicenseType) EnumDescriptor() ([]byte, []int) {
+ return file_project_metadata_proto_rawDescGZIP(), []int{0}
+}
+
+type URL_Type int32
+
+const (
+ // The homepage for the package. For example, "https://bazel.io/". This URL
+ // is optional, but encouraged to help disambiguate similarly named packages
+ // or to get more information about the package. This is especially helpful
+ // when no other URLs provide human readable resources (such as git:// or
+ // sso:// URLs).
+ URL_HOMEPAGE URL_Type = 1
+ // The URL of the archive containing the source code for the package, for
+ // example a zip or tgz file.
+ URL_ARCHIVE URL_Type = 2
+ // The URL of the upstream git repository this package is retrieved from.
+ // For example:
+ // - https://github.com/git/git.git
+ // - git://git.kernel.org/pub/scm/git/git.git
+ //
+ // Use of a git URL requires that the package "version" value must specify a
+ // specific git tag or revision.
+ URL_GIT URL_Type = 3
+ // The URL of the upstream SVN repository this package is retrieved from.
+ // For example:
+ // - http://llvm.org/svn/llvm-project/llvm/
+ //
+ // Use of an SVN URL requires that the package "version" value must specify
+ // a specific SVN tag or revision.
+ URL_SVN URL_Type = 7
+ // The URL of the upstream mercurial repository this package is retrieved
+ // from. For example:
+ // - https://mercurial-scm.org/repo/evolve
+ //
+ // Use of a mercurial URL requires that the package "version" value must
+ // specify a specific tag or revision.
+ URL_HG URL_Type = 8
+ // The URL of the upstream darcs repository this package is retrieved
+ // from. For example:
+ // - https://hub.darcs.net/hu.dwim/hu.dwim.util
+ //
+ // Use of a DARCS URL requires that the package "version" value must
+ // specify a specific tag or revision.
+ URL_DARCS URL_Type = 9
+ // The URL of the upstream piper location. This is primarily used when a
+ // package is being migrated into third_party from elsewhere in piper, or
+ // when a package is being newly developed in third_party. For newly
+ // developed packages, the PIPER URL should reference the package itself
+ // (e.g. "http://google3/third_party/my/package")
+ URL_PIPER URL_Type = 4
+ // A URL that does not fit any other type. This may also indicate that the
+ // source code was received via email or some other out-of-band way. This is
+ // most commonly used with commercial software received directly from the
+ // vendor. In the case of email, the URL value can be used to provide
+ // additional information about how it was received.
+ URL_OTHER URL_Type = 11
+ // The URL identifying where the local copy of the package source code can
+ // be found.
+ //
+ // Typically, the metadata files describing a package reside in the same
+ // directory as the source code for the package. In a few rare cases where
+ // they are separate, the LOCAL_SOURCE URL identifies where to find the
+ // source code. This only describes where to find the local copy of the
+ // source; there should always be an additional URL describing where the
+ // package was retrieved from.
+ //
+ // Examples:
+ // - http://google3/third_party/java_src/gerritcodereview/gerrit/
+ // - https://android.googlesource.com/platform/external/apache-http/
+ URL_LOCAL_SOURCE URL_Type = 6
+)
+
+// Enum value maps for URL_Type.
+var (
+ URL_Type_name = map[int32]string{
+ 1: "HOMEPAGE",
+ 2: "ARCHIVE",
+ 3: "GIT",
+ 7: "SVN",
+ 8: "HG",
+ 9: "DARCS",
+ 4: "PIPER",
+ 11: "OTHER",
+ 6: "LOCAL_SOURCE",
+ }
+ URL_Type_value = map[string]int32{
+ "HOMEPAGE": 1,
+ "ARCHIVE": 2,
+ "GIT": 3,
+ "SVN": 7,
+ "HG": 8,
+ "DARCS": 9,
+ "PIPER": 4,
+ "OTHER": 11,
+ "LOCAL_SOURCE": 6,
+ }
+)
+
+func (x URL_Type) Enum() *URL_Type {
+ p := new(URL_Type)
+ *p = x
+ return p
+}
+
+func (x URL_Type) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (URL_Type) Descriptor() protoreflect.EnumDescriptor {
+ return file_project_metadata_proto_enumTypes[1].Descriptor()
+}
+
+func (URL_Type) Type() protoreflect.EnumType {
+ return &file_project_metadata_proto_enumTypes[1]
+}
+
+func (x URL_Type) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *URL_Type) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = URL_Type(num)
+ return nil
+}
+
+// Deprecated: Use URL_Type.Descriptor instead.
+func (URL_Type) EnumDescriptor() ([]byte, []int) {
+ return file_project_metadata_proto_rawDescGZIP(), []int{2, 0}
+}
+
+type Metadata struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Name of this API/package.
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ // A short description (a few lines) of the package. It will be
+ // included on the summary page.
+ // Example: "Handles location lookups, throttling, batching, etc."
+ Description *string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
+ // Specifies additional data about third-party packages.
+ ThirdParty *ThirdParty `protobuf:"bytes,13,opt,name=third_party,json=thirdParty" json:"third_party,omitempty"`
+}
+
+func (x *Metadata) Reset() {
+ *x = Metadata{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_project_metadata_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Metadata) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Metadata) ProtoMessage() {}
+
+func (x *Metadata) ProtoReflect() protoreflect.Message {
+ mi := &file_project_metadata_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 Metadata.ProtoReflect.Descriptor instead.
+func (*Metadata) Descriptor() ([]byte, []int) {
+ return file_project_metadata_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Metadata) GetName() string {
+ if x != nil && x.Name != nil {
+ return *x.Name
+ }
+ return ""
+}
+
+func (x *Metadata) GetDescription() string {
+ if x != nil && x.Description != nil {
+ return *x.Description
+ }
+ return ""
+}
+
+func (x *Metadata) GetThirdParty() *ThirdParty {
+ if x != nil {
+ return x.ThirdParty
+ }
+ return nil
+}
+
+type ThirdParty struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // URL(s) associated with the package.
+ //
+ // At a minimum, all packages must specify a URL which identifies where it
+ // came from, containing a type of: ARCHIVE, GIT, PIPER, or OTHER. Typically,
+ // a package should contain only a single URL from these types. Occasionally,
+ // a package may be broken across multiple archive files for whatever reason,
+ // in which case having multiple ARCHIVE URLs is okay. However, this should
+ // not be used to combine different logical packages that are versioned and
+ // possibly licensed differently.
+ Url []*URL `protobuf:"bytes,1,rep,name=url" json:"url,omitempty"`
+ // The package version. In order of preference, this should contain:
+ // - If the package comes from Git or another source control system,
+ // a specific tag or revision in source control, such as "r123" or
+ // "58e27d2". This MUST NOT be a mutable ref such as a branch name.
+ // - a released package version such as "1.0", "2.3-beta", etc.
+ // - the date the package was retrieved, formatted as "As of YYYY-MM-DD".
+ Version *string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"`
+ // The date of the change in which the package was last upgraded from
+ // upstream.
+ // This should only identify package upgrades from upstream, not local
+ // modifications. This may identify the date of either the original or
+ // merged change.
+ //
+ // Note: this is NOT the date that this version of the package was released
+ // externally.
+ LastUpgradeDate *Date `protobuf:"bytes,10,opt,name=last_upgrade_date,json=lastUpgradeDate" json:"last_upgrade_date,omitempty"`
+ // License type that identifies how the package may be used. See
+ // go/thirdpartylicenses for instructions on selecting the appropriate type.
+ LicenseType *LicenseType `protobuf:"varint,4,opt,name=license_type,json=licenseType,enum=project_metadata.LicenseType" json:"license_type,omitempty"`
+ // Description of local changes that have been made to the package. This does
+ // not need to (and in most cases should not) attempt to include an exhaustive
+ // list of all changes, but may instead direct readers to review the local
+ // commit history, a collection of patch files, a separate README.md (or
+ // similar) document, etc.
+ // Note: Use of this field to store IDs of advisories fixed with a backported
+ // patch is deprecated, use "security.mitigated_security_patch" instead.
+ LocalModifications *string `protobuf:"bytes,6,opt,name=local_modifications,json=localModifications" json:"local_modifications,omitempty"`
+ // The URL for any public mirror created for compliance purposes.
+ // See go/thirdpartylicenses#reciprocal policy for more details.
+ ComplianceMirrorUrl *string `protobuf:"bytes,12,opt,name=compliance_mirror_url,json=complianceMirrorUrl" json:"compliance_mirror_url,omitempty"`
+ // The homepage for the package. This will eventually replace
+ // `url { type: HOMEPAGE }`
+ Homepage *string `protobuf:"bytes,14,opt,name=homepage" json:"homepage,omitempty"`
+}
+
+func (x *ThirdParty) Reset() {
+ *x = ThirdParty{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_project_metadata_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *ThirdParty) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ThirdParty) ProtoMessage() {}
+
+func (x *ThirdParty) ProtoReflect() protoreflect.Message {
+ mi := &file_project_metadata_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 ThirdParty.ProtoReflect.Descriptor instead.
+func (*ThirdParty) Descriptor() ([]byte, []int) {
+ return file_project_metadata_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *ThirdParty) GetUrl() []*URL {
+ if x != nil {
+ return x.Url
+ }
+ return nil
+}
+
+func (x *ThirdParty) GetVersion() string {
+ if x != nil && x.Version != nil {
+ return *x.Version
+ }
+ return ""
+}
+
+func (x *ThirdParty) GetLastUpgradeDate() *Date {
+ if x != nil {
+ return x.LastUpgradeDate
+ }
+ return nil
+}
+
+func (x *ThirdParty) GetLicenseType() LicenseType {
+ if x != nil && x.LicenseType != nil {
+ return *x.LicenseType
+ }
+ return LicenseType_BY_EXCEPTION_ONLY
+}
+
+func (x *ThirdParty) GetLocalModifications() string {
+ if x != nil && x.LocalModifications != nil {
+ return *x.LocalModifications
+ }
+ return ""
+}
+
+func (x *ThirdParty) GetComplianceMirrorUrl() string {
+ if x != nil && x.ComplianceMirrorUrl != nil {
+ return *x.ComplianceMirrorUrl
+ }
+ return ""
+}
+
+func (x *ThirdParty) GetHomepage() string {
+ if x != nil && x.Homepage != nil {
+ return *x.Homepage
+ }
+ return ""
+}
+
+// URL associated with a third-party package.
+type URL struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The type of resource this URL identifies.
+ Type *URL_Type `protobuf:"varint,1,opt,name=type,enum=project_metadata.URL_Type" json:"type,omitempty"`
+ // The actual URL value. URLs should be absolute and start with 'http://' or
+ // 'https://' (or occasionally 'git://' or 'ftp://' where appropriate).
+ Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
+}
+
+func (x *URL) Reset() {
+ *x = URL{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_project_metadata_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *URL) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*URL) ProtoMessage() {}
+
+func (x *URL) ProtoReflect() protoreflect.Message {
+ mi := &file_project_metadata_proto_msgTypes[2]
+ 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 URL.ProtoReflect.Descriptor instead.
+func (*URL) Descriptor() ([]byte, []int) {
+ return file_project_metadata_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *URL) GetType() URL_Type {
+ if x != nil && x.Type != nil {
+ return *x.Type
+ }
+ return URL_HOMEPAGE
+}
+
+func (x *URL) GetValue() string {
+ if x != nil && x.Value != nil {
+ return *x.Value
+ }
+ return ""
+}
+
+// Represents a whole or partial calendar date, such as a birthday. The time of
+// day and time zone are either specified elsewhere or are insignificant. The
+// date is relative to the Gregorian Calendar. This can represent one of the
+// following:
+//
+// - A full date, with non-zero year, month, and day values.
+// - A month and day, with a zero year (for example, an anniversary).
+// - A year on its own, with a zero month and a zero day.
+// - A year and month, with a zero day (for example, a credit card expiration
+// date).
+type Date struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Year of the date. Must be from 1 to 9999, or 0 to specify a date without
+ // a year.
+ Year *int32 `protobuf:"varint,1,opt,name=year" json:"year,omitempty"`
+ // Month of a year. Must be from 1 to 12, or 0 to specify a year without a
+ // month and day.
+ Month *int32 `protobuf:"varint,2,opt,name=month" json:"month,omitempty"`
+ // Day of a month. Must be from 1 to 31 and valid for the year and month, or 0
+ // to specify a year by itself or a year and month where the day isn't
+ // significant.
+ Day *int32 `protobuf:"varint,3,opt,name=day" json:"day,omitempty"`
+}
+
+func (x *Date) Reset() {
+ *x = Date{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_project_metadata_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Date) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Date) ProtoMessage() {}
+
+func (x *Date) ProtoReflect() protoreflect.Message {
+ mi := &file_project_metadata_proto_msgTypes[3]
+ 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 Date.ProtoReflect.Descriptor instead.
+func (*Date) Descriptor() ([]byte, []int) {
+ return file_project_metadata_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *Date) GetYear() int32 {
+ if x != nil && x.Year != nil {
+ return *x.Year
+ }
+ return 0
+}
+
+func (x *Date) GetMonth() int32 {
+ if x != nil && x.Month != nil {
+ return *x.Month
+ }
+ return 0
+}
+
+func (x *Date) GetDay() int32 {
+ if x != nil && x.Day != nil {
+ return *x.Day
+ }
+ return 0
+}
+
+var File_project_metadata_proto protoreflect.FileDescriptor
+
+var file_project_metadata_proto_rawDesc = []byte{
+ 0x0a, 0x16, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
+ 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x7f, 0x0a, 0x08, 0x4d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0b,
+ 0x74, 0x68, 0x69, 0x72, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61,
+ 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x52,
+ 0x0a, 0x74, 0x68, 0x69, 0x72, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x22, 0xd6, 0x02, 0x0a, 0x0a,
+ 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x12, 0x27, 0x0a, 0x03, 0x75, 0x72,
+ 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
+ 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x52, 0x4c, 0x52, 0x03,
+ 0x75, 0x72, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a,
+ 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x64, 0x61,
+ 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65,
+ 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x61, 0x74, 0x65,
+ 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x44, 0x61, 0x74,
+ 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70,
+ 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
+ 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e,
+ 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54,
+ 0x79, 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x6f, 0x64,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e,
+ 0x63, 0x65, 0x5f, 0x6d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0c, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x4d,
+ 0x69, 0x72, 0x72, 0x6f, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x6d, 0x65,
+ 0x70, 0x61, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x6d, 0x65,
+ 0x70, 0x61, 0x67, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x2e, 0x0a, 0x04,
+ 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f,
+ 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x52,
+ 0x4c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x22, 0x6e, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x48, 0x4f,
+ 0x4d, 0x45, 0x50, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x52, 0x43, 0x48,
+ 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x49, 0x54, 0x10, 0x03, 0x12, 0x07,
+ 0x0a, 0x03, 0x53, 0x56, 0x4e, 0x10, 0x07, 0x12, 0x06, 0x0a, 0x02, 0x48, 0x47, 0x10, 0x08, 0x12,
+ 0x09, 0x0a, 0x05, 0x44, 0x41, 0x52, 0x43, 0x53, 0x10, 0x09, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x49,
+ 0x50, 0x45, 0x52, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x10, 0x0b,
+ 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x5f, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45,
+ 0x10, 0x06, 0x22, 0x42, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x79, 0x65,
+ 0x61, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x79, 0x65, 0x61, 0x72, 0x12, 0x14,
+ 0x0a, 0x05, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d,
+ 0x6f, 0x6e, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x05, 0x52, 0x03, 0x64, 0x61, 0x79, 0x2a, 0x97, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x63, 0x65, 0x6e,
+ 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x42, 0x59, 0x5f, 0x45, 0x58, 0x43,
+ 0x45, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x12, 0x0a, 0x0a,
+ 0x06, 0x4e, 0x4f, 0x54, 0x49, 0x43, 0x45, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52,
+ 0x4d, 0x49, 0x53, 0x53, 0x49, 0x56, 0x45, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x43,
+ 0x49, 0x50, 0x52, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x04, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53,
+ 0x54, 0x52, 0x49, 0x43, 0x54, 0x45, 0x44, 0x5f, 0x49, 0x46, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x49,
+ 0x43, 0x41, 0x4c, 0x4c, 0x59, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x10, 0x05, 0x12, 0x0e,
+ 0x0a, 0x0a, 0x52, 0x45, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, 0x45, 0x44, 0x10, 0x06, 0x12, 0x10,
+ 0x0a, 0x0c, 0x55, 0x4e, 0x45, 0x4e, 0x43, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x45, 0x44, 0x10, 0x07,
+ 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e,
+ 0x67, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x70, 0x72, 0x6f,
+ 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f,
+}
+
+var (
+ file_project_metadata_proto_rawDescOnce sync.Once
+ file_project_metadata_proto_rawDescData = file_project_metadata_proto_rawDesc
+)
+
+func file_project_metadata_proto_rawDescGZIP() []byte {
+ file_project_metadata_proto_rawDescOnce.Do(func() {
+ file_project_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_project_metadata_proto_rawDescData)
+ })
+ return file_project_metadata_proto_rawDescData
+}
+
+var file_project_metadata_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_project_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_project_metadata_proto_goTypes = []interface{}{
+ (LicenseType)(0), // 0: project_metadata.LicenseType
+ (URL_Type)(0), // 1: project_metadata.URL.Type
+ (*Metadata)(nil), // 2: project_metadata.Metadata
+ (*ThirdParty)(nil), // 3: project_metadata.ThirdParty
+ (*URL)(nil), // 4: project_metadata.URL
+ (*Date)(nil), // 5: project_metadata.Date
+}
+var file_project_metadata_proto_depIdxs = []int32{
+ 3, // 0: project_metadata.Metadata.third_party:type_name -> project_metadata.ThirdParty
+ 4, // 1: project_metadata.ThirdParty.url:type_name -> project_metadata.URL
+ 5, // 2: project_metadata.ThirdParty.last_upgrade_date:type_name -> project_metadata.Date
+ 0, // 3: project_metadata.ThirdParty.license_type:type_name -> project_metadata.LicenseType
+ 1, // 4: project_metadata.URL.type:type_name -> project_metadata.URL.Type
+ 5, // [5:5] is the sub-list for method output_type
+ 5, // [5:5] is the sub-list for method input_type
+ 5, // [5:5] is the sub-list for extension type_name
+ 5, // [5:5] is the sub-list for extension extendee
+ 0, // [0:5] is the sub-list for field type_name
+}
+
+func init() { file_project_metadata_proto_init() }
+func file_project_metadata_proto_init() {
+ if File_project_metadata_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_project_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Metadata); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_project_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*ThirdParty); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_project_metadata_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*URL); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_project_metadata_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Date); 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_project_metadata_proto_rawDesc,
+ NumEnums: 2,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_project_metadata_proto_goTypes,
+ DependencyIndexes: file_project_metadata_proto_depIdxs,
+ EnumInfos: file_project_metadata_proto_enumTypes,
+ MessageInfos: file_project_metadata_proto_msgTypes,
+ }.Build()
+ File_project_metadata_proto = out.File
+ file_project_metadata_proto_rawDesc = nil
+ file_project_metadata_proto_goTypes = nil
+ file_project_metadata_proto_depIdxs = nil
+}
diff --git a/compliance/project_metadata_proto/project_metadata.proto b/compliance/project_metadata_proto/project_metadata.proto
new file mode 100644
index 0000000..94cc516
--- /dev/null
+++ b/compliance/project_metadata_proto/project_metadata.proto
@@ -0,0 +1,225 @@
+// 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.
+
+// A proto definition used to parse METADATA file in third party projects.
+
+// This proto will only contain fields and values used by android compliance.
+// It is not intended to be the formal definition of METADATA file.
+
+// See google3/third_party/metadata.proto if you need to add more stuff to
+// match upstream. Do not add new fields and values here. Add them upstream
+// when necessary, and copy them here.
+
+syntax = "proto2"; // As long as upstream is proto2...
+
+package project_metadata;
+option go_package = "android/soong/compliance/project_metadata_proto";
+
+// Definitions for project metadata. (go/thirdparty/metadata)
+
+// Special naming conventions:
+// Repeated fields should have singular names (instead of plural).
+
+message Metadata {
+ // Name of this API/package.
+ optional string name = 1;
+
+ // A short description (a few lines) of the package. It will be
+ // included on the summary page.
+ // Example: "Handles location lookups, throttling, batching, etc."
+ optional string description = 3;
+
+ // Specifies additional data about third-party packages.
+ optional ThirdParty third_party = 13;
+}
+
+message ThirdParty {
+ // The name and description for the package should be specified using the top
+ // level fields in MetaData above
+ //
+ // Description should only specify a short description (a few lines) of the
+ // packages. Instructions for maintainers or similar information should be
+ // specified in BUILD comments, a separate README.md file, etc.
+
+ // URL(s) associated with the package.
+ //
+ // At a minimum, all packages must specify a URL which identifies where it
+ // came from, containing a type of: ARCHIVE, GIT, PIPER, or OTHER. Typically,
+ // a package should contain only a single URL from these types. Occasionally,
+ // a package may be broken across multiple archive files for whatever reason,
+ // in which case having multiple ARCHIVE URLs is okay. However, this should
+ // not be used to combine different logical packages that are versioned and
+ // possibly licensed differently.
+ repeated URL url = 1;
+
+ // The package version. In order of preference, this should contain:
+ // - If the package comes from Git or another source control system,
+ // a specific tag or revision in source control, such as "r123" or
+ // "58e27d2". This MUST NOT be a mutable ref such as a branch name.
+ // - a released package version such as "1.0", "2.3-beta", etc.
+ // - the date the package was retrieved, formatted as "As of YYYY-MM-DD".
+ optional string version = 2;
+
+ // The date of the change in which the package was last upgraded from
+ // upstream.
+ // This should only identify package upgrades from upstream, not local
+ // modifications. This may identify the date of either the original or
+ // merged change.
+ //
+ // Note: this is NOT the date that this version of the package was released
+ // externally.
+ optional Date last_upgrade_date = 10;
+
+ // License type that identifies how the package may be used. See
+ // go/thirdpartylicenses for instructions on selecting the appropriate type.
+ optional LicenseType license_type = 4;
+
+ // Description of local changes that have been made to the package. This does
+ // not need to (and in most cases should not) attempt to include an exhaustive
+ // list of all changes, but may instead direct readers to review the local
+ // commit history, a collection of patch files, a separate README.md (or
+ // similar) document, etc.
+ // Note: Use of this field to store IDs of advisories fixed with a backported
+ // patch is deprecated, use "security.mitigated_security_patch" instead.
+ optional string local_modifications = 6;
+
+ // The URL for any public mirror created for compliance purposes.
+ // See go/thirdpartylicenses#reciprocal policy for more details.
+ optional string compliance_mirror_url = 12;
+
+ // The homepage for the package. This will eventually replace
+ // `url { type: HOMEPAGE }`
+ optional string homepage = 14;
+}
+
+// URL associated with a third-party package.
+message URL {
+ enum Type {
+ // The homepage for the package. For example, "https://bazel.io/". This URL
+ // is optional, but encouraged to help disambiguate similarly named packages
+ // or to get more information about the package. This is especially helpful
+ // when no other URLs provide human readable resources (such as git:// or
+ // sso:// URLs).
+ HOMEPAGE = 1;
+
+ // The URL of the archive containing the source code for the package, for
+ // example a zip or tgz file.
+ ARCHIVE = 2;
+
+ // The URL of the upstream git repository this package is retrieved from.
+ // For example:
+ // - https://github.com/git/git.git
+ // - git://git.kernel.org/pub/scm/git/git.git
+ //
+ // Use of a git URL requires that the package "version" value must specify a
+ // specific git tag or revision.
+ GIT = 3;
+
+ // The URL of the upstream SVN repository this package is retrieved from.
+ // For example:
+ // - http://llvm.org/svn/llvm-project/llvm/
+ //
+ // Use of an SVN URL requires that the package "version" value must specify
+ // a specific SVN tag or revision.
+ SVN = 7;
+
+ // The URL of the upstream mercurial repository this package is retrieved
+ // from. For example:
+ // - https://mercurial-scm.org/repo/evolve
+ //
+ // Use of a mercurial URL requires that the package "version" value must
+ // specify a specific tag or revision.
+ HG = 8;
+
+ // The URL of the upstream darcs repository this package is retrieved
+ // from. For example:
+ // - https://hub.darcs.net/hu.dwim/hu.dwim.util
+ //
+ // Use of a DARCS URL requires that the package "version" value must
+ // specify a specific tag or revision.
+ DARCS = 9;
+
+ // The URL of the upstream piper location. This is primarily used when a
+ // package is being migrated into third_party from elsewhere in piper, or
+ // when a package is being newly developed in third_party. For newly
+ // developed packages, the PIPER URL should reference the package itself
+ // (e.g. "http://google3/third_party/my/package")
+ PIPER = 4;
+
+ // A URL that does not fit any other type. This may also indicate that the
+ // source code was received via email or some other out-of-band way. This is
+ // most commonly used with commercial software received directly from the
+ // vendor. In the case of email, the URL value can be used to provide
+ // additional information about how it was received.
+ OTHER = 11;
+
+ // The URL identifying where the local copy of the package source code can
+ // be found.
+ //
+ // Typically, the metadata files describing a package reside in the same
+ // directory as the source code for the package. In a few rare cases where
+ // they are separate, the LOCAL_SOURCE URL identifies where to find the
+ // source code. This only describes where to find the local copy of the
+ // source; there should always be an additional URL describing where the
+ // package was retrieved from.
+ //
+ // Examples:
+ // - http://google3/third_party/java_src/gerritcodereview/gerrit/
+ // - https://android.googlesource.com/platform/external/apache-http/
+ LOCAL_SOURCE = 6;
+ }
+
+ // The type of resource this URL identifies.
+ optional Type type = 1;
+
+ // The actual URL value. URLs should be absolute and start with 'http://' or
+ // 'https://' (or occasionally 'git://' or 'ftp://' where appropriate).
+ optional string value = 2;
+}
+
+// License type that identifies how the packages may be used. See
+// go/thirdpartylicenses for full explanation of each license type.
+enum LicenseType {
+ BY_EXCEPTION_ONLY = 1;
+ NOTICE = 2;
+ PERMISSIVE = 3;
+ RECIPROCAL = 4;
+ RESTRICTED_IF_STATICALLY_LINKED = 5;
+ RESTRICTED = 6;
+ UNENCUMBERED = 7;
+}
+
+
+// Represents a whole or partial calendar date, such as a birthday. The time of
+// day and time zone are either specified elsewhere or are insignificant. The
+// date is relative to the Gregorian Calendar. This can represent one of the
+// following:
+//
+// * A full date, with non-zero year, month, and day values.
+// * A month and day, with a zero year (for example, an anniversary).
+// * A year on its own, with a zero month and a zero day.
+// * A year and month, with a zero day (for example, a credit card expiration
+// date).
+message Date {
+ // Year of the date. Must be from 1 to 9999, or 0 to specify a date without
+ // a year.
+ optional int32 year = 1;
+ // Month of a year. Must be from 1 to 12, or 0 to specify a year without a
+ // month and day.
+ optional int32 month = 2;
+ // Day of a month. Must be from 1 to 31 and valid for the year and month, or 0
+ // to specify a year by itself or a year and month where the day isn't
+ // significant.
+ optional int32 day = 3;
+}
diff --git a/cuj/cuj.go b/cuj/cuj.go
index 869e0f7..de6f10d 100644
--- a/cuj/cuj.go
+++ b/cuj/cuj.go
@@ -94,7 +94,7 @@
stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, "build_error")))
- stat.AddOutput(status.NewCriticalPath(log))
+ stat.AddOutput(status.NewCriticalPathLogger(log, nil))
defer met.Dump(filepath.Join(logsDir, "soong_metrics"))
diff --git a/dexpreopt/OWNERS b/dexpreopt/OWNERS
deleted file mode 100644
index 5a2a198..0000000
--- a/dexpreopt/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file * = ngeoffray@google.com,calin@google.com,skvadrik@google.com
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 6000cf4..afb3de3 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
@@ -289,9 +285,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)
@@ -340,7 +335,6 @@
clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
Name: lib,
Optional: optional,
- Implicit: implicit,
Host: hostPath,
Device: devicePath,
Subcontexts: subcontexts,
@@ -353,10 +347,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())
}
@@ -400,15 +393,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)
@@ -418,14 +409,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, "", " ")
@@ -629,7 +612,6 @@
type jsonClassLoaderContext struct {
Name string
Optional bool
- Implicit bool
Host string
Device string
Subcontexts []*jsonClassLoaderContext
@@ -662,7 +644,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),
@@ -676,6 +657,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
@@ -695,7 +679,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 5d3a9d9..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)
@@ -287,7 +282,6 @@
func TestCLCMExcludeLibs(t *testing.T) {
ctx := testContext()
const optional = false
- const implicit = true
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.
@@ -305,7 +299,7 @@
t.Run("exclude nothing", func(t *testing.T) {
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
a := excludeLibs(t, m)
@@ -314,7 +308,6 @@
{
"Name": "a",
"Optional": false,
- "Implicit": true,
"Host": "out/soong/a.jar",
"Device": "/system/a.jar",
"Subcontexts": []
@@ -325,8 +318,8 @@
t.Run("one item from list", func(t *testing.T) {
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
- m.AddContext(ctx, 28, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ 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")
@@ -335,7 +328,6 @@
{
"Name": "b",
"Optional": false,
- "Implicit": true,
"Host": "out/soong/b.jar",
"Device": "/system/b.jar",
"Subcontexts": []
@@ -347,8 +339,8 @@
t.Run("all items from a list", func(t *testing.T) {
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
- m.AddContext(ctx, 28, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ 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")
@@ -357,11 +349,11 @@
t.Run("items from a subcontext", func(t *testing.T) {
s := make(ClassLoaderContextMap)
- s.AddContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
- s.AddContext(ctx, AnySdkVersion, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+ 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, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), s)
+ m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), s)
a := excludeLibs(t, m, "b")
@@ -370,14 +362,12 @@
{
"Name": "a",
"Optional": false,
- "Implicit": true,
"Host": "out/soong/a.jar",
"Device": "/system/a.jar",
"Subcontexts": [
{
"Name": "c",
"Optional": false,
- "Implicit": true,
"Host": "out/soong/c.jar",
"Device": "/system/c.jar",
"Subcontexts": []
@@ -389,6 +379,35 @@
})
}
+// 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..0cc3bd6 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
@@ -98,6 +96,8 @@
// quickly silence build errors. This flag should be used with caution and only as a temporary
// measure, as it masks real errors and affects performance.
RelaxUsesLibraryCheck bool
+
+ EnableUffdGc bool // preopt with the assumption that userfaultfd GC will be used on device.
}
var allPlatformSystemServerJarsKey = android.NewOnceKey("allPlatformSystemServerJars")
@@ -252,8 +252,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 +275,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 +304,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.
@@ -445,7 +475,16 @@
ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin)
}
+func IsDex2oatNeeded(ctx android.PathContext) bool {
+ global := GetGlobalConfig(ctx)
+ return !global.DisablePreopt || !global.DisablePreoptBootImages
+}
+
func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
+ if !IsDex2oatNeeded(ctx) {
+ return nil
+ }
+
dex2oatBin := dex2oatModuleName(ctx.Config())
// Find the right dex2oat module, trying to follow PrebuiltDepTag from source
@@ -494,7 +533,7 @@
return &GlobalSoongConfig{
Profman: ctx.Config().HostToolPath(ctx, "profman"),
Dex2oat: dex2oatPathFromDep(ctx),
- Aapt: ctx.Config().HostToolPath(ctx, "aapt"),
+ Aapt: ctx.Config().HostToolPath(ctx, "aapt2"),
SoongZip: ctx.Config().HostToolPath(ctx, "soong_zip"),
Zip2zip: ctx.Config().HostToolPath(ctx, "zip2zip"),
ManifestCheck: ctx.Config().HostToolPath(ctx, "manifest_check"),
@@ -696,7 +735,7 @@
return &GlobalSoongConfig{
Profman: android.PathForTesting("profman"),
Dex2oat: android.PathForTesting("dex2oat"),
- Aapt: android.PathForTesting("aapt"),
+ Aapt: android.PathForTesting("aapt2"),
SoongZip: android.PathForTesting("soong_zip"),
Zip2zip: android.PathForTesting("zip2zip"),
ManifestCheck: android.PathForTesting("manifest_check"),
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index de139c4..2b38793 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -100,7 +100,19 @@
return rule, nil
}
+// If dexpreopt is applicable to the module, returns whether dexpreopt is disabled. Otherwise, the
+// behavior is undefined.
+// When it returns true, dexpreopt artifacts will not be generated, but profile will still be
+// generated if profile-guided compilation is requested.
func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool {
+ if ctx.Config().UnbundledBuild() {
+ return true
+ }
+
+ if global.DisablePreopt {
+ return true
+ }
+
if contains(global.DisablePreoptModules, module.Name) {
return true
}
@@ -201,6 +213,11 @@
if apex := global.AllApexSystemServerJars(ctx).ApexOfJar(lib); apex != "" {
return fmt.Sprintf("/apex/%s/javalib/%s.jar", apex, lib)
}
+
+ if apex := global.AllPlatformSystemServerJars(ctx).ApexOfJar(lib); apex == "system_ext" {
+ return fmt.Sprintf("/system_ext/framework/%s.jar", lib)
+ }
+
return fmt.Sprintf("/system/framework/%s.jar", lib)
}
@@ -394,10 +411,14 @@
if !android.PrefixInList(preoptFlags, "--compiler-filter=") {
var compilerFilter string
if systemServerJars.ContainsJar(module.Name) {
- // Jars of system server, use the product option if it is set, speed otherwise.
if global.SystemServerCompilerFilter != "" {
+ // Use the product option if it is set.
compilerFilter = global.SystemServerCompilerFilter
+ } else if profile != nil {
+ // Use "speed-profile" for system server jars that have a profile.
+ compilerFilter = "speed-profile"
} else {
+ // Use "speed" for system server jars that do not have a profile.
compilerFilter = "speed"
}
} else if contains(global.SpeedApps, module.Name) || contains(global.SystemServerApps, module.Name) {
@@ -486,6 +507,10 @@
cmd.FlagWithInput("--profile-file=", profile)
}
+ if global.EnableUffdGc {
+ cmd.Flag("--runtime-arg").Flag("-Xgc:CMC")
+ }
+
rule.Install(odexPath, odexInstallPath)
rule.Install(vdexPath, vdexInstallPath)
}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 07e4fad..429b5ff 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -59,6 +59,15 @@
android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
}
+func testSystemExtSystemServerModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
+ return createTestModuleConfig(
+ name,
+ fmt.Sprintf("/system_ext/framework/%s.jar", name),
+ android.PathForOutput(ctx, fmt.Sprintf("%s/dexpreopt/%s.jar", name, name)),
+ android.PathForOutput(ctx, fmt.Sprintf("%s/aligned/%s.jar", name, name)),
+ android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+}
+
func createTestModuleConfig(name, dexLocation string, buildPath, dexPath, enforceUsesLibrariesStatusFile android.OutputPath) *ModuleConfig {
return &ModuleConfig{
Name: name,
@@ -213,6 +222,29 @@
android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
}
+func TestDexPreoptSystemExtSystemServerJars(t *testing.T) {
+ config := android.TestConfig("out", nil, "", nil)
+ ctx := android.BuilderContextForTesting(config)
+ globalSoong := globalSoongConfigForTests()
+ global := GlobalConfigForTests(ctx)
+ module := testSystemExtSystemServerModuleConfig(ctx, "service-A")
+
+ global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
+ []string{"system_ext:service-A"})
+
+ rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ wantInstalls := android.RuleBuilderInstalls{
+ {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.odex"), "/system_ext/framework/oat/arm/service-A.odex"},
+ {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system_ext/framework/oat/arm/service-A.vdex"},
+ }
+
+ android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+}
+
func TestDexPreoptApexStandaloneSystemServerJars(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config)
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 47ae494..6ed0736 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -174,3 +174,17 @@
dexpreoptConfig.DisableGenerateProfile = disable
})
}
+
+// FixtureDisableDexpreoptBootImages sets the DisablePreoptBootImages property in the global config.
+func FixtureDisableDexpreoptBootImages(disable bool) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.DisablePreoptBootImages = disable
+ })
+}
+
+// FixtureDisableDexpreopt sets the DisablePreopt property in the global config.
+func FixtureDisableDexpreopt(disable bool) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.DisablePreopt = disable
+ })
+}
diff --git a/docs/OWNERS b/docs/OWNERS
deleted file mode 100644
index 2d71271..0000000
--- a/docs/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-per-file map_files.md = danalbert@google.com, enh@google.com, jiyong@google.com
-per-file native_code_coverage.md = pirama@google.com, srhines@google.com, allenhair@google.com
-per-file tidy.md = chh@google.com, srhines@google.com
diff --git a/docs/clion.md b/docs/clion.md
index d6ae19a..110891b 100644
--- a/docs/clion.md
+++ b/docs/clion.md
@@ -3,6 +3,10 @@
Soong can generate CLion projects. This is intended for source code editing
only. Build should still be done via make/m/mm(a)/mmm(a).
+Note: alternatively, you can use
+[aidegen to generate a Clion or VSCode project](https://android.googlesource.com/platform/tools/asuite/+/refs/heads/master/aidegen/README.md)
+with a single command, using the `-i c` flag.
+
CMakeLists.txt project file generation is enabled via environment variable:
```bash
diff --git a/docs/map_files.md b/docs/map_files.md
index 1388059..35e8cbb 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -148,9 +148,24 @@
### systemapi
-This is a synonym of the `apex` tag. It should be used to clarify that the API
-is an API exposed by the system for an APEX, whereas `apex` should be used for
-APIs exposed by an APEX to the platform or another APEX.
+Indicates that the symbol is exposed by the platform for an apex. Whereas `apex`
+should be used for APIs exposed by an APEX to the platform or another APEX.
+
+May be used in combination with `llndk` if the symbol is exposed to both APEX
+and the LL-NDK.
+
+Since a single library can be installed ether in platform or an apex, but not
+both, a single map.txt file should not contain _both_ # apex and # systemapi symbols.
+
+The granularity between # apex and # systemapi exists to help the API review
+process (b/191371676). These two symbols have very similar lifetime "in
+practice". A #systemapi symbol can be dropped from the next release if we are
+confident that no one is using it. Similarily, #apex can be dropped if we are
+sure that the old platform which used the symbol has reached EOL and thus is no
+longer accepting new APEX updates. Unlike the APIs for apps where we have zero
+control over how APIs are used, we are in a much more controllable environment
+when talking about #systemapi and #apex symbols. So, we have some flexibility
+here when determining the lifetime of a symbol.
### var
diff --git a/docs/perf.md b/docs/perf.md
index 86a27b4..5b53c8d 100644
--- a/docs/perf.md
+++ b/docs/perf.md
@@ -42,16 +42,29 @@
```
If the elapsed time is much longer than the critical path then additional
-parallelism on the build machine will improve total build times. If there are
+parallelism on the build machine will improve total build times. If there are
long individual times listed in the critical path then improving build times
for those steps or adjusting dependencies so that those steps can run earlier
in the build graph will improve total build times.
### Soong
-Soong can be traced and profiled using the standard Go tools. It understands
-the `-cpuprofile`, `-trace`, and `-memprofile` command line arguments, but we
-don't currently have an easy way to enable them in the context of a full build.
+Soong proper (i.e., `soong_build` executable that processes the blueprint
+files) can be traced and profiled using the standard Go tools. It understands
+the `-trace`, `-cpuprofile`, and `-memprofile` command line arguments.
+Setting `SOONG_PROFILE_CPU` and/or `SOONG_PROFILE_MEM` environment variables
+for the build enables respective profiling, e.g., running
+
+```shell
+SOONG_PROFILE_CPU=/tmp/foo m ..._
+```
+
+saves CPU profile for each Soong invocation in /tmp/foo._step_ file, where
+_step_ is Soong execution step. The main step is `build`. The others as
+`bp2build_files`, `bp2build_workspace`, `modulegraph`, `queryview`,
+`api_bp2build`, `soong_docs` (not all of them necessarily run during the build).
+The profiles can be inspected with `go tool pprof` from the command line or
+with _Run>Open Profiler Snapshot_ in IntelliJ IDEA.
### Kati
@@ -221,6 +234,18 @@
various .ninja files. The files are (mostly) human-readable, but a (slow) web
interface can be used by running `NINJA_ARGS="-t browse <target>" m`.
+There is also `SOONG_UI_NINJA_ARGS`, which passes ninja arguments to soong ui's
+ninja invocations, e.g. to emit $OUT_DIR/soong/build.ninja, $OUT_DIR/soong/module-graph.json, etc.
+
+```bash
+$ m nothing
+$ touch Android.bp
+$ SOONG_UI_NINJA_ARGS="-d explain" m nothing
+...
+ninja explain: restat of output out/soong/build.ninja older than most recent input Android.bp
+...
+```
+
#### Builds take a long time
If the long part in the trace view of a build is a relatively solid block, then
diff --git a/docs/tidy.md b/docs/tidy.md
index 890c3a0..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.
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 719771f..3e1bbde 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -31,6 +31,7 @@
"encoding/json"
"fmt"
"path/filepath"
+ "reflect"
"strings"
"github.com/google/blueprint/proptools"
@@ -53,6 +54,7 @@
func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
+ ctx.RegisterModuleType("prebuilt_etc_cacerts", PrebuiltEtcCaCertsFactory)
ctx.RegisterModuleType("prebuilt_root", PrebuiltRootFactory)
ctx.RegisterModuleType("prebuilt_root_host", PrebuiltRootHostFactory)
ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
@@ -295,27 +297,37 @@
}
func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- if p.properties.Src == nil {
- ctx.PropertyErrorf("src", "missing prebuilt source file")
- return
- }
- p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
-
- // Determine the output file basename.
- // If Filename is set, use the name specified by the property.
- // If Filename_from_src is set, use the source file name.
- // Otherwise use the module name.
filename := proptools.String(p.properties.Filename)
filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
- if filename != "" {
- if filenameFromSrc {
- ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
- return
+ if p.properties.Src != nil {
+ p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
+
+ // Determine the output file basename.
+ // If Filename is set, use the name specified by the property.
+ // If Filename_from_src is set, use the source file name.
+ // Otherwise use the module name.
+ if filename != "" {
+ if filenameFromSrc {
+ ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+ return
+ }
+ } else if filenameFromSrc {
+ filename = p.sourceFilePath.Base()
+ } else {
+ filename = ctx.ModuleName()
}
- } else if filenameFromSrc {
- filename = p.sourceFilePath.Base()
+ } else if ctx.Config().AllowMissingDependencies() {
+ // If no srcs was set and AllowMissingDependencies is enabled then
+ // mark the module as missing dependencies and set a fake source path
+ // and file name.
+ ctx.AddMissingDependencies([]string{"MISSING_PREBUILT_SRC_FILE"})
+ p.sourceFilePath = android.PathForModuleSrc(ctx)
+ if filename == "" {
+ filename = ctx.ModuleName()
+ }
} else {
- filename = ctx.ModuleName()
+ ctx.PropertyErrorf("src", "missing prebuilt source file")
+ return
}
p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
@@ -444,6 +456,17 @@
return module
}
+// prebuilt_etc_host is for a host prebuilt artifact that is installed in
+// <partition>/etc/<sub_dir> directory.
+func PrebuiltEtcCaCertsFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "cacerts")
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitBazelModule(module)
+ return module
+}
+
// prebuilt_root is for a prebuilt artifact that is installed in
// <partition>/ directory. Can't have any sub directories.
func PrebuiltRootFactory() android.Module {
@@ -594,7 +617,7 @@
return true
}
-func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
+func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) snapshot.SnapshotPaths {
/*
Snapshot zipped artifacts directory structure for etc modules:
{SNAPSHOT_ARCH}/
@@ -608,7 +631,7 @@
(notice files)
*/
var snapshotOutputs android.Paths
- noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+ var snapshotNotices android.Paths
installedNotices := make(map[string]bool)
ctx.VisitAllModules(func(module android.Module) {
@@ -628,10 +651,8 @@
prop := snapshot.SnapshotJsonFlags{}
propOut := snapshotLibOut + ".json"
- prop.ModuleName = m.BaseModuleName()
- if m.subdirProperties.Relative_install_path != nil {
- prop.RelativeInstallPath = *m.subdirProperties.Relative_install_path
- }
+ prop.InitBaseSnapshotProps(m)
+ prop.RelativeInstallPath = m.SubDir()
if m.properties.Filename != nil {
prop.Filename = *m.properties.Filename
@@ -644,41 +665,32 @@
}
snapshotOutputs = append(snapshotOutputs, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
- if len(m.EffectiveLicenseFiles()) > 0 {
- noticeName := ctx.ModuleName(m) + ".txt"
- noticeOut := filepath.Join(noticeDir, noticeName)
- // skip already copied notice file
- if !installedNotices[noticeOut] {
- installedNotices[noticeOut] = true
-
- noticeOutPath := android.PathForOutput(ctx, noticeOut)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cat,
- Inputs: m.EffectiveLicenseFiles(),
- Output: noticeOutPath,
- Description: "combine notices for " + noticeOut,
- })
- snapshotOutputs = append(snapshotOutputs, noticeOutPath)
+ for _, notice := range m.EffectiveLicenseFiles() {
+ if _, ok := installedNotices[notice.String()]; !ok {
+ installedNotices[notice.String()] = true
+ snapshotNotices = append(snapshotNotices, notice)
}
}
})
- return snapshotOutputs
+ return snapshot.SnapshotPaths{OutputFiles: snapshotOutputs, NoticeFiles: snapshotNotices}
}
// For Bazel / bp2build
type bazelPrebuiltFileAttributes struct {
- Src bazel.LabelAttribute
- Filename string
- Dir string
- Installable bazel.BoolAttribute
+ Src bazel.LabelAttribute
+ Filename bazel.LabelAttribute
+ Dir string
+ Installable bazel.BoolAttribute
+ Filename_from_src bazel.BoolAttribute
}
-// ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
-// All prebuilt_* modules are PrebuiltEtc, which we treat uniformily as *PrebuiltFile*
-func (module *PrebuiltEtc) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+// Bp2buildHelper returns a bazelPrebuiltFileAttributes used for the conversion
+// of prebuilt_* modules. bazelPrebuiltFileAttributes has the common attributes
+// used by both prebuilt_etc_xml and other prebuilt_* moodules
+func (module *PrebuiltEtc) Bp2buildHelper(ctx android.TopDownMutatorContext) *bazelPrebuiltFileAttributes {
var src bazel.LabelAttribute
for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltEtcProperties{}) {
for config, p := range configToProps {
@@ -691,18 +703,40 @@
src.SetSelectValue(axis, config, label)
}
}
+
+ for propName, productConfigProps := range android.ProductVariableProperties(ctx, ctx.Module()) {
+ for configProp, propVal := range productConfigProps {
+ if propName == "Src" {
+ props, ok := propVal.(*string)
+ if !ok {
+ ctx.PropertyErrorf(" Expected Property to have type string, but was %s\n", reflect.TypeOf(propVal).String())
+ continue
+ }
+ if props != nil {
+ label := android.BazelLabelForModuleSrcSingle(ctx, *props)
+ src.SetSelectValue(configProp.ConfigurationAxis(), configProp.SelectKey(), label)
+ }
+ }
+ }
+ }
}
var filename string
- if module.properties.Filename != nil {
- filename = *module.properties.Filename
+ var filenameFromSrc bool
+ moduleProps := module.properties
+
+ if moduleProps.Filename != nil && *moduleProps.Filename != "" {
+ filename = *moduleProps.Filename
+ } else if moduleProps.Filename_from_src != nil && *moduleProps.Filename_from_src {
+ if moduleProps.Src != nil {
+ filename = *moduleProps.Src
+ }
+ filenameFromSrc = true
+ } else {
+ filename = ctx.ModuleName()
}
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
}
@@ -714,11 +748,32 @@
attrs := &bazelPrebuiltFileAttributes{
Src: src,
- Filename: filename,
Dir: dir,
Installable: installable,
}
+ if filename != "" {
+ attrs.Filename = bazel.LabelAttribute{Value: &bazel.Label{Label: filename}}
+ } else if filenameFromSrc {
+ attrs.Filename_from_src = bazel.BoolAttribute{Value: moduleProps.Filename_from_src}
+ }
+
+ return attrs
+
+}
+
+// ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
+// prebuilt_* modules (except prebuilt_etc_xml) are PrebuiltEtc,
+// which we treat as *PrebuiltFile*
+func (module *PrebuiltEtc) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ var dir = module.installDirBase
+ // prebuilt_file supports only `etc` or `usr/share`
+ if !(dir == "etc" || dir == "usr/share") {
+ return
+ }
+
+ attrs := module.Bp2buildHelper(ctx)
+
props := bazel.BazelTargetModuleProperties{
Rule_class: "prebuilt_file",
Bzl_load_location: "//build/bazel/rules:prebuilt_file.bzl",
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index cf1f6d7..0d44c31 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -140,6 +140,7 @@
"LOCAL_REQUIRED_MODULES": {"modA", "moduleB"},
"LOCAL_HOST_REQUIRED_MODULES": {"hostModA", "hostModB"},
"LOCAL_TARGET_REQUIRED_MODULES": {"targetModA"},
+ "LOCAL_SOONG_MODULE_TYPE": {"prebuilt_etc"},
}
mod := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
@@ -195,6 +196,30 @@
}
}
+func TestPrebuiltEtcAllowMissingDependencies(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltEtcTest,
+ android.PrepareForTestDisallowNonExistentPaths,
+ android.FixtureModifyConfig(
+ func(config android.Config) {
+ config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+ }),
+ ).RunTestWithBp(t, `
+ prebuilt_etc {
+ name: "foo.conf",
+ filename_from_src: true,
+ arch: {
+ x86: {
+ src: "x86.conf",
+ },
+ },
+ }
+ `)
+
+ android.AssertStringEquals(t, "expected error rule", "android/soong/android.Error",
+ result.ModuleForTests("foo.conf", "android_arm64_armv8-a").Output("foo.conf").Rule.String())
+}
+
func TestPrebuiltRootInstallDirPath(t *testing.T) {
result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_root {
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index 38684d3..07d57c9 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -12,9 +12,12 @@
"soong-linkerconfig",
],
srcs: [
+ "avb_add_hash_footer.go",
+ "avb_gen_vbmeta_image.go",
"bootimg.go",
"filesystem.go",
"logical_partition.go",
+ "raw_binary.go",
"system_image.go",
"vbmeta.go",
"testing.go",
diff --git a/filesystem/avb_add_hash_footer.go b/filesystem/avb_add_hash_footer.go
new file mode 100644
index 0000000..f3fecd0
--- /dev/null
+++ b/filesystem/avb_add_hash_footer.go
@@ -0,0 +1,197 @@
+// 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"
+ "strconv"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+type avbAddHashFooter struct {
+ android.ModuleBase
+
+ properties avbAddHashFooterProperties
+
+ output android.OutputPath
+ installDir android.InstallPath
+}
+
+type avbProp struct {
+ // Name of a property
+ Name *string
+
+ // Value of a property. Can't be used together with `file`.
+ Value *string
+
+ // File from which the value of the prop is read from. Can't be used together with `value`.
+ File *string `android:"path,arch_variant"`
+}
+
+type avbAddHashFooterProperties struct {
+ // Source file of this image. Can reference a genrule type module with the ":module" syntax.
+ Src *string `android:"path,arch_variant"`
+
+ // Set the name of the output. Defaults to <module_name>.img.
+ Filename *string
+
+ // Name of the image partition. Defaults to the name of this module.
+ Partition_name *string
+
+ // Size of the partition. Defaults to dynamically calculating the size.
+ Partition_size *int64
+
+ // Path to the private key that avbtool will use to sign this image.
+ Private_key *string `android:"path"`
+
+ // Algorithm that avbtool will use to sign this image. Default is SHA256_RSA4096.
+ Algorithm *string
+
+ // The salt in hex. Required for reproducible builds.
+ Salt *string
+
+ // List of properties to add to the footer
+ Props []avbProp
+
+ // Include descriptors from images
+ Include_descriptors_from_images []string `android:"path,arch_variant"`
+}
+
+// The AVB footer adds verification information to the image.
+func avbAddHashFooterFactory() android.Module {
+ module := &avbAddHashFooter{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
+
+func (a *avbAddHashFooter) installFileName() string {
+ return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".img")
+}
+
+func (a *avbAddHashFooter) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ builder := android.NewRuleBuilder(pctx, ctx)
+
+ if a.properties.Src == nil {
+ ctx.PropertyErrorf("src", "missing source file")
+ return
+ }
+ input := android.PathForModuleSrc(ctx, proptools.String(a.properties.Src))
+ a.output = android.PathForModuleOut(ctx, a.installFileName()).OutputPath
+ builder.Command().Text("cp").Input(input).Output(a.output)
+
+ cmd := builder.Command().BuiltTool("avbtool").Text("add_hash_footer")
+
+ partition_name := proptools.StringDefault(a.properties.Partition_name, a.BaseModuleName())
+ cmd.FlagWithArg("--partition_name ", partition_name)
+
+ if a.properties.Partition_size == nil {
+ cmd.Flag("--dynamic_partition_size")
+ } else {
+ partition_size := proptools.Int(a.properties.Partition_size)
+ cmd.FlagWithArg("--partition_size ", strconv.Itoa(partition_size))
+ }
+
+ key := android.PathForModuleSrc(ctx, proptools.String(a.properties.Private_key))
+ cmd.FlagWithInput("--key ", key)
+
+ algorithm := proptools.StringDefault(a.properties.Algorithm, "SHA256_RSA4096")
+ cmd.FlagWithArg("--algorithm ", algorithm)
+
+ if a.properties.Salt == nil {
+ ctx.PropertyErrorf("salt", "missing salt value")
+ return
+ }
+ cmd.FlagWithArg("--salt ", proptools.String(a.properties.Salt))
+
+ imagePaths := android.PathsForModuleSrc(ctx, a.properties.Include_descriptors_from_images)
+ for _, imagePath := range imagePaths {
+ cmd.FlagWithInput("--include_descriptors_from_image ", imagePath)
+ }
+
+ for _, prop := range a.properties.Props {
+ addAvbProp(ctx, cmd, prop)
+ }
+
+ cmd.FlagWithOutput("--image ", a.output)
+
+ builder.Build("avbAddHashFooter", fmt.Sprintf("avbAddHashFooter %s", ctx.ModuleName()))
+
+ a.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(a.installDir, a.installFileName(), a.output)
+}
+
+func addAvbProp(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, prop avbProp) {
+ name := proptools.String(prop.Name)
+ value := proptools.String(prop.Value)
+ file := proptools.String(prop.File)
+ if name == "" {
+ ctx.PropertyErrorf("name", "can't be empty")
+ return
+ }
+ if value == "" && file == "" {
+ ctx.PropertyErrorf("value", "either value or file should be set")
+ return
+ }
+ if value != "" && file != "" {
+ ctx.PropertyErrorf("value", "value and file can't be set at the same time")
+ return
+ }
+
+ if value != "" {
+ cmd.FlagWithArg("--prop ", proptools.ShellEscape(fmt.Sprintf("%s:%s", name, value)))
+ } else {
+ p := android.PathForModuleSrc(ctx, file)
+ cmd.Implicit(p)
+ cmd.FlagWithArg("--prop_from_file ", proptools.ShellEscape(fmt.Sprintf("%s:%s", name, cmd.PathForInput(p))))
+ }
+}
+
+var _ android.AndroidMkEntriesProvider = (*avbAddHashFooter)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (a *avbAddHashFooter) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(a.output),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", a.installDir.String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", a.installFileName())
+ },
+ },
+ }}
+}
+
+var _ Filesystem = (*avbAddHashFooter)(nil)
+
+func (a *avbAddHashFooter) OutputPath() android.Path {
+ return a.output
+}
+
+func (a *avbAddHashFooter) SignedOutputPath() android.Path {
+ return a.OutputPath() // always signed
+}
+
+// TODO(b/185115783): remove when not needed as input to a prebuilt_etc rule
+var _ android.SourceFileProducer = (*avbAddHashFooter)(nil)
+
+// Implements android.SourceFileProducer
+func (a *avbAddHashFooter) Srcs() android.Paths {
+ return append(android.Paths{}, a.output)
+}
diff --git a/filesystem/avb_gen_vbmeta_image.go b/filesystem/avb_gen_vbmeta_image.go
new file mode 100644
index 0000000..0f331f9
--- /dev/null
+++ b/filesystem/avb_gen_vbmeta_image.go
@@ -0,0 +1,108 @@
+// 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/proptools"
+
+ "android/soong/android"
+)
+
+type avbGenVbmetaImage struct {
+ android.ModuleBase
+
+ properties avbGenVbmetaImageProperties
+
+ output android.OutputPath
+ installDir android.InstallPath
+}
+
+type avbGenVbmetaImageProperties struct {
+ // Source file of this image. Can reference a genrule type module with the ":module" syntax.
+ Src *string `android:"path,arch_variant"`
+
+ // Name of the image partition. Defaults to the name of this module.
+ Partition_name *string
+
+ // The salt in hex. Required for reproducible builds.
+ Salt *string
+}
+
+// The avbGenVbmetaImage generates an unsigned VBMeta image output for the given image.
+func avbGenVbmetaImageFactory() android.Module {
+ module := &avbGenVbmetaImage{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
+
+func (a *avbGenVbmetaImage) installFileName() string {
+ return a.Name() + ".img"
+}
+
+func (a *avbGenVbmetaImage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ builder := android.NewRuleBuilder(pctx, ctx)
+ cmd := builder.Command().BuiltTool("avbtool").Text("add_hash_footer")
+ cmd.Flag("--dynamic_partition_size")
+ cmd.Flag("--do_not_append_vbmeta_image")
+
+ partition_name := proptools.StringDefault(a.properties.Partition_name, a.Name())
+ cmd.FlagWithArg("--partition_name ", partition_name)
+
+ if a.properties.Src == nil {
+ ctx.PropertyErrorf("src", "missing source file")
+ return
+ }
+ input := android.PathForModuleSrc(ctx, proptools.String(a.properties.Src))
+ cmd.FlagWithInput("--image ", input)
+
+ if a.properties.Salt == nil {
+ ctx.PropertyErrorf("salt", "missing salt value")
+ return
+ }
+ cmd.FlagWithArg("--salt ", proptools.String(a.properties.Salt))
+
+ a.output = android.PathForModuleOut(ctx, a.installFileName()).OutputPath
+ cmd.FlagWithOutput("--output_vbmeta_image ", a.output)
+ builder.Build("avbGenVbmetaImage", fmt.Sprintf("avbGenVbmetaImage %s", ctx.ModuleName()))
+}
+
+var _ android.AndroidMkEntriesProvider = (*avbGenVbmetaImage)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (a *avbGenVbmetaImage) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(a.output),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", a.installDir.String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", a.installFileName())
+ },
+ },
+ }}
+}
+
+var _ android.OutputFileProducer = (*avbGenVbmetaImage)(nil)
+
+// Implements android.OutputFileProducer
+func (a *avbGenVbmetaImage) OutputFiles(tag string) (android.Paths, error) {
+ if tag == "" {
+ return []android.Path{a.output}, nil
+ }
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
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 665faaa..023c69a 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -15,11 +15,14 @@
package filesystem
import (
+ "crypto/sha256"
"fmt"
+ "io"
"path/filepath"
"strings"
"android/soong/android"
+ "android/soong/cc"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -32,6 +35,8 @@
func registerBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("android_filesystem", filesystemFactory)
ctx.RegisterModuleType("android_system_image", systemImageFactory)
+ ctx.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory)
+ ctx.RegisterModuleType("avb_gen_vbmeta_image", avbGenVbmetaImageFactory)
}
type filesystem struct {
@@ -92,6 +97,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
@@ -255,6 +267,14 @@
Input(rootZip).
Input(rebasedDepsZip)
+ // run host_init_verifier
+ // Ideally we should have a concept of pluggable linters that verify the generated image.
+ // While such concept is not implement this will do.
+ // TODO(b/263574231): substitute with pluggable linter.
+ builder.Command().
+ BuiltTool("host_init_verifier").
+ FlagWithArg("--out_system=", rootDir.String()+"/system")
+
propFile, toolDeps := f.buildPropFile(ctx)
output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
builder.Command().BuiltTool("build_image").
@@ -280,6 +300,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
@@ -329,12 +354,19 @@
addStr("avb_add_hashtree_footer_args", avb_add_hashtree_footer_args)
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)
@@ -459,3 +491,19 @@
}
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))
+}
+
+// Base cc.UseCoverage
+
+var _ cc.UseCoverage = (*filesystem)(nil)
+
+func (*filesystem) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+ return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index cda06d9..aef4756 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -20,6 +20,9 @@
"android/soong/android"
"android/soong/cc"
+ "android/soong/etc"
+
+ "github.com/google/blueprint/proptools"
)
func TestMain(m *testing.M) {
@@ -28,6 +31,7 @@
var fixture = android.GroupFixturePreparers(
android.PrepareForIntegrationTestWithAndroid,
+ etc.PrepareForTestWithPrebuiltEtc,
cc.PrepareForIntegrationTestWithCc,
PrepareForTestWithFilesystemBuildComponents,
)
@@ -125,3 +129,156 @@
module := result.ModuleForTests("myfilesystem", "android_common").Module().(*systemImage)
android.AssertDeepEquals(t, "entries should have foo only", []string{"components/foo"}, module.entries)
}
+
+func TestAvbGenVbmetaImage(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ avb_gen_vbmeta_image {
+ name: "input_hashdesc",
+ src: "input.img",
+ partition_name: "input_partition_name",
+ salt: "2222",
+ }`)
+ cmd := result.ModuleForTests("input_hashdesc", "android_arm64_armv8-a").Rule("avbGenVbmetaImage").RuleParams.Command
+ android.AssertStringDoesContain(t, "Can't find correct --partition_name argument",
+ cmd, "--partition_name input_partition_name")
+ android.AssertStringDoesContain(t, "Can't find --do_not_append_vbmeta_image",
+ cmd, "--do_not_append_vbmeta_image")
+ android.AssertStringDoesContain(t, "Can't find --output_vbmeta_image",
+ cmd, "--output_vbmeta_image ")
+ android.AssertStringDoesContain(t, "Can't find --salt argument",
+ cmd, "--salt 2222")
+}
+
+func TestAvbAddHashFooter(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ avb_gen_vbmeta_image {
+ name: "input_hashdesc",
+ src: "input.img",
+ partition_name: "input",
+ salt: "2222",
+ }
+
+ avb_add_hash_footer {
+ name: "myfooter",
+ src: "input.img",
+ filename: "output.img",
+ partition_name: "mypartition",
+ private_key: "mykey",
+ salt: "1111",
+ props: [
+ {
+ name: "prop1",
+ value: "value1",
+ },
+ {
+ name: "prop2",
+ file: "value_file",
+ },
+ ],
+ include_descriptors_from_images: ["input_hashdesc"],
+ }
+ `)
+ cmd := result.ModuleForTests("myfooter", "android_arm64_armv8-a").Rule("avbAddHashFooter").RuleParams.Command
+ android.AssertStringDoesContain(t, "Can't find correct --partition_name argument",
+ cmd, "--partition_name mypartition")
+ android.AssertStringDoesContain(t, "Can't find correct --key argument",
+ cmd, "--key mykey")
+ android.AssertStringDoesContain(t, "Can't find --salt argument",
+ cmd, "--salt 1111")
+ android.AssertStringDoesContain(t, "Can't find --prop argument",
+ cmd, "--prop 'prop1:value1'")
+ android.AssertStringDoesContain(t, "Can't find --prop_from_file argument",
+ cmd, "--prop_from_file 'prop2:value_file'")
+ android.AssertStringDoesContain(t, "Can't find --include_descriptors_from_image",
+ cmd, "--include_descriptors_from_image ")
+}
+
+func TestFileSystemShouldInstallCoreVariantIfTargetBuildAppsIsSet(t *testing.T) {
+ context := android.GroupFixturePreparers(
+ fixture,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Unbundled_build_apps = []string{"bar"}
+ }),
+ )
+ result := context.RunTestWithBp(t, `
+ android_system_image {
+ name: "myfilesystem",
+ deps: [
+ "libfoo",
+ ],
+ linker_config_src: "linker.config.json",
+ }
+
+ cc_library {
+ name: "libfoo",
+ shared_libs: [
+ "libbar",
+ ],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libbar",
+ sdk_version: "9",
+ stl: "none",
+ }
+ `)
+
+ inputs := result.ModuleForTests("myfilesystem", "android_common").Output("deps.zip").Implicits
+ android.AssertStringListContains(t, "filesystem should have libbar even for unbundled build",
+ inputs.Strings(),
+ "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
+}
+
+func TestFileSystemWithCoverageVariants(t *testing.T) {
+ context := android.GroupFixturePreparers(
+ fixture,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.GcovCoverage = proptools.BoolPtr(true)
+ variables.Native_coverage = proptools.BoolPtr(true)
+ }),
+ )
+
+ result := context.RunTestWithBp(t, `
+ prebuilt_etc {
+ name: "prebuilt",
+ src: ":myfilesystem",
+ }
+
+ android_system_image {
+ name: "myfilesystem",
+ deps: [
+ "libfoo",
+ ],
+ linker_config_src: "linker.config.json",
+ }
+
+ cc_library {
+ name: "libfoo",
+ shared_libs: [
+ "libbar",
+ ],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libbar",
+ stl: "none",
+ }
+ `)
+
+ filesystem := result.ModuleForTests("myfilesystem", "android_common_cov")
+ inputs := filesystem.Output("deps.zip").Implicits
+ android.AssertStringListContains(t, "filesystem should have libfoo(cov)",
+ inputs.Strings(),
+ "out/soong/.intermediates/libfoo/android_arm64_armv8-a_shared_cov/libfoo.so")
+ android.AssertStringListContains(t, "filesystem should have libbar(cov)",
+ inputs.Strings(),
+ "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared_cov/libbar.so")
+
+ filesystemOutput := filesystem.Output("myfilesystem.img").Output
+ prebuiltInput := result.ModuleForTests("prebuilt", "android_arm64_armv8-a").Rule("Cp").Input
+ if filesystemOutput != prebuiltInput {
+ t.Error("prebuilt should use cov variant of filesystem")
+ }
+}
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/finder/finder.go b/finder/finder.go
index 5961290..054ccd6 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
@@ -1415,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/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 89f8187..79d2412 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -18,6 +18,7 @@
import (
"encoding/json"
+ "fmt"
"sort"
"strings"
@@ -29,11 +30,20 @@
type Lang string
const (
- Cc Lang = ""
+ Cc Lang = "cc"
Rust Lang = "rust"
Java Lang = "java"
)
+type Framework string
+
+const (
+ AFL Framework = "afl"
+ LibFuzzer Framework = "libfuzzer"
+ Jazzer Framework = "jazzer"
+ UnknownFramework Framework = "unknownframework"
+)
+
var BoolDefault = proptools.BoolDefault
type FuzzModule struct {
@@ -59,16 +69,257 @@
Dir string
}
+type Vector string
+
+const (
+ unknown_access_vector Vector = "unknown_access_vector"
+ // The code being fuzzed is reachable from a remote source, or using data
+ // provided by a remote source. For example: media codecs process media files
+ // from the internet, SMS processing handles remote message data.
+ // See
+ // https://source.android.com/docs/security/overview/updates-resources#local-vs-remote
+ // for an explanation of what's considered "remote."
+ remote = "remote"
+ // The code being fuzzed can only be reached locally, such as from an
+ // installed app. As an example, if it's fuzzing a Binder interface, it's
+ // assumed that you'd need a local app to make arbitrary Binder calls.
+ // And the app that's calling the fuzzed code does not require any privileges;
+ // any 3rd party app could make these calls.
+ local_no_privileges_required = "local_no_privileges_required"
+ // The code being fuzzed can only be called locally, and the calling process
+ // requires additional permissions that prevent arbitrary 3rd party apps from
+ // calling the code. For instance: this requires a privileged or signature
+ // permission to reach, or SELinux restrictions prevent the untrusted_app
+ // domain from calling it.
+ local_privileges_required = "local_privileges_required"
+ // The code is only callable on a PC host, not on a production Android device.
+ // For instance, this is fuzzing code used during the build process, or
+ // tooling that does not exist on a user's actual Android device.
+ host_access = "host_access"
+ // The code being fuzzed is only reachable if the user has enabled Developer
+ // Options, or has enabled a persistent Developer Options setting.
+ local_with_developer_options = "local_with_developer_options"
+)
+
+func (vector Vector) isValidVector() bool {
+ switch vector {
+ case "",
+ unknown_access_vector,
+ remote,
+ local_no_privileges_required,
+ local_privileges_required,
+ host_access,
+ local_with_developer_options:
+ return true
+ }
+ return false
+}
+
+type ServicePrivilege string
+
+const (
+ unknown_service_privilege ServicePrivilege = "unknown_service_privilege"
+ // The code being fuzzed runs on a Secure Element. This has access to some
+ // of the most privileged data on the device, such as authentication keys.
+ // Not all devices have a Secure Element.
+ secure_element = "secure_element"
+ // The code being fuzzed runs in the TEE. The TEE is designed to be resistant
+ // to a compromised kernel, and stores sensitive data.
+ trusted_execution = "trusted_execution"
+ // The code being fuzzed has privileges beyond what arbitrary 3rd party apps
+ // have. For instance, it's running as the System UID, or it's in an SELinux
+ // domain that's able to perform calls that can't be made by 3rd party apps.
+ privileged = "privileged"
+ // The code being fuzzed is equivalent to a 3rd party app. It runs in the
+ // untrusted_app SELinux domain, or it only has privileges that are equivalent
+ // to what a 3rd party app could have.
+ unprivileged = "unprivileged"
+ // The code being fuzzed is significantly constrained, and even if it's
+ // compromised, it has significant restrictions that prevent it from
+ // performing most actions. This is significantly more restricted than
+ // UNPRIVILEGED. An example is the isolatedProcess=true setting in a 3rd
+ // party app. Or a process that's very restricted by SELinux, such as
+ // anything in the mediacodec SELinux domain.
+ constrained = "constrained"
+ // The code being fuzzed always has Negligible Security Impact. Even
+ // arbitrary out of bounds writes and full code execution would not be
+ // considered a security vulnerability. This typically only makes sense if
+ // FuzzedCodeUsage is set to FUTURE_VERSION or EXPERIMENTAL, and if
+ // AutomaticallyRouteTo is set to ALWAYS_NSI.
+ nsi = "nsi"
+ // The code being fuzzed only runs on a PC host, not on a production Android
+ // device. For instance, the fuzzer is fuzzing code used during the build
+ // process, or tooling that does not exist on a user's actual Android device.
+ host_only = "host_only"
+)
+
+func (service_privilege ServicePrivilege) isValidServicePrivilege() bool {
+ switch service_privilege {
+ case "",
+ unknown_service_privilege,
+ secure_element,
+ trusted_execution,
+ privileged,
+ unprivileged,
+ constrained,
+ nsi,
+ host_only:
+ return true
+ }
+ return false
+}
+
+type UserData string
+
+const (
+ unknown_user_data UserData = "unknown_user_data"
+ // The process being fuzzed only handles data from a single user, or from a
+ // single process or app. It's possible the process shuts down before
+ // handling data from another user/process/app, or it's possible the process
+ // only ever handles one user's/process's/app's data. As an example, some
+ // print spooler processes are started for a single document and terminate
+ // when done, so each instance only handles data from a single user/app.
+ single_user = "single_user"
+ // The process handles data from multiple users, or from multiple other apps
+ // or processes. Media processes, for instance, can handle media requests
+ // from multiple different apps without restarting. Wi-Fi and network
+ // processes handle data from multiple users, and processes, and apps.
+ multi_user = "multi_user"
+)
+
+func (user_data UserData) isValidUserData() bool {
+ switch user_data {
+ case "",
+ unknown_user_data,
+ single_user,
+ multi_user:
+ return true
+ }
+ return false
+}
+
+type FuzzedCodeUsage string
+
+const (
+ undefined FuzzedCodeUsage = "undefined"
+ unknown = "unknown"
+ // The code being fuzzed exists in a shipped version of Android and runs on
+ // devices in production.
+ shipped = "shipped"
+ // The code being fuzzed is not yet in a shipping version of Android, but it
+ // will be at some point in the future.
+ future_version = "future_version"
+ // The code being fuzzed is not in a shipping version of Android, and there
+ // are no plans to ship it in the future.
+ experimental = "experimental"
+)
+
+func (fuzzed_code_usage FuzzedCodeUsage) isValidFuzzedCodeUsage() bool {
+ switch fuzzed_code_usage {
+ case "",
+ undefined,
+ unknown,
+ shipped,
+ future_version,
+ experimental:
+ return true
+ }
+ return false
+}
+
+type AutomaticallyRouteTo string
+
+const (
+ undefined_routing AutomaticallyRouteTo = "undefined_routing"
+ // Automatically route this to the Android Automotive security team for
+ // assessment.
+ android_automotive = "android_automotive"
+ // This should not be used in fuzzer configurations. It is used internally
+ // by Severity Assigner to flag memory leak reports.
+ memory_leak = "memory_leak"
+ // Route this vulnerability to our Ittiam vendor team for assessment.
+ ittiam = "ittiam"
+ // Reports from this fuzzer are always NSI (see the NSI ServicePrivilegeEnum
+ // value for additional context). It is not possible for this code to ever
+ // have a security vulnerability.
+ always_nsi = "always_nsi"
+ // Route this vulnerability to AIDL team for assessment.
+ aidl = "aidl"
+)
+
+func (automatically_route_to AutomaticallyRouteTo) isValidAutomaticallyRouteTo() bool {
+ switch automatically_route_to {
+ case "",
+ undefined_routing,
+ android_automotive,
+ memory_leak,
+ ittiam,
+ always_nsi,
+ aidl:
+ return true
+ }
+ return false
+}
+
+func IsValidConfig(fuzzModule FuzzPackagedModule, moduleName string) bool {
+ var config = fuzzModule.FuzzProperties.Fuzz_config
+ if config != nil {
+ if !config.Vector.isValidVector() {
+ panic(fmt.Errorf("Invalid vector in fuzz config in %s", moduleName))
+ }
+
+ if !config.Service_privilege.isValidServicePrivilege() {
+ panic(fmt.Errorf("Invalid service_privilege in fuzz config in %s", moduleName))
+ }
+
+ if !config.Users.isValidUserData() {
+ panic(fmt.Errorf("Invalid users (user_data) in fuzz config in %s", moduleName))
+ }
+
+ if !config.Fuzzed_code_usage.isValidFuzzedCodeUsage() {
+ panic(fmt.Errorf("Invalid fuzzed_code_usage in fuzz config in %s", moduleName))
+ }
+
+ if !config.Automatically_route_to.isValidAutomaticallyRouteTo() {
+ panic(fmt.Errorf("Invalid automatically_route_to in fuzz config in %s", moduleName))
+ }
+ }
+ return true
+}
+
type FuzzConfig struct {
// Email address of people to CC on bugs or contact about this fuzz target.
Cc []string `json:"cc,omitempty"`
+ // A brief description of what the fuzzed code does.
+ Description string `json:"description,omitempty"`
+ // Whether the code being fuzzed is remotely accessible or requires privileges
+ // to access locally.
+ Vector Vector `json:"vector,omitempty"`
+ // How privileged the service being fuzzed is.
+ Service_privilege ServicePrivilege `json:"service_privilege,omitempty"`
+ // Whether the service being fuzzed handles data from multiple users or only
+ // a single one.
+ Users UserData `json:"users,omitempty"`
+ // Specifies the use state of the code being fuzzed. This state factors into
+ // how an issue is handled.
+ Fuzzed_code_usage FuzzedCodeUsage `json:"fuzzed_code_usage,omitempty"`
+ // Comment describing how we came to these settings for this fuzzer.
+ Config_comment string
+ // Which team to route this to, if it should be routed automatically.
+ Automatically_route_to AutomaticallyRouteTo `json:"automatically_route_to,omitempty"`
+ // Can third party/untrusted apps supply data to fuzzed code.
+ Untrusted_data *bool `json:"untrusted_data,omitempty"`
+ // When code was released or will be released.
+ Production_date string `json:"production_date,omitempty"`
+ // Prevents critical service functionality like phone calls, bluetooth, etc.
+ Critical *bool `json:"critical,omitempty"`
// Specify whether to enable continuous fuzzing on devices. Defaults to true.
Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
// Specify whether to enable continuous fuzzing on host. Defaults to true.
Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
// Component in Google's bug tracking system that bugs should be filed to.
Componentid *int64 `json:"componentid,omitempty"`
- // Hotlists in Google's bug tracking system that bugs should be marked with.
+ // Hotlist(s) in Google's bug tracking system that bugs should be marked with.
Hotlists []string `json:"hotlists,omitempty"`
// Specify whether this fuzz target was submitted by a researcher. Defaults
// to false.
@@ -82,6 +333,19 @@
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"`
+ // List of modules for monitoring coverage drops in directories (e.g. "libicu")
+ Target_modules []string `json:"target_modules,omitempty"`
+ // Specifies a bug assignee to replace default ISE assignment
+ Assignee string `json:"assignee,omitempty"`
+}
+
+type FuzzFrameworks struct {
+ Afl *bool
+ Libfuzzer *bool
+ Jazzer *bool
}
type FuzzProperties struct {
@@ -93,6 +357,10 @@
Data []string `android:"path"`
// Optional dictionary to be installed to the fuzz target's output directory.
Dictionary *string `android:"path"`
+ // Define the fuzzing frameworks this fuzz target can be built for. If
+ // empty then the fuzz target will be available to be built for all fuzz
+ // frameworks available
+ Fuzzing_frameworks *FuzzFrameworks
// Config for running the target on fuzzing infrastructure.
Fuzz_config *FuzzConfig
}
@@ -107,6 +375,49 @@
DataIntermediateDir android.Path
}
+func GetFramework(ctx android.LoadHookContext, lang Lang) Framework {
+ framework := ctx.Config().Getenv("FUZZ_FRAMEWORK")
+
+ if lang == Cc {
+ switch strings.ToLower(framework) {
+ case "":
+ return LibFuzzer
+ case "libfuzzer":
+ return LibFuzzer
+ case "afl":
+ return AFL
+ }
+ } else if lang == Rust {
+ return LibFuzzer
+ } else if lang == Java {
+ return Jazzer
+ }
+
+ ctx.ModuleErrorf(fmt.Sprintf("%s is not a valid fuzzing framework for %s", framework, lang))
+ return UnknownFramework
+}
+
+func IsValidFrameworkForModule(targetFramework Framework, lang Lang, moduleFrameworks *FuzzFrameworks) bool {
+ if targetFramework == UnknownFramework {
+ return false
+ }
+
+ if moduleFrameworks == nil {
+ return true
+ }
+
+ switch targetFramework {
+ case LibFuzzer:
+ return proptools.BoolDefault(moduleFrameworks.Libfuzzer, true)
+ case AFL:
+ return proptools.BoolDefault(moduleFrameworks.Afl, true)
+ case Jazzer:
+ return proptools.BoolDefault(moduleFrameworks.Jazzer, true)
+ default:
+ panic("%s is not supported as a fuzz framework")
+ }
+}
+
func IsValid(fuzzModule FuzzModule) bool {
// Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
// fuzz targets we're going to package anyway.
@@ -154,7 +465,7 @@
}
// Additional fuzz config.
- if fuzzModule.Config != nil {
+ if fuzzModule.Config != nil && IsValidConfig(fuzzModule, module.Name()) {
files = append(files, FileToZip{fuzzModule.Config, ""})
}
@@ -205,7 +516,7 @@
return string(b)
}
-func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, lang Lang, pctx android.PackageContext) {
+func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, fuzzType Lang, pctx android.PackageContext) {
var archOsList []ArchOs
for archOs := range archDirs {
archOsList = append(archOsList, archOs)
@@ -218,12 +529,13 @@
hostOrTarget := archOs.HostOrTarget
builder := android.NewRuleBuilder(pctx, ctx)
zipFileName := "fuzz-" + hostOrTarget + "-" + arch + ".zip"
- if lang == Rust {
+ if fuzzType == Rust {
zipFileName = "fuzz-rust-" + hostOrTarget + "-" + arch + ".zip"
}
- if lang == Java {
+ if fuzzType == Java {
zipFileName = "fuzz-java-" + hostOrTarget + "-" + arch + ".zip"
}
+
outputFile := android.PathForOutput(ctx, zipFileName)
s.Packages = append(s.Packages, outputFile)
@@ -234,7 +546,6 @@
Flag("-L 0") // No need to try and re-compress the zipfiles.
for _, fileToZip := range filesToZip {
-
if fileToZip.DestinationPathPrefix != "" {
command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
} else {
@@ -253,45 +564,7 @@
for target, _ := range s.FuzzTargets {
fuzzTargets = append(fuzzTargets, target)
}
+
sort.Strings(fuzzTargets)
ctx.Strict(targets, strings.Join(fuzzTargets, " "))
}
-
-// CollectAllSharedDependencies performs a breadth-first search over the provided module's
-// dependencies using `visitDirectDeps` to enumerate all shared library
-// dependencies. We require breadth-first expansion, as otherwise we may
-// incorrectly use the core libraries (sanitizer runtimes, libc, libdl, etc.)
-// from a dependency. This may cause issues when dependencies have explicit
-// sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
-func CollectAllSharedDependencies(ctx android.SingletonContext, module android.Module, unstrippedOutputFile func(module android.Module) android.Path, isValidSharedDependency func(dependency android.Module) bool) android.Paths {
- var fringe []android.Module
-
- seen := make(map[string]bool)
-
- // Enumerate the first level of dependencies, as we discard all non-library
- // modules in the BFS loop below.
- ctx.VisitDirectDeps(module, func(dep android.Module) {
- if isValidSharedDependency(dep) {
- fringe = append(fringe, dep)
- }
- })
-
- var sharedLibraries android.Paths
-
- for i := 0; i < len(fringe); i++ {
- module := fringe[i]
- if seen[module.Name()] {
- continue
- }
- seen[module.Name()] = true
-
- sharedLibraries = append(sharedLibraries, unstrippedOutputFile(module))
- ctx.VisitDirectDeps(module, func(dep android.Module) {
- if isValidSharedDependency(dep) && !seen[dep.Name()] {
- fringe = append(fringe, dep)
- }
- })
- }
-
- return sharedLibraries
-}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index d542743..f5da50e 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -25,6 +25,8 @@
"strconv"
"strings"
+ "android/soong/bazel/cquery"
+
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools"
@@ -189,6 +191,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 +253,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.PathForBazelOutRelative(ctx, ctx.ModuleDir(), 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.
@@ -456,6 +469,7 @@
return "SOONG_ERROR", nil
}
+ // Apply shell escape to each cases to prevent source file paths containing $ from being evaluated in shell
switch name {
case "location":
if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
@@ -469,15 +483,15 @@
return reportError("default label %q has multiple files, use $(locations %s) to reference it",
firstLabel, firstLabel)
}
- return paths[0], nil
+ return proptools.ShellEscape(paths[0]), nil
case "in":
- return strings.Join(cmd.PathsForInputs(srcFiles), " "), nil
+ return strings.Join(proptools.ShellEscapeList(cmd.PathsForInputs(srcFiles)), " "), nil
case "out":
var sandboxOuts []string
for _, out := range task.out {
sandboxOuts = append(sandboxOuts, cmd.PathForOutput(out))
}
- return strings.Join(sandboxOuts, " "), nil
+ return strings.Join(proptools.ShellEscapeList(sandboxOuts), " "), nil
case "depfile":
referencedDepfile = true
if !Bool(g.properties.Depfile) {
@@ -485,7 +499,7 @@
}
return "__SBOX_DEPFILE__", nil
case "genDir":
- return cmd.PathForOutput(task.genDir), nil
+ return proptools.ShellEscape(cmd.PathForOutput(task.genDir)), nil
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -497,7 +511,7 @@
return reportError("label %q has multiple files, use $(locations %s) to reference it",
label, label)
}
- return paths[0], nil
+ return proptools.ShellEscape(paths[0]), nil
} else {
return reportError("unknown location label %q is not in srcs, out, tools or tool_files.", label)
}
@@ -508,7 +522,7 @@
if len(paths) == 0 {
return reportError("label %q has no files", label)
}
- return strings.Join(paths, " "), nil
+ return proptools.ShellEscape(strings.Join(paths, " ")), nil
} else {
return reportError("unknown locations label %q is not in srcs, out, tools or tool_files.", label)
}
@@ -575,31 +589,49 @@
}
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) {
+ // Allowlist genrule to use depfile until we have a solution to remove it.
+ // TODO(b/235582219): Remove allowlist for genrule
+ if ctx.ModuleType() == "gensrcs" &&
+ !ctx.DeviceConfig().BuildBrokenDepfile() &&
+ Bool(g.properties.Depfile) {
+ ctx.PropertyErrorf(
+ "depfile",
+ "Deprecated to ensure the module type is convertible to Bazel. "+
+ "Try specifying the dependencies explicitly so that there is no need to use depfile. "+
+ "If not possible, the escape hatch is to use BUILD_BROKEN_DEPFILE to bypass the error.")
}
- 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}
- }
+
+ 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}
}
}
+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.
func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...)
@@ -787,6 +819,7 @@
func GenSrcsFactory() android.Module {
m := NewGenSrcs()
android.InitAndroidModule(m)
+ android.InitBazelModule(m)
return m
}
@@ -798,6 +831,13 @@
Shard_size *int64
}
+type bazelGensrcsAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Output_extension *string
+ Tools bazel.LabelListAttribute
+ Cmd string
+}
+
const defaultShardSize = 50
func NewGenRule() *Module {
@@ -835,7 +875,7 @@
type genRuleProperties struct {
// names of the output files that will be generated
- Out []string `android:"arch_variant"`
+ Out []string
}
type bazelGenruleAttributes struct {
@@ -853,22 +893,39 @@
tools_prop.Append(tool_files_prop)
tools := bazel.MakeLabelListAttribute(tools_prop)
- srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Srcs))
+ srcs := bazel.LabelListAttribute{}
+ srcs_labels := bazel.LabelList{}
+ // Only cc_genrule is arch specific
+ if ctx.ModuleType() == "cc_genrule" {
+ for axis, configToProps := range m.GetArchVariantProperties(ctx, &generatorProperties{}) {
+ for config, props := range configToProps {
+ if props, ok := props.(*generatorProperties); ok {
+ labels := android.BazelLabelForModuleSrcExcludes(ctx, props.Srcs, props.Exclude_srcs)
+ srcs_labels.Append(labels)
+ srcs.SetSelectValue(axis, config, labels)
+ }
+ }
+ }
+ } else {
+ srcs_labels = android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+ srcs = bazel.MakeLabelListAttribute(srcs_labels)
+ }
var allReplacements bazel.LabelList
allReplacements.Append(tools.Value)
- allReplacements.Append(srcs.Value)
+ allReplacements.Append(bazel.FirstUniqueBazelLabelList(srcs_labels))
// 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)
- genDir := "$(GENDIR)"
- if t := ctx.ModuleType(); t == "cc_genrule" || t == "java_genrule" || t == "java_genrule_host" {
- genDir = "$(RULEDIR)"
+ 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)
}
- cmd = strings.Replace(cmd, "$(genDir)", genDir, -1)
+ cmd = strings.Replace(cmd, "$(genDir)", "$(RULEDIR)", -1)
if len(tools.Value.Includes) > 0 {
cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Value.Includes[0].Label), -1)
cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Value.Includes[0].Label), -1)
@@ -883,30 +940,58 @@
}
}
- // 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
+ tags := android.ApexAvailableTags(m)
+
+ 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(),
+ Tags: tags,
+ }, 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(),
+ Tags: tags,
+ }, 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 1b5cef2..63f8fa9 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -15,6 +15,7 @@
package genrule
import (
+ "fmt"
"os"
"regexp"
"testing"
@@ -421,7 +422,7 @@
allowMissingDependencies: true,
- expect: "cat ***missing srcs :missing*** > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "cat '***missing srcs :missing***' > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "tool allow missing dependencies",
@@ -433,7 +434,7 @@
allowMissingDependencies: true,
- expect: "***missing tool :missing*** > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "'***missing tool :missing***' > __SBOX_SANDBOX_DIR__/out/out",
},
}
@@ -626,6 +627,73 @@
}
}
+func TestGensrcsBuildBrokenDepfile(t *testing.T) {
+ tests := []struct {
+ name string
+ prop string
+ BuildBrokenDepfile *bool
+ err string
+ }{
+ {
+ name: `error when BuildBrokenDepfile is set to false`,
+ prop: `
+ depfile: true,
+ cmd: "cat $(in) > $(out) && cat $(depfile)",
+ `,
+ BuildBrokenDepfile: proptools.BoolPtr(false),
+ err: "depfile: Deprecated to ensure the module type is convertible to Bazel",
+ },
+ {
+ name: `error when BuildBrokenDepfile is not set`,
+ prop: `
+ depfile: true,
+ cmd: "cat $(in) > $(out) && cat $(depfile)",
+ `,
+ err: "depfile: Deprecated to ensure the module type is convertible to Bazel.",
+ },
+ {
+ name: `no error when BuildBrokenDepfile is explicitly set to true`,
+ prop: `
+ depfile: true,
+ cmd: "cat $(in) > $(out) && cat $(depfile)",
+ `,
+ BuildBrokenDepfile: proptools.BoolPtr(true),
+ },
+ {
+ name: `no error if depfile is not set`,
+ prop: `
+ cmd: "cat $(in) > $(out)",
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ bp := fmt.Sprintf(`
+ gensrcs {
+ name: "foo",
+ srcs: ["data.txt"],
+ %s
+ }`, test.prop)
+
+ var expectedErrors []string
+ if test.err != "" {
+ expectedErrors = append(expectedErrors, test.err)
+ }
+ android.GroupFixturePreparers(
+ prepareForGenRuleTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if test.BuildBrokenDepfile != nil {
+ variables.BuildBrokenDepfile = test.BuildBrokenDepfile
+ }
+ }),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
+ RunTestWithBp(t, bp)
+ })
+
+ }
+}
+
func TestGenruleDefaults(t *testing.T) {
bp := `
genrule_defaults {
@@ -722,6 +790,94 @@
result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
}
+func TestGenSrcsWithNonRootAndroidBpOutputFiles(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForGenRuleTest,
+ android.FixtureMergeMockFs(android.MockFS{
+ "external-protos/path/Android.bp": []byte(`
+ filegroup {
+ name: "external-protos",
+ srcs: ["baz/baz.proto", "bar.proto"],
+ }
+ `),
+ "package-dir/Android.bp": []byte(`
+ gensrcs {
+ name: "module-name",
+ cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
+ srcs: [
+ "src/foo.proto",
+ ":external-protos",
+ ],
+ output_extension: "proto.h",
+ }
+ `),
+ }),
+ ).RunTest(t)
+
+ exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
+ gen := result.Module("module-name", "").(*Module)
+
+ android.AssertPathsRelativeToTopEquals(
+ t,
+ "include path",
+ []string{exportedIncludeDir},
+ gen.exportedIncludeDirs,
+ )
+ android.AssertPathsRelativeToTopEquals(
+ t,
+ "files",
+ []string{
+ exportedIncludeDir + "/package-dir/src/foo.proto.h",
+ exportedIncludeDir + "/external-protos/path/baz/baz.proto.h",
+ exportedIncludeDir + "/external-protos/path/bar.proto.h",
+ },
+ gen.outputFiles,
+ )
+}
+
+func TestGenSrcsWithSrcsFromExternalPackage(t *testing.T) {
+ bp := `
+ gensrcs {
+ name: "module-name",
+ cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
+ srcs: [
+ ":external-protos",
+ ],
+ output_extension: "proto.h",
+ }
+ `
+ result := android.GroupFixturePreparers(
+ prepareForGenRuleTest,
+ android.FixtureMergeMockFs(android.MockFS{
+ "external-protos/path/Android.bp": []byte(`
+ filegroup {
+ name: "external-protos",
+ srcs: ["foo/foo.proto", "bar.proto"],
+ }
+ `),
+ }),
+ ).RunTestWithBp(t, bp)
+
+ exportedIncludeDir := "out/soong/.intermediates/module-name/gen/gensrcs"
+ gen := result.Module("module-name", "").(*Module)
+
+ android.AssertPathsRelativeToTopEquals(
+ t,
+ "include path",
+ []string{exportedIncludeDir},
+ gen.exportedIncludeDirs,
+ )
+ android.AssertPathsRelativeToTopEquals(
+ t,
+ "files",
+ []string{
+ exportedIncludeDir + "/external-protos/path/foo/foo.proto.h",
+ exportedIncludeDir + "/external-protos/path/bar.proto.h",
+ },
+ gen.outputFiles,
+ )
+}
+
func TestPrebuiltTool(t *testing.T) {
testcases := []struct {
name string
@@ -810,6 +966,92 @@
android.AssertDeepEquals(t, "output deps", expectedOutputFiles, gen.outputDeps.Strings())
}
+func TestGenruleWithGlobPaths(t *testing.T) {
+ testcases := []struct {
+ name string
+ bp string
+ additionalFiles android.MockFS
+ expectedCmd string
+ }{
+ {
+ name: "single file in directory with $ sign",
+ bp: `
+ genrule {
+ name: "gen",
+ srcs: ["inn*.txt"],
+ out: ["out.txt"],
+ cmd: "cp $(in) $(out)",
+ }
+ `,
+ additionalFiles: android.MockFS{"inn$1.txt": nil},
+ expectedCmd: "cp 'inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
+ },
+ {
+ name: "multiple file in directory with $ sign",
+ bp: `
+ genrule {
+ name: "gen",
+ srcs: ["inn*.txt"],
+ out: ["."],
+ cmd: "cp $(in) $(out)",
+ }
+ `,
+ additionalFiles: android.MockFS{"inn$1.txt": nil, "inn$2.txt": nil},
+ expectedCmd: "cp 'inn$1.txt' 'inn$2.txt' __SBOX_SANDBOX_DIR__/out",
+ },
+ {
+ name: "file in directory with other shell unsafe character",
+ bp: `
+ genrule {
+ name: "gen",
+ srcs: ["inn*.txt"],
+ out: ["out.txt"],
+ cmd: "cp $(in) $(out)",
+ }
+ `,
+ additionalFiles: android.MockFS{"inn@1.txt": nil},
+ expectedCmd: "cp 'inn@1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
+ },
+ {
+ name: "glob location param with filepath containing $",
+ bp: `
+ genrule {
+ name: "gen",
+ srcs: ["**/inn*"],
+ out: ["."],
+ cmd: "cp $(in) $(location **/inn*)",
+ }
+ `,
+ additionalFiles: android.MockFS{"a/inn$1.txt": nil},
+ expectedCmd: "cp 'a/inn$1.txt' 'a/inn$1.txt'",
+ },
+ {
+ name: "glob locations param with filepath containing $",
+ bp: `
+ genrule {
+ name: "gen",
+ tool_files: ["**/inn*"],
+ out: ["out.txt"],
+ cmd: "cp $(locations **/inn*) $(out)",
+ }
+ `,
+ additionalFiles: android.MockFS{"a/inn$1.txt": nil},
+ expectedCmd: "cp '__SBOX_SANDBOX_DIR__/tools/src/a/inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
+ },
+ }
+
+ for _, test := range testcases {
+ t.Run(test.name, func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForGenRuleTest,
+ android.FixtureMergeMockFs(test.additionalFiles),
+ ).RunTestWithBp(t, test.bp)
+ gen := result.Module("gen", "").(*Module)
+ android.AssertStringEquals(t, "command", test.expectedCmd, gen.rawCommands[0])
+ })
+ }
+}
+
type testTool struct {
android.ModuleBase
outputFile android.Path
diff --git a/go.mod b/go.mod
index 14444b3..a5d9dd5 100644
--- a/go.mod
+++ b/go.mod
@@ -1,19 +1,9 @@
module android/soong
-require google.golang.org/protobuf v0.0.0
+go 1.19
-require github.com/google/blueprint v0.0.0
-
-replace google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
-
-replace github.com/google/blueprint v0.0.0 => ../blueprint
-
-// Indirect deps from golang-protobuf
-exclude github.com/golang/protobuf v1.5.0
-
-replace github.com/google/go-cmp v0.5.5 => ../../external/go-cmp
-
-// Indirect dep from go-cmp
-exclude golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
-
-go 1.15
+require (
+ github.com/google/blueprint v0.0.0
+ google.golang.org/protobuf v0.0.0
+ prebuilts/bazel/common/proto/analysis_v2 v0.0.0
+)
diff --git a/go.work b/go.work
new file mode 100644
index 0000000..737a9df
--- /dev/null
+++ b/go.work
@@ -0,0 +1,19 @@
+go 1.19
+
+use (
+ .
+ ../../external/go-cmp
+ ../../external/golang-protobuf
+ ../../prebuilts/bazel/common/proto/analysis_v2
+ ../../prebuilts/bazel/common/proto/build
+ ../blueprint
+)
+
+replace (
+ github.com/golang/protobuf v0.0.0 => ../../external/golang-protobuf
+ github.com/google/blueprint v0.0.0 => ../blueprint
+ github.com/google/go-cmp v0.0.0 => ../../external/go-cmp
+ google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
+ prebuilts/bazel/common/proto/analysis_v2 v0.0.0 => ../../prebuilts/bazel/common/proto/analysis_v2
+ prebuilts/bazel/common/proto/build v0.0.0 => ../../prebuilts/bazel/common/proto/build
+)
diff --git a/java/Android.bp b/java/Android.bp
index d66e095..4af2a14 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -65,6 +65,7 @@
"plugin.go",
"prebuilt_apis.go",
"proto.go",
+ "resourceshrinker.go",
"robolectric.go",
"rro.go",
"sdk.go",
@@ -77,6 +78,7 @@
"tradefed.go",
],
testSrcs: [
+ "aar_test.go",
"androidmk_test.go",
"app_import_test.go",
"app_set_test.go",
@@ -85,10 +87,11 @@
"device_host_converter_test.go",
"dex_test.go",
"dexpreopt_test.go",
- "dexpreopt_bootjars_test.go",
"dexpreopt_config_test.go",
"droiddoc_test.go",
"droidstubs_test.go",
+ "fuzz_test.go",
+ "genrule_test.go",
"hiddenapi_singleton_test.go",
"jacoco_test.go",
"java_test.go",
@@ -99,6 +102,8 @@
"platform_compat_config_test.go",
"plugin_test.go",
"prebuilt_apis_test.go",
+ "proto_test.go",
+ "resourceshrinker_test.go",
"rro_test.go",
"sdk_test.go",
"sdk_library_test.go",
diff --git a/java/OWNERS b/java/OWNERS
deleted file mode 100644
index 5b71b1e..0000000
--- a/java/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,skvadrik@google.com
-
-# For metalava team to disable lint checks in platform
-per-file droidstubs.go = aurimas@google.com,emberrose@google.com,sjgilbert@google.com
\ No newline at end of file
diff --git a/java/aapt2.go b/java/aapt2.go
index 5346ddf..7845a0b 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -256,17 +256,21 @@
var aapt2ConvertRule = pctx.AndroidStaticRule("aapt2Convert",
blueprint.RuleParams{
- Command: `${config.Aapt2Cmd} convert --output-format proto $in -o $out`,
+ Command: `${config.Aapt2Cmd} convert --output-format $format $in -o $out`,
CommandDeps: []string{"${config.Aapt2Cmd}"},
- })
+ }, "format",
+)
// Converts xml files and resource tables (resources.arsc) in the given jar/apk file to a proto
// format. The proto definition is available at frameworks/base/tools/aapt2/Resources.proto.
-func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path) {
+func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path, format string) {
ctx.Build(pctx, android.BuildParams{
Rule: aapt2ConvertRule,
Input: in,
Output: out,
- Description: "convert to proto",
+ Description: "convert to " + format,
+ Args: map[string]string{
+ "format": format,
+ },
})
}
diff --git a/java/aar.go b/java/aar.go
index 2ddd188..f1b137d 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -21,6 +21,7 @@
"strings"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/dexpreopt"
"github.com/google/blueprint"
@@ -28,8 +29,8 @@
)
type AndroidLibraryDependency interface {
+ LibraryDependency
ExportPackage() android.Path
- ExportedProguardFlagFiles() android.Paths
ExportedRRODirs() []rroDir
ExportedStaticPackages() android.Paths
ExportedManifests() android.Paths
@@ -105,6 +106,7 @@
noticeFile android.OptionalPath
assetPackage android.OptionalPath
isLibrary bool
+ defaultManifestVersion string
useEmbeddedNativeLibs bool
useEmbeddedDex bool
usesNonSdkApis bool
@@ -217,13 +219,32 @@
linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
linkDeps = append(linkDeps, assetDeps...)
- // SDK version flags
- minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
- if err != nil {
- ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ // Returns the effective version for {min|target}_sdk_version
+ effectiveVersionString := func(sdkVersion android.SdkSpec, minSdkVersion android.ApiLevel) string {
+ // If {min|target}_sdk_version is current, use sdk_version to determine the effective level
+ // This is necessary for vendor modules.
+ // The effective version does not _only_ depend on {min|target}_sdk_version(level),
+ // but also on the sdk_version (kind+level)
+ if minSdkVersion.IsCurrent() {
+ ret, err := sdkVersion.EffectiveVersionString(ctx)
+ if err != nil {
+ ctx.ModuleErrorf("invalid sdk_version: %s", err)
+ }
+ return ret
+ }
+ ret, err := minSdkVersion.EffectiveVersionString(ctx)
+ if err != nil {
+ ctx.ModuleErrorf("invalid min_sdk_version: %s", err)
+ }
+ return ret
}
+ // SDK version flags
+ sdkVersion := sdkContext.SdkVersion(ctx)
+ minSdkVersion := effectiveVersionString(sdkVersion, sdkContext.MinSdkVersion(ctx))
linkFlags = append(linkFlags, "--min-sdk-version "+minSdkVersion)
+ // Use minSdkVersion for target-sdk-version, even if `target_sdk_version` is set
+ // This behavior has been copied from Make.
linkFlags = append(linkFlags, "--target-sdk-version "+minSdkVersion)
// Version code
@@ -284,6 +305,7 @@
SdkContext: sdkContext,
ClassLoaderContexts: classLoaderContexts,
IsLibrary: a.isLibrary,
+ DefaultManifestVersion: a.defaultManifestVersion,
UseEmbeddedNativeLibs: a.useEmbeddedNativeLibs,
UsesNonSdkApis: a.usesNonSdkApis,
UseEmbeddedDex: a.useEmbeddedDex,
@@ -437,7 +459,7 @@
switch depTag {
case instrumentationForTag:
// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
- case libTag:
+ case sdkLibTag, libTag:
if exportPackage != nil {
sharedLibs = append(sharedLibs, exportPackage)
}
@@ -489,13 +511,13 @@
type AndroidLibrary struct {
Library
aapt
+ android.BazelModuleBase
androidLibraryProperties androidLibraryProperties
aarFile android.WritablePath
- exportedProguardFlagFiles android.Paths
- exportedStaticPackages android.Paths
+ exportedStaticPackages android.Paths
}
var _ android.OutputFileProducer = (*AndroidLibrary)(nil)
@@ -510,10 +532,6 @@
}
}
-func (a *AndroidLibrary) ExportedProguardFlagFiles() android.Paths {
- return a.exportedProguardFlagFiles
-}
-
func (a *AndroidLibrary) ExportedStaticPackages() android.Paths {
return a.exportedStaticPackages
}
@@ -526,7 +544,7 @@
if sdkDep.hasFrameworkLibs() {
a.aapt.deps(ctx, sdkDep)
}
- a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
+ a.usesLibrary.deps(ctx, false)
}
func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -562,21 +580,36 @@
a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles,
android.PathsForModuleSrc(ctx, a.dexProperties.Optimize.Proguard_flags_files)...)
ctx.VisitDirectDeps(func(m android.Module) {
- if lib, ok := m.(AndroidLibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag {
- a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...)
- a.exportedStaticPackages = append(a.exportedStaticPackages, lib.ExportPackage())
- a.exportedStaticPackages = append(a.exportedStaticPackages, lib.ExportedStaticPackages()...)
+ if ctx.OtherModuleDependencyTag(m) == staticLibTag {
+ if lib, ok := m.(LibraryDependency); ok {
+ a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...)
+ }
+ if alib, ok := m.(AndroidLibraryDependency); ok {
+ a.exportedStaticPackages = append(a.exportedStaticPackages, alib.ExportPackage())
+ a.exportedStaticPackages = append(a.exportedStaticPackages, alib.ExportedStaticPackages()...)
+ }
}
})
-
a.exportedProguardFlagFiles = android.FirstUniquePaths(a.exportedProguardFlagFiles)
a.exportedStaticPackages = android.FirstUniquePaths(a.exportedStaticPackages)
+
+ prebuiltJniPackages := android.Paths{}
+ ctx.VisitDirectDeps(func(module android.Module) {
+ if info, ok := ctx.OtherModuleProvider(module, JniPackageProvider).(JniPackageInfo); ok {
+ prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...)
+ }
+ })
+ if len(prebuiltJniPackages) > 0 {
+ ctx.SetProvider(JniPackageProvider, JniPackageInfo{
+ JniPackages: prebuiltJniPackages,
+ })
+ }
}
// android_library builds and links sources into a `.jar` file for the device along with Android resources.
//
// An android_library has a single variant that produces a `.jar` file containing `.class` files that were
-// compiled against the device bootclasspath, along with a `package-res.apk` file containing Android resources compiled
+// compiled against the device bootclasspath, along with a `package-res.apk` file containing Android resources compiled
// with aapt2. This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of
// an android_app module.
func AndroidLibraryFactory() android.Module {
@@ -592,6 +625,7 @@
android.InitApexModule(module)
InitJavaModule(module, android.DeviceSupported)
+ android.InitBazelModule(module)
return module
}
@@ -620,17 +654,24 @@
Libs []string
// If set to true, run Jetifier against .aar file. Defaults to false.
Jetifier *bool
+ // If true, extract JNI libs from AAR archive. These libs will be accessible to android_app modules and
+ // will be passed transitively through android_libraries to an android_app.
+ //TODO(b/241138093) evaluate whether we can have this flag default to true for Bazel conversion
+ Extract_jni *bool
}
type AARImport struct {
android.ModuleBase
android.DefaultableModuleBase
android.ApexModuleBase
+ android.BazelModuleBase
prebuilt android.Prebuilt
// Functionality common to Module and Import.
embeddableInModuleAndImport
+ providesTransitiveHeaderJars
+
properties AARImportProperties
classpathFile android.WritablePath
@@ -644,10 +685,11 @@
hideApexVariantFromMake bool
- aarPath android.Path
+ aarPath android.Path
+ jniPackages android.Paths
sdkVersion android.SdkSpec
- minSdkVersion android.SdkSpec
+ minSdkVersion android.ApiLevel
}
var _ android.OutputFileProducer = (*AARImport)(nil)
@@ -672,15 +714,19 @@
return ""
}
-func (a *AARImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (a *AARImport) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
if a.properties.Min_sdk_version != nil {
- return android.SdkSpecFrom(ctx, *a.properties.Min_sdk_version)
+ return android.ApiLevelFrom(ctx, *a.properties.Min_sdk_version)
}
- return a.SdkVersion(ctx)
+ return a.SdkVersion(ctx).ApiLevel
}
-func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
- return a.SdkVersion(ctx)
+func (a *AARImport) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
+ return android.SdkSpecFrom(ctx, "").ApiLevel
+}
+
+func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ return a.SdkVersion(ctx).ApiLevel
}
func (a *AARImport) javaVersion() string {
@@ -748,6 +794,28 @@
ctx.AddVariationDependencies(nil, staticLibTag, a.properties.Static_libs...)
}
+type JniPackageInfo struct {
+ // List of zip files containing JNI libraries
+ // Zip files should have directory structure jni/<arch>/*.so
+ JniPackages android.Paths
+}
+
+var JniPackageProvider = blueprint.NewProvider(JniPackageInfo{})
+
+// Unzip an AAR and extract the JNI libs for $archString.
+var extractJNI = pctx.AndroidStaticRule("extractJNI",
+ blueprint.RuleParams{
+ Command: `rm -rf $out $outDir && touch $out && ` +
+ `unzip -qoDD -d $outDir $in "jni/${archString}/*" && ` +
+ `jni_files=$$(find $outDir/jni -type f) && ` +
+ // print error message if there are no JNI libs for this arch
+ `[ -n "$$jni_files" ] || (echo "ERROR: no JNI libs found for arch ${archString}" && exit 1) && ` +
+ `${config.SoongZipCmd} -o $out -P 'lib/${archString}' ` +
+ `-C $outDir/jni/${archString} $$(echo $$jni_files | xargs -n1 printf " -f %s")`,
+ CommandDeps: []string{"${config.SoongZipCmd}"},
+ },
+ "outDir", "archString")
+
// Unzip an AAR into its constituent files and directories. Any files in Outputs that don't exist in the AAR will be
// touched to create an empty file. The res directory is not extracted, as it will be extracted in its own rule.
var unzipAAR = pctx.AndroidStaticRule("unzipAAR",
@@ -850,11 +918,39 @@
a.assetsPackage = mergedAssets
}
+ a.collectTransitiveHeaderJars(ctx)
ctx.SetProvider(JavaInfoProvider, JavaInfo{
HeaderJars: android.PathsIfNonNil(a.classpathFile),
+ TransitiveLibsHeaderJars: a.transitiveLibsHeaderJars,
+ TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars,
ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
ImplementationJars: android.PathsIfNonNil(a.classpathFile),
})
+
+ if proptools.Bool(a.properties.Extract_jni) {
+ for _, t := range ctx.MultiTargets() {
+ arch := t.Arch.Abi[0]
+ path := android.PathForModuleOut(ctx, arch+"_jni.zip")
+ a.jniPackages = append(a.jniPackages, path)
+
+ outDir := android.PathForModuleOut(ctx, "aarForJni")
+ aarPath := android.PathForModuleSrc(ctx, a.properties.Aars[0])
+ ctx.Build(pctx, android.BuildParams{
+ Rule: extractJNI,
+ Input: aarPath,
+ Outputs: android.WritablePaths{path},
+ Description: "extract JNI from AAR",
+ Args: map[string]string{
+ "outDir": outDir.String(),
+ "archString": arch,
+ },
+ })
+ }
+
+ ctx.SetProvider(JniPackageProvider, JniPackageInfo{
+ JniPackages: a.jniPackages,
+ })
+ }
}
func (a *AARImport) HeaderJars() android.Paths {
@@ -890,7 +986,7 @@
return nil
}
-var _ android.PrebuiltInterface = (*Import)(nil)
+var _ android.PrebuiltInterface = (*AARImport)(nil)
// android_library_import imports an `.aar` file into the build graph as if it was built with android_library.
//
@@ -903,6 +999,136 @@
android.InitPrebuiltModule(module, &module.properties.Aars)
android.InitApexModule(module)
- InitJavaModule(module, android.DeviceSupported)
+ InitJavaModuleMultiTargets(module, android.DeviceSupported)
+ android.InitBazelModule(module)
return module
}
+
+type bazelAapt struct {
+ Manifest bazel.Label
+ Resource_files bazel.LabelListAttribute
+}
+
+type bazelAndroidLibrary struct {
+ *javaLibraryAttributes
+ *bazelAapt
+}
+
+type bazelAndroidLibraryImport struct {
+ Aar bazel.Label
+ Deps bazel.LabelListAttribute
+ Exports bazel.LabelListAttribute
+ Sdk_version bazel.StringAttribute
+}
+
+func (a *aapt) convertAaptAttrsWithBp2Build(ctx android.TopDownMutatorContext) *bazelAapt {
+ manifest := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
+
+ resourceFiles := bazel.LabelList{
+ Includes: []bazel.Label{},
+ }
+ for _, dir := range android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res") {
+ files := android.RootToModuleRelativePaths(ctx, androidResourceGlob(ctx, dir))
+ resourceFiles.Includes = append(resourceFiles.Includes, files...)
+ }
+ return &bazelAapt{
+ android.BazelLabelForModuleSrcSingle(ctx, manifest),
+ bazel.MakeLabelListAttribute(resourceFiles),
+ }
+}
+
+func (a *AARImport) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ aars := android.BazelLabelForModuleSrcExcludes(ctx, a.properties.Aars, []string{})
+ exportableStaticLibs := []string{}
+ // TODO(b/240716882): investigate and handle static_libs deps that are not imports. They are not supported for export by Bazel.
+ for _, depName := range a.properties.Static_libs {
+ if dep, ok := ctx.ModuleFromName(depName); ok {
+ switch dep.(type) {
+ case *AARImport, *Import:
+ exportableStaticLibs = append(exportableStaticLibs, depName)
+ }
+ }
+ }
+ name := android.RemoveOptionalPrebuiltPrefix(a.Name())
+ deps := android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(append(a.properties.Static_libs, a.properties.Libs...))))
+ exports := android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(exportableStaticLibs))
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "aar_import",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+ },
+ android.CommonAttributes{Name: name},
+ &bazelAndroidLibraryImport{
+ Aar: aars.Includes[0],
+ Deps: bazel.MakeLabelListAttribute(deps),
+ Exports: bazel.MakeLabelListAttribute(exports),
+ Sdk_version: bazel.StringAttribute{Value: a.properties.Sdk_version},
+ },
+ )
+
+ neverlink := true
+ ctx.CreateBazelTargetModule(
+ AndroidLibraryBazelTargetModuleProperties(),
+ android.CommonAttributes{Name: name + "-neverlink"},
+ &bazelAndroidLibrary{
+ javaLibraryAttributes: &javaLibraryAttributes{
+ Neverlink: bazel.BoolAttribute{Value: &neverlink},
+ Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+ javaCommonAttributes: &javaCommonAttributes{
+ Sdk_version: bazel.StringAttribute{Value: a.properties.Sdk_version},
+ },
+ },
+ },
+ )
+
+}
+func AndroidLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
+ return bazel.BazelTargetModuleProperties{
+ Rule_class: "android_library",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+ }
+}
+
+func (a *AndroidLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ commonAttrs, bp2buildInfo := a.convertLibraryAttrsBp2Build(ctx)
+ depLabels := bp2buildInfo.DepLabels
+
+ 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 !depLabels.Deps.IsEmpty() {
+ ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
+ }
+ name := a.Name()
+ props := AndroidLibraryBazelTargetModuleProperties()
+
+ ctx.CreateBazelTargetModule(
+ props,
+ android.CommonAttributes{Name: name},
+ &bazelAndroidLibrary{
+ &javaLibraryAttributes{
+ javaCommonAttributes: commonAttrs,
+ Deps: deps,
+ Exports: depLabels.StaticDeps,
+ },
+ a.convertAaptAttrsWithBp2Build(ctx),
+ },
+ )
+
+ neverlink := true
+ ctx.CreateBazelTargetModule(
+ props,
+ android.CommonAttributes{Name: name + "-neverlink"},
+ &bazelAndroidLibrary{
+ javaLibraryAttributes: &javaLibraryAttributes{
+ Neverlink: bazel.BoolAttribute{Value: &neverlink},
+ Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+ javaCommonAttributes: &javaCommonAttributes{
+ Sdk_version: bazel.StringAttribute{Value: a.deviceProperties.Sdk_version},
+ Java_version: bazel.StringAttribute{Value: a.properties.Java_version},
+ },
+ },
+ },
+ )
+}
diff --git a/java/aar_test.go b/java/aar_test.go
new file mode 100644
index 0000000..8afa039
--- /dev/null
+++ b/java/aar_test.go
@@ -0,0 +1,83 @@
+// 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 (
+ "android/soong/android"
+ "testing"
+)
+
+func TestAarImportProducesJniPackages(t *testing.T) {
+ ctx := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ ).RunTestWithBp(t, `
+ android_library_import {
+ name: "aar-no-jni",
+ aars: ["aary.aar"],
+ }
+ android_library_import {
+ name: "aar-jni",
+ aars: ["aary.aar"],
+ extract_jni: true,
+ }`)
+
+ testCases := []struct {
+ name string
+ hasPackage bool
+ }{
+ {
+ name: "aar-no-jni",
+ hasPackage: false,
+ },
+ {
+ name: "aar-jni",
+ hasPackage: true,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ appMod := ctx.Module(tc.name, "android_common")
+ appTestMod := ctx.ModuleForTests(tc.name, "android_common")
+
+ info, ok := ctx.ModuleProvider(appMod, JniPackageProvider).(JniPackageInfo)
+ if !ok {
+ t.Errorf("expected android_library_import to have JniPackageProvider")
+ }
+
+ if !tc.hasPackage {
+ if len(info.JniPackages) != 0 {
+ t.Errorf("expected JniPackages to be empty, but got %v", info.JniPackages)
+ }
+ outputFile := "arm64-v8a_jni.zip"
+ jniOutputLibZip := appTestMod.MaybeOutput(outputFile)
+ if jniOutputLibZip.Rule != nil {
+ t.Errorf("did not expect an output file, but found %v", outputFile)
+ }
+ return
+ }
+
+ if len(info.JniPackages) != 1 {
+ t.Errorf("expected a single JniPackage, but got %v", info.JniPackages)
+ }
+
+ outputFile := info.JniPackages[0].String()
+ jniOutputLibZip := appTestMod.Output(outputFile)
+ if jniOutputLibZip.Rule == nil {
+ t.Errorf("did not find output file %v", outputFile)
+ }
+ })
+ }
+}
diff --git a/java/android_manifest.go b/java/android_manifest.go
index cde7d10..f2ebfa6 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -44,14 +44,14 @@
// When TARGET_BUILD_APPS is not empty, this method returns 10000 for modules targeting an unreleased SDK
// This enables release builds (that run with TARGET_BUILD_APPS=[val...]) to target APIs that have not yet been finalized as part of an SDK
func targetSdkVersionForManifestFixer(ctx android.ModuleContext, params ManifestFixerParams) string {
- targetSdkVersionSpec := params.SdkContext.TargetSdkVersion(ctx)
+ targetSdkVersionLevel := params.SdkContext.TargetSdkVersion(ctx)
// Check if we want to return 10000
// TODO(b/240294501): Determine the rules for handling test apexes
- if shouldReturnFinalOrFutureInt(ctx, targetSdkVersionSpec, params.EnforceDefaultTargetSdkVersion) {
+ if shouldReturnFinalOrFutureInt(ctx, targetSdkVersionLevel, params.EnforceDefaultTargetSdkVersion) {
return strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt())
}
- targetSdkVersion, err := targetSdkVersionSpec.EffectiveVersionString(ctx)
+ targetSdkVersion, err := targetSdkVersionLevel.EffectiveVersionString(ctx)
if err != nil {
ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
}
@@ -62,11 +62,13 @@
// 1. The module is built in unbundled mode (TARGET_BUILD_APPS not empty)
// 2. The module is run as part of MTS, and should be testable on stable branches
// Do not return 10000 if we are enforcing default targetSdkVersion and sdk has been finalised
-func shouldReturnFinalOrFutureInt(ctx android.ModuleContext, targetSdkVersionSpec android.SdkSpec, enforceDefaultTargetSdkVersion bool) bool {
- if enforceDefaultTargetSdkVersion && ctx.Config().PlatformSdkFinal() {
+func shouldReturnFinalOrFutureInt(ctx android.ModuleContext, targetSdkVersionLevel android.ApiLevel, enforceDefaultTargetSdkVersion bool) bool {
+ // If this is a REL branch, do not return 10000
+ if ctx.Config().PlatformSdkFinal() {
return false
}
- return targetSdkVersionSpec.ApiLevel.IsPreview() && (ctx.Config().UnbundledBuildApps() || includedInMts(ctx.Module()))
+ // If this a module targeting an unreleased SDK (MTS or unbundled builds), return 10000
+ return targetSdkVersionLevel.IsPreview() && (ctx.Config().UnbundledBuildApps() || includedInMts(ctx.Module()))
}
// Helper function that casts android.Module to java.androidTestApp
@@ -107,8 +109,8 @@
if minSdkVersion.FinalOrFutureInt() >= 23 {
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=%d doesn't support it",
- minSdkVersion)
+ ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%s doesn't support it",
+ minSdkVersion.String())
}
}
@@ -121,9 +123,9 @@
}
if params.ClassLoaderContexts != nil {
- // manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added
- // explicitly via `uses_libs`/`optional_uses_libs`.
- requiredUsesLibs, optionalUsesLibs := params.ClassLoaderContexts.ImplicitUsesLibs()
+ // 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)
@@ -149,18 +151,24 @@
if params.SdkContext != nil {
targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params)
- args = append(args, "--targetSdkVersion ", targetSdkVersion)
if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
deps = append(deps, ApiFingerprintPath(ctx))
}
+ args = append(args, "--targetSdkVersion ", targetSdkVersion)
+
minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
+ replaceMaxSdkVersionPlaceholder, err := params.SdkContext.ReplaceMaxSdkVersionPlaceholder(ctx).EffectiveVersion(ctx)
+ if err != nil {
+ ctx.ModuleErrorf("invalid ReplaceMaxSdkVersionPlaceholder: %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))
@@ -170,8 +178,12 @@
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
args = append(args, "--minSdkVersion ", minSdkVersion)
+ args = append(args, "--replaceMaxSdkVersionPlaceholder ", strconv.Itoa(replaceMaxSdkVersionPlaceholder.FinalOrFutureInt()))
args = append(args, "--raise-min-sdk-version")
}
+ if params.DefaultManifestVersion != "" {
+ args = append(args, "--override-placeholder-version", params.DefaultManifestVersion)
+ }
fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
argsMapper["args"] = strings.Join(args, " ")
diff --git a/java/androidmk.go b/java/androidmk.go
index a9a21b6..9c21633 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,6 +17,7 @@
import (
"fmt"
"io"
+ "strings"
"android/soong/android"
)
@@ -132,6 +133,23 @@
return entriesList
}
+func (j *JavaFuzzTest) 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(android.Paths{j.implementationJarFile}, entries)
+ androidMkWriteTestData(j.jniFilePaths, entries)
+ if j.fuzzPackagedModule.Corpus != nil {
+ androidMkWriteTestData(j.fuzzPackagedModule.Corpus, entries)
+ }
+ if j.fuzzPackagedModule.Dictionary != nil {
+ androidMkWriteTestData(android.Paths{j.fuzzPackagedModule.Dictionary}, 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")
@@ -157,9 +175,8 @@
entries.SetString("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", "true")
}
entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", j.testProperties.Test_mainline_modules...)
- if Bool(j.testProperties.Test_options.Unit_test) {
- entries.SetBool("LOCAL_IS_UNIT_TEST", true)
- }
+
+ j.testProperties.Test_options.CommonTestOptions.SetAndroidMkEntries(entries)
})
return entriesList
@@ -189,11 +206,6 @@
dexpreoptEntries := prebuilt.dexpreopter.AndroidMkEntriesForApex()
return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
}
- if !prebuilt.ContainingSdk().Unversioned() {
- return []android.AndroidMkEntries{android.AndroidMkEntries{
- Disabled: true,
- }}
- }
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "JAVA_LIBRARIES",
OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile),
@@ -389,6 +401,19 @@
} else {
for _, jniLib := range app.jniLibs {
entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
+ var partitionTag string
+
+ // Mimic the creation of partition_tag in build/make,
+ // which defaults to an empty string when the partition is system.
+ // Otherwise, capitalize with a leading _
+ if jniLib.partition == "system" {
+ partitionTag = ""
+ } else {
+ split := strings.Split(jniLib.partition, "/")
+ partitionTag = "_" + strings.ToUpper(split[len(split)-1])
+ }
+ entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(),
+ jniLib.name+":"+partitionTag)
}
}
@@ -540,6 +565,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,
@@ -711,3 +739,22 @@
},
}
}
+
+func (al *ApiLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+ var entriesList []android.AndroidMkEntries
+
+ entriesList = append(entriesList, android.AndroidMkEntries{
+ Class: "JAVA_LIBRARIES",
+ OutputFile: android.OptionalPathForPath(al.stubsJar),
+ Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
+ entries.SetPath("LOCAL_SOONG_CLASSES_JAR", al.stubsJar)
+ entries.SetPath("LOCAL_SOONG_HEADER_JAR", al.stubsJar)
+ },
+ },
+ })
+
+ return entriesList
+}
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 197da4f..1232cd1 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -19,6 +19,9 @@
"testing"
"android/soong/android"
+ "android/soong/cc"
+
+ "github.com/google/blueprint/proptools"
)
func TestRequired(t *testing.T) {
@@ -252,3 +255,149 @@
android.AssertDeepEquals(t, "overrides property", expected.overrides, actual)
}
}
+
+func TestJniPartition(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libjni_system",
+ system_shared_libs: [],
+ sdk_version: "current",
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libjni_system_ext",
+ system_shared_libs: [],
+ sdk_version: "current",
+ stl: "none",
+ system_ext_specific: true,
+ }
+
+ cc_library {
+ name: "libjni_odm",
+ system_shared_libs: [],
+ sdk_version: "current",
+ stl: "none",
+ device_specific: true,
+ }
+
+ cc_library {
+ name: "libjni_product",
+ system_shared_libs: [],
+ sdk_version: "current",
+ stl: "none",
+ product_specific: true,
+ }
+
+ cc_library {
+ name: "libjni_vendor",
+ system_shared_libs: [],
+ sdk_version: "current",
+ stl: "none",
+ soc_specific: true,
+ }
+
+ android_app {
+ name: "test_app_system_jni_system",
+ privileged: true,
+ platform_apis: true,
+ certificate: "platform",
+ jni_libs: ["libjni_system"],
+ }
+
+ android_app {
+ name: "test_app_system_jni_system_ext",
+ privileged: true,
+ platform_apis: true,
+ certificate: "platform",
+ jni_libs: ["libjni_system_ext"],
+ }
+
+ android_app {
+ name: "test_app_system_ext_jni_system",
+ privileged: true,
+ platform_apis: true,
+ certificate: "platform",
+ jni_libs: ["libjni_system"],
+ system_ext_specific: true
+ }
+
+ android_app {
+ name: "test_app_system_ext_jni_system_ext",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni_system_ext"],
+ system_ext_specific: true
+ }
+
+ android_app {
+ name: "test_app_product_jni_product",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni_product"],
+ product_specific: true
+ }
+
+ android_app {
+ name: "test_app_vendor_jni_odm",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni_odm"],
+ soc_specific: true
+ }
+
+ android_app {
+ name: "test_app_odm_jni_vendor",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni_vendor"],
+ device_specific: true
+ }
+ android_app {
+ name: "test_app_system_jni_multiple",
+ privileged: true,
+ platform_apis: true,
+ certificate: "platform",
+ jni_libs: ["libjni_system", "libjni_system_ext"],
+ }
+ android_app {
+ name: "test_app_vendor_jni_multiple",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni_odm", "libjni_vendor"],
+ soc_specific: true
+ }
+ `
+ arch := "arm64"
+ ctx := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ cc.PrepareForTestWithCcDefaultModules,
+ android.PrepareForTestWithAndroidMk,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.TestProductVariables.DeviceArch = proptools.StringPtr(arch)
+ }),
+ ).
+ RunTestWithBp(t, bp)
+ testCases := []struct {
+ name string
+ partitionNames []string
+ partitionTags []string
+ }{
+ {"test_app_system_jni_system", []string{"libjni_system"}, []string{""}},
+ {"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
+ {"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}},
+ {"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
+ {"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}},
+ {"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}},
+ {"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}},
+ {"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}},
+ {"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ mod := ctx.ModuleForTests(test.name, "android_common").Module()
+ entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
+ for i := range test.partitionNames {
+ actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i]
+ expected := test.partitionNames[i] + ":" + test.partitionTags[i]
+ android.AssertStringEquals(t, "Expected and actual differ", expected, actual)
+ }
+ })
+ }
+}
diff --git a/java/app.go b/java/app.go
index c3a33ff..706f99a 100755
--- a/java/app.go
+++ b/java/app.go
@@ -440,6 +440,9 @@
a.aapt.splitNames = a.appProperties.Package_splits
a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent)
+ if a.Updatable() {
+ a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion
+ }
a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts,
a.usesLibraryProperties.Exclude_uses_libs, a.enforceDefaultTargetSdkVersion(), aaptLinkFlags...)
@@ -450,7 +453,7 @@
func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) {
var staticLibProguardFlagFiles android.Paths
ctx.VisitDirectDeps(func(m android.Module) {
- if lib, ok := m.(AndroidLibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag {
+ if lib, ok := m.(LibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag {
staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, lib.ExportedProguardFlagFiles()...)
}
})
@@ -495,14 +498,14 @@
return a.dexJarFile.PathOrNil()
}
-func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
+func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, prebuiltJniPackages android.Paths, ctx android.ModuleContext) android.WritablePath {
var jniJarFile android.WritablePath
- if len(jniLibs) > 0 {
+ if len(jniLibs) > 0 || len(prebuiltJniPackages) > 0 {
a.jniLibs = jniLibs
if a.shouldEmbedJnis(ctx) {
jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
a.installPathForJNISymbols = a.installPath(ctx)
- TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx))
+ TransformJniLibsToJar(ctx, jniJarFile, jniLibs, prebuiltJniPackages, a.useEmbeddedNativeLibs(ctx))
for _, jni := range jniLibs {
if jni.coverageFile.Valid() {
// Only collect coverage for the first target arch if this is a multilib target.
@@ -540,7 +543,8 @@
// 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 {
+func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate,
+ ctx android.ModuleContext) (mainCertificate Certificate, allCertificates []Certificate) {
if android.SrcIsModule(certPropValue) == "" {
var mainCert Certificate
if certPropValue != "" {
@@ -559,8 +563,23 @@
certificates = append([]Certificate{mainCert}, certificates...)
}
+ if len(certificates) > 0 {
+ mainCertificate = certificates[0]
+ } else {
+ // 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 !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.
+ mainCertificate = Certificate{
+ Key: android.PathForModuleOut(ctx, "missing.pk8"),
+ Pem: android.PathForModuleOut(ctx, "missing.x509.pem"),
+ }
+ }
+
if !m.Platform() {
- certPath := certificates[0].Pem.String()
+ certPath := mainCertificate.Pem.String()
systemCertPath := ctx.Config().DefaultAppCertificateDir(ctx).String()
if strings.HasPrefix(certPath, systemCertPath) {
enforceSystemCert := ctx.Config().EnforceSystemCertificate()
@@ -572,7 +591,8 @@
}
}
- return certificates
+
+ return mainCertificate, certificates
}
func (a *AndroidApp) InstallApkName() string {
@@ -651,29 +671,14 @@
dexJarFile := a.dexBuildActions(ctx)
- jniLibs, certificateDeps := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
- jniJarFile := a.jniBuildActions(jniLibs, ctx)
+ jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
+ jniJarFile := a.jniBuildActions(jniLibs, prebuiltJniPackages, ctx)
if ctx.Failed() {
return
}
- certificates := processMainCert(a.ModuleBase, a.getCertString(ctx), certificateDeps, ctx)
-
- // 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"),
- }
- }
+ a.certificate, certificates = processMainCert(a.ModuleBase, a.getCertString(ctx), certificates, ctx)
// Build a final signed app package.
packageFile := android.PathForModuleOut(ctx, a.installApkName+".apk")
@@ -686,10 +691,9 @@
if lineage := String(a.overridableAppProperties.Lineage); lineage != "" {
lineageFile = android.PathForModuleSrc(ctx, lineage)
}
-
rotationMinSdkVersion := String(a.overridableAppProperties.RotationMinSdkVersion)
- CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion)
+ CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion, Bool(a.dexProperties.Optimize.Shrink_resources))
a.outputFile = packageFile
if v4SigningRequested {
a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
@@ -718,7 +722,7 @@
if v4SigningRequested {
v4SignatureFile = android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk.idsig")
}
- CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion)
+ CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion, false)
a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
if v4SigningRequested {
a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
@@ -749,15 +753,16 @@
type appDepsInterface interface {
SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
- MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+ MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
RequiresStableAPIs(ctx android.BaseModuleContext) bool
}
func collectAppDeps(ctx android.ModuleContext, app appDepsInterface,
shouldCollectRecursiveNativeDeps bool,
- checkNativeSdkVersion bool) ([]jniLib, []Certificate) {
+ checkNativeSdkVersion bool) ([]jniLib, android.Paths, []Certificate) {
var jniLibs []jniLib
+ var prebuiltJniPackages android.Paths
var certificates []Certificate
seenModulePaths := make(map[string]bool)
@@ -795,7 +800,10 @@
target: module.Target(),
coverageFile: dep.CoverageOutputFile(),
unstrippedFile: dep.UnstrippedOutputFile(),
+ partition: dep.Partition(),
})
+ } else if ctx.Config().AllowMissingDependencies() {
+ ctx.AddMissingDependencies([]string{otherName})
} else {
ctx.ModuleErrorf("dependency %q missing output file", otherName)
}
@@ -806,6 +814,10 @@
return shouldCollectRecursiveNativeDeps
}
+ if info, ok := ctx.OtherModuleProvider(module, JniPackageProvider).(JniPackageInfo); ok {
+ prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...)
+ }
+
if tag == certificateTag {
if dep, ok := module.(*AndroidAppCertificate); ok {
certificates = append(certificates, dep.Certificate)
@@ -817,7 +829,7 @@
return false
})
- return jniLibs, certificates
+ return jniLibs, prebuiltJniPackages, certificates
}
func (a *AndroidApp) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
@@ -854,10 +866,10 @@
} else {
toMinSdkVersion := "(no version)"
if m, ok := to.(interface {
- MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+ MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
}); ok {
- if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
- toMinSdkVersion = v.ApiLevel.String()
+ if v := m.MinSdkVersion(ctx); !v.IsNone() {
+ toMinSdkVersion = v.String()
}
} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
// TODO(b/175678607) eliminate the use of MinSdkVersion returning
@@ -973,8 +985,11 @@
// The name of the android_app module that the tests will run against.
Instrumentation_for *string
- // if specified, the instrumentation target package name in the manifest is overwritten by it.
+ // If specified, the instrumentation target package name in the manifest is overwritten by it.
Instrumentation_target_package *string
+
+ // If specified, the mainline module package name in the test config is overwritten by it.
+ Mainline_package_name *string
}
type AndroidTest struct {
@@ -1052,6 +1067,11 @@
FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name)
}
+ if a.appTestProperties.Mainline_package_name != nil {
+ fixNeeded = true
+ command.FlagWithArg("--mainline-package-name ", *a.appTestProperties.Mainline_package_name)
+ }
+
if fixNeeded {
rule.Build("fix_test_config", "fix test config")
return fixedConfig
@@ -1078,7 +1098,7 @@
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
@@ -1132,6 +1152,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)
@@ -1286,31 +1307,23 @@
}
}
-func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
+func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, addCompatDeps 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)...)
-
- // Only add these extra dependencies if the module depends on framework libs. This avoids
- // creating a cyclic dependency:
+ ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs...)
+ ctx.AddVariationDependencies(nil, usesLibOptTag, u.presentOptionalUsesLibs(ctx)...)
+ // Only add these extra dependencies if the module is an app that 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...)
+ if addCompatDeps {
+ // 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...)
}
+ } else {
+ ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.usesLibraryProperties.Uses_libs...)
+ ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.presentOptionalUsesLibs(ctx)...)
}
}
@@ -1368,7 +1381,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() {
@@ -1418,7 +1431,7 @@
Flag("--enforce-uses-libraries").
Input(inputFile).
FlagWithOutput("--enforce-uses-libraries-status ", statusFile).
- FlagWithInput("--aapt ", ctx.Config().HostToolPath(ctx, "aapt"))
+ FlagWithInput("--aapt ", ctx.Config().HostToolPath(ctx, "aapt2"))
if outputFile != nil {
cmd.FlagWithOutput("-o ", outputFile)
@@ -1477,71 +1490,91 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "android_app_certificate",
- Bzl_load_location: "//build/bazel/rules/android:android_app_certificate.bzl",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
}
+type manifestValueAttribute struct {
+ MinSdkVersion *string
+}
+
type bazelAndroidAppAttributes struct {
*javaCommonAttributes
+ *bazelAapt
Deps bazel.LabelListAttribute
- Manifest bazel.Label
Custom_package *string
- Resource_files bazel.LabelListAttribute
- Certificate *bazel.Label
- Certificate_name *string
+ Certificate bazel.LabelAttribute
+ Certificate_name bazel.StringAttribute
+ Manifest_values *manifestValueAttribute
}
// ConvertWithBp2build is used to convert android_app to Bazel.
func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- commonAttrs, depLabels := a.convertLibraryAttrsBp2Build(ctx)
+ commonAttrs, bp2BuildInfo := a.convertLibraryAttrsBp2Build(ctx)
+ depLabels := bp2BuildInfo.DepLabels
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.")
+ deps.Append(depLabels.StaticDeps)
+
+ aapt := a.convertAaptAttrsWithBp2Build(ctx)
+
+ certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableAppProperties.Certificate)
+
+ manifestValues := &manifestValueAttribute{}
+ // TODO(b/274474008 ): Directly convert deviceProperties.Min_sdk_version in bp2build
+ // MinSdkVersion(ctx) calls SdkVersion(ctx) if no value for min_sdk_version is set
+ minSdkVersion := a.MinSdkVersion(ctx)
+ if !minSdkVersion.IsPreview() && !minSdkVersion.IsInvalid() {
+ minSdkStr, err := minSdkVersion.EffectiveVersionString(ctx)
+ if err == nil {
+ manifestValues.MinSdkVersion = &minSdkStr
+ }
}
- manifest := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
-
- resourceFiles := bazel.LabelList{
- Includes: []bazel.Label{},
- }
- for _, dir := range android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res") {
- files := android.RootToModuleRelativePaths(ctx, androidResourceGlob(ctx, dir))
- resourceFiles.Includes = append(resourceFiles.Includes, files...)
- }
-
- 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{
- commonAttrs,
- deps,
- android.BazelLabelForModuleSrcSingle(ctx, manifest),
+ appAttrs := &bazelAndroidAppAttributes{
// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
- a.overridableAppProperties.Package_name,
- bazel.MakeLabelListAttribute(resourceFiles),
- certificate,
- certificateNamePtr,
+ Custom_package: a.overridableAppProperties.Package_name,
+ Certificate: certificate,
+ Certificate_name: certificateName,
+ Manifest_values: manifestValues,
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "android_binary",
- Bzl_load_location: "//build/bazel/rules/android:android_binary.bzl",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
+ if !bp2BuildInfo.hasKotlin {
+ appAttrs.javaCommonAttributes = commonAttrs
+ appAttrs.bazelAapt = aapt
+ appAttrs.Deps = deps
+ } else {
+ ktName := a.Name() + "_kt"
+ ctx.CreateBazelTargetModule(
+ AndroidLibraryBazelTargetModuleProperties(),
+ android.CommonAttributes{Name: ktName},
+ &bazelAndroidLibrary{
+ javaLibraryAttributes: &javaLibraryAttributes{
+ javaCommonAttributes: commonAttrs,
+ Deps: deps,
+ },
+ bazelAapt: aapt,
+ },
+ )
+
+ appAttrs.bazelAapt = &bazelAapt{Manifest: aapt.Manifest}
+ appAttrs.Deps = bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + ktName})
+ appAttrs.javaCommonAttributes = &javaCommonAttributes{
+ Sdk_version: commonAttrs.Sdk_version,
+ }
+ }
+
+ ctx.CreateBazelTargetModule(
+ props,
+ android.CommonAttributes{Name: a.Name()},
+ appAttrs,
+ )
}
diff --git a/java/app_builder.go b/java/app_builder.go
index 31023cb..d20a6bf 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -52,7 +52,7 @@
})
func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
- packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string) {
+ packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string, shrinkResources bool) {
unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk"
unsignedApk := android.PathForModuleOut(ctx, unsignedApkName)
@@ -65,7 +65,6 @@
if jniJarFile != nil {
inputs = append(inputs, jniJarFile)
}
-
ctx.Build(pctx, android.BuildParams{
Rule: combineApk,
Inputs: inputs,
@@ -73,6 +72,11 @@
Implicits: deps,
})
+ if shrinkResources {
+ shrunkenApk := android.PathForModuleOut(ctx, "resource-shrunken", unsignedApk.Base())
+ ShrinkResources(ctx, unsignedApk, shrunkenApk)
+ unsignedApk = shrunkenApk
+ }
SignAppPackage(ctx, outputFile, unsignedApk, certificates, v4SignatureFile, lineageFile, rotationMinSdkVersion)
}
@@ -84,7 +88,6 @@
certificateArgs = append(certificateArgs, c.Pem.String(), c.Key.String())
deps = append(deps, c.Pem, c.Key)
}
-
outputFiles := android.WritablePaths{signedApk}
var flags []string
if v4SignatureFile != nil {
@@ -182,7 +185,7 @@
packageFile, jniJarFile, dexJarFile android.Path) {
protoResJarFile := android.PathForModuleOut(ctx, "package-res.pb.apk")
- aapt2Convert(ctx, protoResJarFile, packageFile)
+ aapt2Convert(ctx, protoResJarFile, packageFile, "proto")
var zips android.Paths
@@ -222,8 +225,14 @@
})
}
-func TransformJniLibsToJar(ctx android.ModuleContext, outputFile android.WritablePath,
- jniLibs []jniLib, uncompressJNI bool) {
+const jniJarOutputPathString = "jniJarOutput.zip"
+
+func TransformJniLibsToJar(
+ ctx android.ModuleContext,
+ outputFile android.WritablePath,
+ jniLibs []jniLib,
+ prebuiltJniPackages android.Paths,
+ uncompressJNI bool) {
var deps android.Paths
jarArgs := []string{
@@ -249,13 +258,20 @@
rule = zipRE
args["implicits"] = strings.Join(deps.Strings(), ",")
}
+ jniJarPath := android.PathForModuleOut(ctx, jniJarOutputPathString)
ctx.Build(pctx, android.BuildParams{
Rule: rule,
Description: "zip jni libs",
- Output: outputFile,
+ Output: jniJarPath,
Implicits: deps,
Args: args,
})
+ ctx.Build(pctx, android.BuildParams{
+ Rule: mergeAssetsRule,
+ Description: "merge prebuilt JNI packages",
+ Inputs: append(prebuiltJniPackages, jniJarPath),
+ Output: outputFile,
+ })
}
func (a *AndroidApp) generateJavaUsedByApex(ctx android.ModuleContext) {
diff --git a/java/app_import.go b/java/app_import.go
index 52e37b0..85b35eb 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -19,6 +19,8 @@
import (
"reflect"
+ "github.com/google/blueprint"
+
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -31,6 +33,24 @@
initAndroidAppImportVariantGroupTypes()
}
+var (
+ uncompressEmbeddedJniLibsRule = pctx.AndroidStaticRule("uncompress-embedded-jni-libs", blueprint.RuleParams{
+ Command: `if (zipinfo $in 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
+ `${config.Zip2ZipCmd} -i $in -o $out -0 'lib/**/*.so'` +
+ `; else cp -f $in $out; fi`,
+ CommandDeps: []string{"${config.Zip2ZipCmd}"},
+ Description: "Uncompress embedded JNI libs",
+ })
+
+ uncompressDexRule = pctx.AndroidStaticRule("uncompress-dex", blueprint.RuleParams{
+ Command: `if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
+ `${config.Zip2ZipCmd} -i $in -o $out -0 'classes*.dex'` +
+ `; else cp -f $in $out; fi`,
+ CommandDeps: []string{"${config.Zip2ZipCmd}"},
+ Description: "Uncompress dex files",
+ })
+)
+
func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
@@ -158,10 +178,6 @@
}
}
-func (a *AndroidAppImport) isPrebuiltFrameworkRes() bool {
- return a.Name() == "prebuilt_framework-res"
-}
-
func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
cert := android.SrcIsModule(String(a.properties.Certificate))
if cert != "" {
@@ -178,7 +194,7 @@
}
}
- a.usesLibrary.deps(ctx, !a.isPrebuiltFrameworkRes())
+ a.usesLibrary.deps(ctx, true)
}
func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
@@ -193,15 +209,12 @@
})
return
}
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool("zip2zip").
- FlagWithInput("-i ", inputPath).
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-0 ", "'lib/**/*.so'").
- Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: uncompressEmbeddedJniLibsRule,
+ Input: inputPath,
+ Output: outputPath,
+ })
}
// Returns whether this module should have the dex file stored uncompressed in the APK.
@@ -218,19 +231,6 @@
return shouldUncompressDex(ctx, &a.dexpreopter)
}
-func (a *AndroidAppImport) uncompressDex(
- ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool("zip2zip").
- FlagWithInput("-i ", inputPath).
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-0 ", "'classes*.dex'").
- Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build("uncompress-dex", "Uncompress dex files")
-}
-
func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.generateAndroidBuildActions(ctx)
}
@@ -240,6 +240,10 @@
}
func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
+ if a.Name() == "prebuilt_framework-res" {
+ ctx.ModuleErrorf("prebuilt_framework-res found. This used to have special handling in soong, but was removed due to prebuilt_framework-res no longer existing. This check is to ensure it doesn't come back without readding the special handling.")
+ }
+
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if !apexInfo.IsForPlatform() {
a.hideApexVariantFromMake = true
@@ -259,7 +263,7 @@
ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
}
- _, certificates := collectAppDeps(ctx, a, false, false)
+ _, _, certificates := collectAppDeps(ctx, a, false, false)
// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
// TODO: LOCAL_PACKAGE_SPLITS
@@ -275,14 +279,7 @@
var pathFragments []string
relInstallPath := String(a.properties.Relative_install_path)
- if a.isPrebuiltFrameworkRes() {
- // framework-res.apk is installed as system/framework/framework-res.apk
- if relInstallPath != "" {
- ctx.PropertyErrorf("relative_install_path", "Relative_install_path cannot be set for framework-res")
- }
- pathFragments = []string{"framework"}
- a.preprocessed = true
- } else if Bool(a.properties.Privileged) {
+ if Bool(a.properties.Privileged) {
pathFragments = []string{"priv-app", relInstallPath, a.BaseModuleName()}
} else if ctx.InstallInTestcases() {
pathFragments = []string{relInstallPath, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()}
@@ -306,7 +303,11 @@
a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
if a.dexpreopter.uncompressedDex {
dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
- a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: uncompressDexRule,
+ Input: jnisUncompressed,
+ Output: dexUncompressed,
+ })
jnisUncompressed = dexUncompressed
}
@@ -316,21 +317,13 @@
// Sign or align the package if package has not been preprocessed
- if a.isPrebuiltFrameworkRes() {
- a.outputFile = srcApk
- certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
- if len(certificates) != 1 {
- ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
- }
- a.certificate = certificates[0]
- } else if a.preprocessed {
+ if a.preprocessed {
a.outputFile = srcApk
a.certificate = PresignedCertificate
} else if !Bool(a.properties.Presigned) {
// If the certificate property is empty at this point, default_dev_cert must be set to true.
// Which makes processMainCert's behavior for the empty cert string WAI.
- certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
- a.certificate = certificates[0]
+ a.certificate, certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
signed := android.PathForModuleOut(ctx, "signed", apkFilename)
var lineageFile android.Path
if lineage := String(a.properties.Lineage); lineage != "" {
@@ -421,8 +414,8 @@
return android.SdkSpecPrivate
}
-func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
- return android.SdkSpecPrivate
+func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ return android.SdkSpecPrivate.ApiLevel
}
func (a *AndroidAppImport) LintDepSets() LintDepSets {
@@ -502,7 +495,18 @@
type AndroidTestImport struct {
AndroidAppImport
- testProperties testProperties
+ testProperties struct {
+ // list of compatibility suites (for example "cts", "vts") that the module should be
+ // installed into.
+ Test_suites []string `android:"arch_variant"`
+
+ // list of files or filegroup modules that provide data that should be installed alongside
+ // the test
+ Data []string `android:"path"`
+
+ // Install the test into a folder named for the module in all test suites.
+ Per_testcase_directory *bool
+ }
testImportProperties androidTestImportProperties
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 41be092..8093024 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -17,7 +17,6 @@
import (
"fmt"
"reflect"
- "regexp"
"strings"
"testing"
@@ -294,7 +293,6 @@
},
}
- jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
for _, test := range testCases {
result := android.GroupFixturePreparers(
PrepareForTestWithJavaDefaultModules,
@@ -305,13 +303,9 @@
).RunTestWithBp(t, bp)
variant := result.ModuleForTests("foo", "android_common")
- jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
- if len(matches) != 2 {
- t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
- }
- if strings.HasSuffix(matches[1], test.expected) {
- t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+ input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+ if strings.HasSuffix(input, test.expected) {
+ t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
}
provenanceMetaDataRule := variant.Rule("genProvenanceMetaData")
@@ -369,11 +363,14 @@
a := variant.Module().(*AndroidAppImport)
expectedValues := []string{test.expected}
- actualValues := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
+ entries := android.AndroidMkEntriesForTest(t, ctx, a)[0]
+ actualValues := entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
if !reflect.DeepEqual(actualValues, expectedValues) {
t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
actualValues, expectedValues)
}
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "android_app_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+
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())
@@ -456,7 +453,6 @@
},
}
- jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
for _, test := range testCases {
ctx, _ := testJava(t, test.bp)
@@ -469,13 +465,9 @@
android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule)
continue
}
- jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
- if len(matches) != 2 {
- t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
- }
- if strings.HasSuffix(matches[1], test.expected) {
- t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+ input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+ if strings.HasSuffix(input, test.expected) {
+ t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String())
@@ -513,66 +505,6 @@
}
}
-func TestAndroidAppImport_frameworkRes(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "framework-res",
- certificate: "platform",
- apk: "package-res.apk",
- prefer: true,
- export_package_resources: true,
- // Disable dexpreopt and verify_uses_libraries check as the app
- // contains no Java code to be dexpreopted.
- enforce_uses_libs: false,
- dex_preopt: {
- enabled: false,
- },
- }
- `)
-
- mod := ctx.ModuleForTests("prebuilt_framework-res", "android_common").Module()
- a := mod.(*AndroidAppImport)
-
- if !a.preprocessed {
- t.Errorf("prebuilt framework-res is not preprocessed")
- }
-
- expectedInstallPath := "out/soong/target/product/test_device/system/framework/framework-res.apk"
-
- android.AssertPathRelativeToTopEquals(t, "prebuilt framework-res install location", expectedInstallPath, a.dexpreopter.installPath)
-
- entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
-
- expectedPath := "."
- // From apk property above, in the root of the source tree.
- expectedPrebuiltModuleFile := "package-res.apk"
- // Verify that the apk is preprocessed: The export package is the same
- // as the prebuilt.
- expectedSoongResourceExportPackage := expectedPrebuiltModuleFile
-
- actualPath := entries.EntryMap["LOCAL_PATH"]
- actualPrebuiltModuleFile := entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"]
- actualSoongResourceExportPackage := entries.EntryMap["LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE"]
-
- if len(actualPath) != 1 {
- t.Errorf("LOCAL_PATH incorrect len %d", len(actualPath))
- } else if actualPath[0] != expectedPath {
- t.Errorf("LOCAL_PATH mismatch, actual: %s, expected: %s", actualPath[0], expectedPath)
- }
-
- if len(actualPrebuiltModuleFile) != 1 {
- t.Errorf("LOCAL_PREBUILT_MODULE_FILE incorrect len %d", len(actualPrebuiltModuleFile))
- } else if actualPrebuiltModuleFile[0] != expectedPrebuiltModuleFile {
- t.Errorf("LOCAL_PREBUILT_MODULE_FILE mismatch, actual: %s, expected: %s", actualPrebuiltModuleFile[0], expectedPrebuiltModuleFile)
- }
-
- if len(actualSoongResourceExportPackage) != 1 {
- t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE incorrect len %d", len(actualSoongResourceExportPackage))
- } else if actualSoongResourceExportPackage[0] != expectedSoongResourceExportPackage {
- t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE mismatch, actual: %s, expected: %s", actualSoongResourceExportPackage[0], expectedSoongResourceExportPackage)
- }
-}
-
func TestAndroidAppImport_relativeInstallPath(t *testing.T) {
bp := `
android_app_import {
@@ -589,13 +521,6 @@
}
android_app_import {
- name: "framework-res",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- prefer: true,
- }
-
- android_app_import {
name: "privileged_relative_install_path",
apk: "prebuilts/apk/app.apk",
presigned: true,
@@ -619,11 +544,6 @@
errorMessage: "Install path is not correct for app when relative_install_path is present",
},
{
- name: "prebuilt_framework-res",
- expectedInstallPath: "out/soong/target/product/test_device/system/framework/framework-res.apk",
- errorMessage: "Install path is not correct for framework-res",
- },
- {
name: "privileged_relative_install_path",
expectedInstallPath: "out/soong/target/product/test_device/system/priv-app/my/path/privileged_relative_install_path/privileged_relative_install_path.apk",
errorMessage: "Install path is not correct for privileged app when relative_install_path is present",
@@ -686,8 +606,8 @@
`)
variant := ctx.ModuleForTests("foo", "android_common")
- jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- if !strings.HasPrefix(jniRule, "if (zipinfo") {
+ jniRule := variant.Output("jnis-uncompressed/foo.apk").BuildParams.Rule.String()
+ if jniRule == android.Cp.String() {
t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
}
@@ -807,3 +727,23 @@
}
}
}
+
+func TestAppImportMissingCertificateAllowMissingDependencies(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.PrepareForTestWithAllowMissingDependencies,
+ android.PrepareForTestWithAndroidMk,
+ ).RunTestWithBp(t, `
+ android_app_import {
+ name: "foo",
+ apk: "a.apk",
+ certificate: ":missing_certificate",
+ }`)
+
+ foo := result.ModuleForTests("foo", "android_common")
+ fooApk := foo.Output("signed/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/app_set.go b/java/app_set.go
index d99fadb..d2d3b06 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -90,14 +90,15 @@
}
var TargetCpuAbi = map[string]string{
- "arm": "ARMEABI_V7A",
- "arm64": "ARM64_V8A",
- "riscv64": "RISCV64",
+ "arm": "ARMEABI_V7A",
+ "arm64": "ARM64_V8A",
+ // TODO: use "RISCV64" when that is supported in bundles
+ "riscv64": "ARM64_V8A",
"x86": "X86",
"x86_64": "X86_64",
}
-func SupportedAbis(ctx android.ModuleContext) []string {
+func SupportedAbis(ctx android.ModuleContext, excludeNativeBridgeAbis bool) []string {
abiName := func(targetIdx int, deviceArch string) string {
if abi, found := TargetCpuAbi[deviceArch]; found {
return abi
@@ -108,6 +109,9 @@
var result []string
for i, target := range ctx.Config().Targets[android.Android] {
+ if target.NativeBridge == android.NativeBridgeEnabled && excludeNativeBridgeAbis {
+ continue
+ }
result = append(result, abiName(i, target.Arch.ArchType.String()))
}
return result
@@ -134,10 +138,11 @@
ImplicitOutputs: android.WritablePaths{as.packedOutput, as.apkcertsFile},
Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)},
Args: map[string]string{
- "abis": strings.Join(SupportedAbis(ctx), ","),
+ "abis": strings.Join(SupportedAbis(ctx, false), ","),
"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
"screen-densities": screenDensities,
"sdk-version": ctx.Config().PlatformSdkVersion().String(),
+ "skip-sdk-check": strconv.FormatBool(ctx.Config().IsEnvTrue("SOONG_SKIP_APPSET_SDK_CHECK")),
"stem": as.BaseModuleName(),
"apkcerts": as.apkcertsFile.String(),
"partition": as.PartitionTag(ctx.DeviceConfig()),
diff --git a/java/app_set_test.go b/java/app_set_test.go
index 03eb667..10bc5de 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -89,6 +89,7 @@
"allow-prereleased": "false",
"screen-densities": "LDPI,XXHDPI",
"sdk-version": "29",
+ "skip-sdk-check": "false",
"stem": "foo",
},
},
@@ -105,6 +106,7 @@
"allow-prereleased": "false",
"screen-densities": "all",
"sdk-version": "30",
+ "skip-sdk-check": "false",
"stem": "foo",
},
},
diff --git a/java/app_test.go b/java/app_test.go
index 53676be..b154bc9 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1005,6 +1005,7 @@
platformSdkInt int
platformSdkCodename string
platformSdkFinal bool
+ minSdkVersionBp string
expectedMinSdkVersion string
platformApis bool
activeCodenames []string
@@ -1052,6 +1053,14 @@
platformSdkCodename: "S",
activeCodenames: []string{"S"},
},
+ {
+ name: "two active SDKs",
+ sdkVersion: "module_current",
+ minSdkVersionBp: "UpsideDownCake",
+ expectedMinSdkVersion: "UpsideDownCake", // And not VanillaIceCream
+ platformSdkCodename: "VanillaIceCream",
+ activeCodenames: []string{"UpsideDownCake", "VanillaIceCream"},
+ },
}
for _, moduleType := range []string{"android_app", "android_library"} {
@@ -1061,12 +1070,17 @@
if test.platformApis {
platformApiProp = "platform_apis: true,"
}
+ minSdkVersionProp := ""
+ if test.minSdkVersionBp != "" {
+ minSdkVersionProp = fmt.Sprintf(` min_sdk_version: "%s",`, test.minSdkVersionBp)
+ }
bp := fmt.Sprintf(`%s {
name: "foo",
srcs: ["a.java"],
sdk_version: "%s",
%s
- }`, moduleType, test.sdkVersion, platformApiProp)
+ %s
+ }`, moduleType, test.sdkVersion, platformApiProp, minSdkVersionProp)
result := android.GroupFixturePreparers(
prepareForJavaTest,
@@ -1218,7 +1232,7 @@
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
app := ctx.ModuleForTests(test.name, "android_common")
- jniLibZip := app.Output("jnilibs.zip")
+ jniLibZip := app.Output(jniJarOutputPathString)
var abis []string
args := strings.Fields(jniLibZip.Args["jarArgs"])
for i := 0; i < len(args); i++ {
@@ -1351,7 +1365,7 @@
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
app := ctx.ModuleForTests(test.name, "android_common")
- jniLibZip := app.MaybeOutput("jnilibs.zip")
+ jniLibZip := app.MaybeOutput(jniJarOutputPathString)
if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
t.Errorf("expected jni packaged %v, got %v", w, g)
}
@@ -1442,7 +1456,7 @@
t.Run(test.name, func(t *testing.T) {
app := ctx.ModuleForTests(test.name, "android_common")
- jniLibZip := app.MaybeOutput("jnilibs.zip")
+ jniLibZip := app.MaybeOutput(jniJarOutputPathString)
if len(jniLibZip.Implicits) != 1 {
t.Fatalf("expected exactly one jni library, got %q", jniLibZip.Implicits.Strings())
}
@@ -1490,6 +1504,7 @@
testCases := []struct {
name string
bp string
+ allowMissingDependencies bool
certificateOverride string
expectedCertSigningFlags string
expectedCertificate string
@@ -1505,7 +1520,7 @@
`,
certificateOverride: "",
expectedCertSigningFlags: "",
- expectedCertificate: "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8",
+ expectedCertificate: "build/make/target/product/security/testkey",
},
{
name: "module certificate property",
@@ -1524,7 +1539,7 @@
`,
certificateOverride: "",
expectedCertSigningFlags: "",
- expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+ expectedCertificate: "cert/new_cert",
},
{
name: "path certificate property",
@@ -1538,7 +1553,7 @@
`,
certificateOverride: "",
expectedCertSigningFlags: "",
- expectedCertificate: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+ expectedCertificate: "build/make/target/product/security/expiredkey",
},
{
name: "certificate overrides",
@@ -1557,7 +1572,7 @@
`,
certificateOverride: "foo:new_certificate",
expectedCertSigningFlags: "",
- expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+ expectedCertificate: "cert/new_cert",
},
{
name: "certificate signing flags",
@@ -1578,7 +1593,7 @@
`,
certificateOverride: "",
expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32",
- expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+ expectedCertificate: "cert/new_cert",
},
{
name: "cert signing flags from filegroup",
@@ -1604,7 +1619,20 @@
`,
certificateOverride: "",
expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32",
- expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+ expectedCertificate: "cert/new_cert",
+ },
+ {
+ name: "missing with AllowMissingDependencies",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ certificate: ":new_certificate",
+ sdk_version: "current",
+ }
+ `,
+ expectedCertificate: "out/soong/.intermediates/foo/android_common/missing",
+ allowMissingDependencies: true,
},
}
@@ -1616,17 +1644,32 @@
if test.certificateOverride != "" {
variables.CertificateOverrides = []string{test.certificateOverride}
}
+ if test.allowMissingDependencies {
+ variables.Allow_missing_dependencies = proptools.BoolPtr(true)
+ }
+ }),
+ android.FixtureModifyContext(func(ctx *android.TestContext) {
+ ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
}),
).RunTestWithBp(t, test.bp)
foo := result.ModuleForTests("foo", "android_common")
- signapk := foo.Output("foo.apk")
- signCertificateFlags := signapk.Args["certificates"]
- android.AssertStringEquals(t, "certificates flags", test.expectedCertificate, signCertificateFlags)
+ certificate := foo.Module().(*AndroidApp).certificate
+ android.AssertPathRelativeToTopEquals(t, "certificates key", test.expectedCertificate+".pk8", certificate.Key)
+ // The sign_target_files_apks and check_target_files_signatures
+ // tools require that certificates have a .x509.pem extension.
+ android.AssertPathRelativeToTopEquals(t, "certificates pem", test.expectedCertificate+".x509.pem", certificate.Pem)
- certSigningFlags := signapk.Args["flags"]
- android.AssertStringEquals(t, "cert signing flags", test.expectedCertSigningFlags, certSigningFlags)
+ signapk := foo.Output("foo.apk")
+ if signapk.Rule != android.ErrorRule {
+ signCertificateFlags := signapk.Args["certificates"]
+ expectedFlags := certificate.Pem.String() + " " + certificate.Key.String()
+ android.AssertStringEquals(t, "certificates flags", expectedFlags, signCertificateFlags)
+
+ certSigningFlags := signapk.Args["flags"]
+ android.AssertStringEquals(t, "cert signing flags", test.expectedCertSigningFlags, certSigningFlags)
+ }
})
}
}
@@ -2301,12 +2344,14 @@
srcs: ["b.java"],
package_name: "com.android.bar.test",
instrumentation_for: "foo",
+ mainline_package_name: "com.android.bar",
}
override_android_test {
name: "baz_test",
base: "foo_test",
package_name: "com.android.baz.test",
+ mainline_package_name: "com.android.baz",
}
`)
@@ -2325,6 +2370,7 @@
expectedFlags: []string{
"--manifest out/soong/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
"--package-name com.android.bar.test",
+ "--mainline-package-name com.android.bar",
},
},
{
@@ -2334,6 +2380,8 @@
"--manifest out/soong/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
"--package-name com.android.baz.test",
"--test-file-name baz_test.apk",
+ "out/soong/.intermediates/foo_test/android_common_baz_test/test_config_fixer/AndroidTest.xml",
+ "--mainline-package-name com.android.baz",
},
},
}
@@ -2428,7 +2476,7 @@
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
app := ctx.ModuleForTests(test.name, "android_common")
- jniLibZip := app.Output("jnilibs.zip")
+ jniLibZip := app.Output(jniJarOutputPathString)
var jnis []string
args := strings.Fields(jniLibZip.Args["jarArgs"])
for i := 0; i < len(args); i++ {
@@ -2605,12 +2653,20 @@
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`
+ `--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).
@@ -2978,11 +3034,13 @@
func TestTargetSdkVersionManifestFixer(t *testing.T) {
platform_sdk_codename := "Tiramisu"
+ platform_sdk_version := 33
testCases := []struct {
name string
targetSdkVersionInBp string
targetSdkVersionExpected string
unbundledBuild bool
+ platformSdkFinal bool
}{
{
name: "Non-Unbundled build: Android.bp has targetSdkVersion",
@@ -3019,20 +3077,34 @@
targetSdkVersionExpected: "10000",
unbundledBuild: true,
},
+ {
+ name: "Bundled build in REL branches",
+ targetSdkVersionExpected: "33",
+ unbundledBuild: false,
+ platformSdkFinal: true,
+ },
}
for _, testCase := range testCases {
+ targetSdkVersionTemplate := ""
+ if testCase.targetSdkVersionInBp != "" {
+ targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, testCase.targetSdkVersionInBp)
+ }
bp := fmt.Sprintf(`
android_app {
name: "foo",
sdk_version: "current",
- target_sdk_version: "%v",
+ %s
}
- `, testCase.targetSdkVersionInBp)
+ `, targetSdkVersionTemplate)
fixture := android.GroupFixturePreparers(
prepareForJavaTest,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if testCase.platformSdkFinal {
+ variables.Platform_sdk_final = proptools.BoolPtr(true)
+ }
// explicitly set platform_sdk_codename to make the test deterministic
variables.Platform_sdk_codename = &platform_sdk_codename
+ variables.Platform_sdk_version = &platform_sdk_version
variables.Platform_version_active_codenames = []string{platform_sdk_codename}
// create a non-empty list if unbundledBuild==true
if testCase.unbundledBuild {
@@ -3105,16 +3177,20 @@
},
}
for _, testCase := range testCases {
+ targetSdkVersionTemplate := ""
+ if testCase.targetSdkVersionInBp != nil {
+ targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, *testCase.targetSdkVersionInBp)
+ }
bp := fmt.Sprintf(`
android_app {
name: "foo",
sdk_version: "current",
min_sdk_version: "29",
- target_sdk_version: "%v",
+ %s
updatable: %t,
enforce_default_target_sdk_version: %t
}
- `, proptools.String(testCase.targetSdkVersionInBp), testCase.updatable, testCase.updatable) // enforce default target sdk version if app is updatable
+ `, targetSdkVersionTemplate, testCase.updatable, testCase.updatable) // enforce default target sdk version if app is updatable
fixture := android.GroupFixturePreparers(
PrepareForTestWithJavaDefaultModules,
@@ -3126,6 +3202,7 @@
variables.Platform_sdk_version = &platform_sdk_version
variables.Platform_sdk_codename = &platform_sdk_codename
variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+ variables.Unbundled_build = proptools.BoolPtr(true)
variables.Unbundled_build_apps = []string{"sampleModule"}
}),
)
@@ -3204,6 +3281,7 @@
variables.Platform_sdk_final = &testCase.platform_sdk_final
variables.Platform_sdk_version = &platform_sdk_version
variables.Platform_sdk_codename = &platform_sdk_codename
+ variables.Unbundled_build = proptools.BoolPtr(true)
variables.Unbundled_build_apps = []string{"sampleModule"}
}),
)
@@ -3274,6 +3352,7 @@
variables.Platform_sdk_final = &testCase.platform_sdk_final
variables.Platform_sdk_version = &platform_sdk_version
variables.Platform_sdk_codename = &platform_sdk_codename
+ variables.Unbundled_build = proptools.BoolPtr(true)
variables.Unbundled_build_apps = []string{"sampleModule"}
}),
)
@@ -3303,6 +3382,14 @@
srcs: ["a.java"],
certificate: ":missing_certificate",
sdk_version: "current",
+ }
+
+ android_app {
+ name: "bar",
+ srcs: ["a.java"],
+ certificate: ":missing_certificate",
+ product_specific: true,
+ sdk_version: "current",
}`)
foo := result.ModuleForTests("foo", "android_common")
@@ -3313,6 +3400,92 @@
android.AssertStringDoesContain(t, "expected error rule message", fooApk.Args["error"], "missing dependencies: missing_certificate\n")
}
+func TestAppIncludesJniPackages(t *testing.T) {
+ ctx := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ ).RunTestWithBp(t, `
+ android_library_import {
+ name: "aary-nodeps",
+ aars: ["aary.aar"],
+ extract_jni: true,
+ }
+
+ android_library {
+ name: "aary-lib",
+ sdk_version: "current",
+ min_sdk_version: "21",
+ static_libs: ["aary-nodeps"],
+ }
+
+ android_app {
+ name: "aary-lib-dep",
+ sdk_version: "current",
+ min_sdk_version: "21",
+ manifest: "AndroidManifest.xml",
+ static_libs: ["aary-lib"],
+ use_embedded_native_libs: true,
+ }
+
+ android_app {
+ name: "aary-import-dep",
+ sdk_version: "current",
+ min_sdk_version: "21",
+ manifest: "AndroidManifest.xml",
+ static_libs: ["aary-nodeps"],
+ use_embedded_native_libs: true,
+ }
+
+ android_app {
+ name: "aary-no-use-embedded",
+ sdk_version: "current",
+ min_sdk_version: "21",
+ manifest: "AndroidManifest.xml",
+ static_libs: ["aary-nodeps"],
+ }`)
+
+ testCases := []struct {
+ name string
+ hasPackage bool
+ }{
+ {
+ name: "aary-import-dep",
+ hasPackage: true,
+ },
+ {
+ name: "aary-lib-dep",
+ hasPackage: true,
+ },
+ {
+ name: "aary-no-use-embedded",
+ hasPackage: false,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ app := ctx.ModuleForTests(tc.name, "android_common")
+
+ outputFile := "jnilibs.zip"
+ jniOutputLibZip := app.MaybeOutput(outputFile)
+ if jniOutputLibZip.Rule == nil && !tc.hasPackage {
+ return
+ }
+
+ jniPackage := "arm64-v8a_jni.zip"
+ inputs := jniOutputLibZip.Inputs
+ foundPackage := false
+ for i := 0; i < len(inputs); i++ {
+ if strings.Contains(inputs[i].String(), jniPackage) {
+ foundPackage = true
+ }
+ }
+ if foundPackage != tc.hasPackage {
+ t.Errorf("expected to find %v in %v inputs; inputs = %v", jniPackage, outputFile, inputs)
+ }
+ })
+ }
+}
+
func TestTargetSdkVersionMtsTests(t *testing.T) {
platformSdkCodename := "Tiramisu"
android_test := "android_test"
diff --git a/java/base.go b/java/base.go
index 4e7b18c..9911323 100644
--- a/java/base.go
+++ b/java/base.go
@@ -190,7 +190,7 @@
// constructing a new module.
type DeviceProperties struct {
// If not blank, set to the version of the sdk to compile against.
- // Defaults to private.
+ // Defaults to an empty string, which compiles the module against the private platform APIs.
// 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>"
@@ -206,6 +206,10 @@
// Defaults to empty string "". See sdk_version for possible values.
Max_sdk_version *string
+ // if not blank, set the maxSdkVersion properties of permission and uses-permission tags.
+ // Defaults to empty string "". See sdk_version for possible values.
+ Replace_max_sdk_version_placeholder *string
+
// if not blank, set the targetSdkVersion in the AndroidManifest.xml.
// Defaults to sdk_version if not set. See sdk_version for possible values.
Target_sdk_version *string
@@ -391,7 +395,6 @@
android.ModuleBase
android.DefaultableModuleBase
android.ApexModuleBase
- android.SdkBase
android.BazelModuleBase
// Functionality common to Module and Import.
@@ -443,16 +446,15 @@
// installed file for hostdex copy
hostdexInstallFile android.InstallPath
- // list of .java files and srcjars that was passed to javac
- compiledJavaSrcs android.Paths
- compiledSrcJars android.Paths
+ // list of unique .java and .kt source files
+ uniqueSrcFiles android.Paths
+
+ // list of srcjars that was passed to javac
+ compiledSrcJars android.Paths
// 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
@@ -487,8 +489,8 @@
hideApexVariantFromMake bool
sdkVersion android.SdkSpec
- minSdkVersion android.SdkSpec
- maxSdkVersion android.SdkSpec
+ minSdkVersion android.ApiLevel
+ maxSdkVersion android.ApiLevel
sourceExtensions []string
}
@@ -529,7 +531,7 @@
// TODO(satayev): cover other types as well, e.g. imports
case *Library, *AndroidLibrary:
switch tag {
- case bootClasspathTag, libTag, staticLibTag, java9LibTag:
+ case bootClasspathTag, sdkLibTag, libTag, staticLibTag, java9LibTag:
j.checkSdkLinkType(ctx, module.(moduleWithSdkDep), tag.(dependencyTag))
}
}
@@ -590,6 +592,8 @@
return android.Paths{j.outputFile}, nil
case ".jar":
return android.Paths{j.implementationAndResourcesJar}, nil
+ case ".hjar":
+ return android.Paths{j.headerJarFile}, nil
case ".proguard_map":
if j.dexer.proguardDictionary.Valid() {
return android.Paths{j.dexer.proguardDictionary.Path()}, nil
@@ -661,29 +665,38 @@
return proptools.String(j.deviceProperties.System_modules)
}
-func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
if j.deviceProperties.Min_sdk_version != nil {
- return android.SdkSpecFrom(ctx, *j.deviceProperties.Min_sdk_version)
+ return android.ApiLevelFrom(ctx, *j.deviceProperties.Min_sdk_version)
}
- return j.SdkVersion(ctx)
+ return j.SdkVersion(ctx).ApiLevel
}
-func (j *Module) MaxSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
- maxSdkVersion := proptools.StringDefault(j.deviceProperties.Max_sdk_version, "")
- // SdkSpecFrom returns SdkSpecPrivate for this, which may be confusing.
- // TODO(b/208456999): ideally MaxSdkVersion should be an ApiLevel and not SdkSpec.
- return android.SdkSpecFrom(ctx, maxSdkVersion)
+func (j *Module) MaxSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ if j.deviceProperties.Max_sdk_version != nil {
+ return android.ApiLevelFrom(ctx, *j.deviceProperties.Max_sdk_version)
+ }
+ // Default is PrivateApiLevel
+ return android.SdkSpecPrivate.ApiLevel
+}
+
+func (j *Module) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
+ if j.deviceProperties.Replace_max_sdk_version_placeholder != nil {
+ return android.ApiLevelFrom(ctx, *j.deviceProperties.Replace_max_sdk_version_placeholder)
+ }
+ // Default is PrivateApiLevel
+ return android.SdkSpecPrivate.ApiLevel
}
func (j *Module) MinSdkVersionString() string {
- return j.minSdkVersion.Raw
+ return j.minSdkVersion.String()
}
-func (j *Module) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (j *Module) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
if j.deviceProperties.Target_sdk_version != nil {
- return android.SdkSpecFrom(ctx, *j.deviceProperties.Target_sdk_version)
+ return android.ApiLevelFrom(ctx, *j.deviceProperties.Target_sdk_version)
}
- return j.SdkVersion(ctx)
+ return j.SdkVersion(ctx).ApiLevel
}
func (j *Module) AvailableFor(what string) bool {
@@ -750,8 +763,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)
}
}
@@ -772,9 +787,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
@@ -820,7 +833,7 @@
}
func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
- aidlIncludeDirs android.Paths) (string, android.Paths) {
+ aidlIncludeDirs android.Paths, aidlSrcs android.Paths) (string, android.Paths) {
aidlIncludes := android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Local_include_dirs)
aidlIncludes = append(aidlIncludes,
@@ -830,6 +843,7 @@
var flags []string
var deps android.Paths
+ var includeDirs android.Paths
flags = append(flags, j.deviceProperties.Aidl.Flags...)
@@ -837,23 +851,28 @@
flags = append(flags, "-p"+aidlPreprocess.String())
deps = append(deps, aidlPreprocess.Path())
} else if len(aidlIncludeDirs) > 0 {
- flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
+ includeDirs = append(includeDirs, aidlIncludeDirs...)
}
if len(j.exportAidlIncludeDirs) > 0 {
- flags = append(flags, android.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I"))
+ includeDirs = append(includeDirs, j.exportAidlIncludeDirs...)
}
if len(aidlIncludes) > 0 {
- flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
+ includeDirs = append(includeDirs, aidlIncludes...)
}
- flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String())
+ includeDirs = append(includeDirs, android.PathForModuleSrc(ctx))
if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() {
- flags = append(flags, "-I"+src.String())
+ includeDirs = append(includeDirs, src.Path())
}
+ flags = append(flags, android.JoinWithPrefix(includeDirs.Strings(), "-I"))
+ // add flags for dirs containing AIDL srcs that haven't been specified yet
+ flags = append(flags, genAidlIncludeFlags(ctx, aidlSrcs, includeDirs))
- if Bool(j.deviceProperties.Aidl.Generate_traces) {
+ sdkVersion := (j.SdkVersion(ctx)).Kind
+ defaultTrace := ((sdkVersion == android.SdkSystemServer) || (sdkVersion == android.SdkCore) || (sdkVersion == android.SdkCorePlatform) || (sdkVersion == android.SdkModule) || (sdkVersion == android.SdkSystem))
+ if proptools.BoolDefault(j.deviceProperties.Aidl.Generate_traces, defaultTrace) {
flags = append(flags, "-t")
}
@@ -866,7 +885,7 @@
j.ignoredAidlPermissionList = android.PathsForModuleSrcExcludes(ctx, exceptions, nil)
}
- aidlMinSdkVersion := j.MinSdkVersion(ctx).ApiLevel.String()
+ aidlMinSdkVersion := j.MinSdkVersion(ctx).String()
flags = append(flags, "--min_sdk_version="+aidlMinSdkVersion)
return strings.Join(flags, " "), deps
@@ -935,9 +954,6 @@
// systemModules
flags.systemModules = deps.systemModules
- // aidl flags.
- flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
-
return flags
}
@@ -991,7 +1007,7 @@
topLevelDirs[srcFileParts[0]] = true
}
}
- patchPaths = append(patchPaths, android.SortedStringKeys(topLevelDirs)...)
+ patchPaths = append(patchPaths, android.SortedKeys(topLevelDirs)...)
classPath := flags.classpath.FormJavaClassPath("")
if classPath != "" {
@@ -1046,6 +1062,10 @@
ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files")
}
+ aidlSrcs := srcFiles.FilterByExt(".aidl")
+ flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs, aidlSrcs)
+
+ nonGeneratedSrcJars := srcFiles.FilterByExt(".srcjar")
srcFiles = j.genSources(ctx, srcFiles, flags)
// Collect javac flags only after computing the full set of srcFiles to
@@ -1065,15 +1085,26 @@
jarName := ctx.ModuleName() + ".jar"
- javaSrcFiles := srcFiles.FilterByExt(".java")
- var uniqueSrcFiles android.Paths
+ var uniqueJavaFiles android.Paths
set := make(map[string]bool)
- for _, v := range javaSrcFiles {
+ for _, v := range srcFiles.FilterByExt(".java") {
if _, found := set[v.String()]; !found {
set[v.String()] = true
- uniqueSrcFiles = append(uniqueSrcFiles, v)
+ uniqueJavaFiles = append(uniqueJavaFiles, v)
}
}
+ var uniqueKtFiles android.Paths
+ for _, v := range srcFiles.FilterByExt(".kt") {
+ if _, found := set[v.String()]; !found {
+ set[v.String()] = true
+ uniqueKtFiles = append(uniqueKtFiles, v)
+ }
+ }
+
+ var uniqueSrcFiles android.Paths
+ uniqueSrcFiles = append(uniqueSrcFiles, uniqueJavaFiles...)
+ uniqueSrcFiles = append(uniqueSrcFiles, uniqueKtFiles...)
+ j.uniqueSrcFiles = uniqueSrcFiles
// 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
@@ -1081,7 +1112,7 @@
// is used to run all of the annotation processors.
disableTurbine := deps.disableTurbine
- // Collect .java files for AIDEGen
+ // Collect .java and .kt files for AIDEGen
j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...)
var kotlinJars android.Paths
@@ -1119,19 +1150,12 @@
flags.kotlincFlags += "$kotlincFlags"
}
- var kotlinSrcFiles android.Paths
- kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
- kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
-
- // Collect .kt files for AIDEGen
- j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.FilterByExt(".kt").Strings()...)
+ // Collect common .kt files for AIDEGen
j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, kotlinCommonSrcFiles.Strings()...)
flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
- flags.dexClasspath = append(flags.dexClasspath, deps.kotlinAnnotations...)
-
flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...)
flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...)
@@ -1139,7 +1163,7 @@
// Use kapt for annotation processing
kaptSrcJar := android.PathForModuleOut(ctx, "kapt", "kapt-sources.jar")
kaptResJar := android.PathForModuleOut(ctx, "kapt", "kapt-res.jar")
- kotlinKapt(ctx, kaptSrcJar, kaptResJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+ kotlinKapt(ctx, kaptSrcJar, kaptResJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
srcJars = append(srcJars, kaptSrcJar)
kotlinJars = append(kotlinJars, kaptResJar)
// Disable annotation processing in javac, it's already been handled by kapt
@@ -1149,7 +1173,7 @@
kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName)
- kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+ kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
if ctx.Failed() {
return
}
@@ -1163,16 +1187,17 @@
// 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...)
}
}
jars := append(android.Paths(nil), kotlinJars...)
- // Store the list of .java files that was passed to javac
- j.compiledJavaSrcs = uniqueSrcFiles
j.compiledSrcJars = srcJars
enableSharding := false
@@ -1187,18 +1212,27 @@
// with sharding enabled. See: b/77284273.
}
headerJarFileWithoutDepsOrJarjar, j.headerJarFile =
- j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinHeaderJars)
+ j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, kotlinHeaderJars)
if ctx.Failed() {
return
}
}
- if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
+ if len(uniqueJavaFiles) > 0 || len(srcJars) > 0 {
+ hasErrorproneableFiles := false
+ for _, ext := range j.sourceExtensions {
+ if ext != ".proto" && ext != ".aidl" {
+ // Skip running errorprone on pure proto or pure aidl modules. Some modules take a long time to
+ // compile, and it's not useful to have warnings on these generated sources.
+ hasErrorproneableFiles = true
+ break
+ }
+ }
var extraJarDeps android.Paths
if Bool(j.properties.Errorprone.Enabled) {
// If error-prone is enabled, enable errorprone flags on the regular
// build.
flags = enableErrorproneFlags(flags)
- } else if ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil {
+ } else if hasErrorproneableFiles && ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil {
// Otherwise, if the RUN_ERROR_PRONE environment variable is set, create
// a new jar file just for compiling with the errorprone compiler to.
// This is because we don't want to cause the java files to get completely
@@ -1208,7 +1242,7 @@
errorproneFlags := enableErrorproneFlags(flags)
errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
- transformJavaToClasses(ctx, errorprone, -1, uniqueSrcFiles, srcJars, errorproneFlags, nil,
+ transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneFlags, nil,
"errorprone", "errorprone")
extraJarDeps = append(extraJarDeps, errorprone)
@@ -1220,8 +1254,8 @@
}
shardSize := int(*(j.properties.Javac_shard_size))
var shardSrcs []android.Paths
- if len(uniqueSrcFiles) > 0 {
- shardSrcs = android.ShardPaths(uniqueSrcFiles, shardSize)
+ if len(uniqueJavaFiles) > 0 {
+ shardSrcs = android.ShardPaths(uniqueJavaFiles, shardSize)
for idx, shardSrc := range shardSrcs {
classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc,
nil, flags, extraJarDeps)
@@ -1234,7 +1268,7 @@
jars = append(jars, classes)
}
} else {
- classes := j.compileJavaClasses(ctx, jarName, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps)
+ classes := j.compileJavaClasses(ctx, jarName, -1, uniqueJavaFiles, srcJars, flags, extraJarDeps)
jars = append(jars, classes)
}
if ctx.Failed() {
@@ -1457,7 +1491,14 @@
}
// Dex compilation
var dexOutputFile android.OutputPath
- dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), implementationAndResourcesJar, jarName)
+ params := &compileDexParams{
+ flags: flags,
+ sdkVersion: j.SdkVersion(ctx),
+ minSdkVersion: j.MinSdkVersion(ctx),
+ classesJar: implementationAndResourcesJar,
+ jarName: jarName,
+ }
+ dexOutputFile = j.dexer.compileDex(ctx, params)
if ctx.Failed() {
return
}
@@ -1505,22 +1546,41 @@
}
if ctx.Device() {
- lintSDKVersion := func(sdkSpec android.SdkSpec) android.ApiLevel {
- if v := sdkSpec.ApiLevel; !v.IsPreview() {
- return v
+ lintSDKVersion := func(apiLevel android.ApiLevel) int {
+ if !apiLevel.IsPreview() {
+ return apiLevel.FinalInt()
} else {
- return ctx.Config().DefaultAppTargetSdk(ctx)
+ // When running metalava, we pass --version-codename. When that value
+ // is not REL, metalava will add 1 to the --current-version argument.
+ // On old branches, PLATFORM_SDK_VERSION is the latest version (for that
+ // branch) and the codename is REL, except potentially on the most
+ // recent non-master branch. On that branch, it goes through two other
+ // phases before it gets to the phase previously described:
+ // - PLATFORM_SDK_VERSION has not been updated yet, and the codename
+ // is not rel. This happens for most of the internal branch's life
+ // while the branch has been cut but is still under active development.
+ // - PLATFORM_SDK_VERSION has been set, but the codename is still not
+ // REL. This happens briefly during the release process. During this
+ // state the code to add --current-version is commented out, and then
+ // that commenting out is reverted after the codename is set to REL.
+ // On the master branch, the PLATFORM_SDK_VERSION always represents a
+ // prior version and the codename is always non-REL.
+ //
+ // We need to add one here to match metalava adding 1. Technically
+ // this means that in the state described in the second bullet point
+ // above, this number is 1 higher than it should be.
+ return ctx.Config().PlatformSdkVersion().FinalInt() + 1
}
}
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 = lintSDKVersion(j.MinSdkVersion(ctx))
j.linter.targetSdkVersion = lintSDKVersion(j.TargetSdkVersion(ctx))
- j.linter.compileSdkVersion = lintSDKVersion(j.SdkVersion(ctx))
+ j.linter.compileSdkVersion = lintSDKVersion(j.SdkVersion(ctx).ApiLevel)
j.linter.compileSdkKind = j.SdkVersion(ctx).Kind
j.linter.javaLanguageLevel = flags.javaVersion.String()
j.linter.kotlinLanguageLevel = "1.3"
@@ -1534,6 +1594,8 @@
ctx.SetProvider(JavaInfoProvider, JavaInfo{
HeaderJars: android.PathsIfNonNil(j.headerJarFile),
+ TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars,
+ TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar),
ImplementationJars: android.PathsIfNonNil(j.implementationJarFile),
ResourceJars: android.PathsIfNonNil(j.resourceJar),
@@ -1670,6 +1732,52 @@
return instrumentedJar
}
+type providesTransitiveHeaderJars struct {
+ // set of header jars for all transitive libs deps
+ transitiveLibsHeaderJars *android.DepSet
+ // set of header jars for all transitive static libs deps
+ transitiveStaticLibsHeaderJars *android.DepSet
+}
+
+func (j *providesTransitiveHeaderJars) TransitiveLibsHeaderJars() *android.DepSet {
+ return j.transitiveLibsHeaderJars
+}
+
+func (j *providesTransitiveHeaderJars) TransitiveStaticLibsHeaderJars() *android.DepSet {
+ return j.transitiveStaticLibsHeaderJars
+}
+
+func (j *providesTransitiveHeaderJars) collectTransitiveHeaderJars(ctx android.ModuleContext) {
+ directLibs := android.Paths{}
+ directStaticLibs := android.Paths{}
+ transitiveLibs := []*android.DepSet{}
+ transitiveStaticLibs := []*android.DepSet{}
+ ctx.VisitDirectDeps(func(module android.Module) {
+ // don't add deps of the prebuilt version of the same library
+ if ctx.ModuleName() == android.RemoveOptionalPrebuiltPrefix(module.Name()) {
+ return
+ }
+
+ dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+ if dep.TransitiveLibsHeaderJars != nil {
+ transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars)
+ }
+ if dep.TransitiveStaticLibsHeaderJars != nil {
+ transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars)
+ }
+
+ tag := ctx.OtherModuleDependencyTag(module)
+ _, isUsesLibDep := tag.(usesLibraryDependencyTag)
+ if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep {
+ directLibs = append(directLibs, dep.HeaderJars...)
+ } else if tag == staticLibTag {
+ directStaticLibs = append(directStaticLibs, dep.HeaderJars...)
+ }
+ })
+ j.transitiveLibsHeaderJars = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs)
+ j.transitiveStaticLibsHeaderJars = android.NewDepSet(android.POSTORDER, directStaticLibs, transitiveStaticLibs)
+}
+
func (j *Module) HeaderJars() android.Paths {
if j.headerJarFile == nil {
return nil
@@ -1741,15 +1849,17 @@
// Implements android.ApexModule
func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
- sdkSpec := j.MinSdkVersion(ctx)
- if !sdkSpec.Specified() {
+ sdkVersionSpec := j.SdkVersion(ctx)
+ minSdkVersion := j.MinSdkVersion(ctx)
+ if !minSdkVersion.Specified() {
return fmt.Errorf("min_sdk_version is not specified")
}
- if sdkSpec.Kind == android.SdkCore {
+ // If the module is compiling against core (via sdk_version), skip comparison check.
+ if sdkVersionSpec.Kind == android.SdkCore {
return nil
}
- if sdkSpec.ApiLevel.GreaterThan(sdkVersion) {
- return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel)
+ if minSdkVersion.GreaterThan(sdkVersion) {
+ return fmt.Errorf("newer SDK(%v)", minSdkVersion)
}
return nil
}
@@ -1813,19 +1923,19 @@
func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) {
switch name {
- case "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs",
+ case android.SdkCore.JavaLibraryName(ctx.Config()), "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs",
"stub-annotations", "private-stub-annotations-jar",
"core-lambda-stubs", "core-generated-annotation-stubs":
return javaCore, true
- case "android_stubs_current":
+ case android.SdkPublic.JavaLibraryName(ctx.Config()):
return javaSdk, true
- case "android_system_stubs_current":
+ case android.SdkSystem.JavaLibraryName(ctx.Config()):
return javaSystem, true
- case "android_module_lib_stubs_current":
+ case android.SdkModule.JavaLibraryName(ctx.Config()):
return javaModule, true
- case "android_system_server_stubs_current":
+ case android.SdkSystemServer.JavaLibraryName(ctx.Config()):
return javaSystemServer, true
- case "android_test_stubs_current":
+ case android.SdkTest.JavaLibraryName(ctx.Config()):
return javaSystem, true
}
@@ -1898,6 +2008,7 @@
sdkLinkType, _ := j.getSdkLinkType(ctx, ctx.ModuleName())
+ j.collectTransitiveHeaderJars(ctx)
ctx.VisitDirectDeps(func(module android.Module) {
otherName := ctx.OtherModuleName(module)
tag := ctx.OtherModuleDependencyTag(module)
@@ -1913,7 +2024,7 @@
if dep, ok := module.(SdkLibraryDependency); ok {
switch tag {
- case libTag:
+ case sdkLibTag, libTag:
depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))
deps.classpath = append(deps.classpath, depHeaderJars...)
deps.dexClasspath = append(deps.dexClasspath, depHeaderJars...)
@@ -1933,7 +2044,7 @@
switch tag {
case bootClasspathTag:
deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
- case libTag, instrumentationForTag:
+ case sdkLibTag, libTag, instrumentationForTag:
if _, ok := module.(*Plugin); ok {
ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a libs dependency", otherName)
}
@@ -2006,7 +2117,7 @@
}
} else if dep, ok := module.(android.SourceFileProducer); ok {
switch tag {
- case libTag:
+ case sdkLibTag, libTag:
checkProducesJars(ctx, dep)
deps.classpath = append(deps.classpath, dep.Srcs()...)
deps.dexClasspath = append(deps.classpath, dep.Srcs()...)
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 6bd98d4..108fdd4 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -229,7 +229,6 @@
type BootclasspathFragmentModule struct {
android.ModuleBase
android.ApexModuleBase
- android.SdkBase
ClasspathFragmentBase
// True if this fragment is for testing purposes.
@@ -242,10 +241,8 @@
// Collect the module directory for IDE info in java/jdeps.go.
modulePaths []string
- // Installs for on-device boot image files. This list has entries only if the installs should be
- // handled by Make (e.g., the boot image should be installed on the system partition, rather than
- // in the APEX).
- bootImageDeviceInstalls []dexpreopterInstall
+ // Path to the boot image profile.
+ profilePath android.Path
}
// commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
@@ -265,23 +262,22 @@
// If it could not create the files then it will return nil. Otherwise, it will return a map from
// android.ArchType to the predefined paths of the boot image files.
produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs
+
+ // getImageName returns the `image_name` property of this fragment.
+ getImageName() *string
+
+ // getProfilePath returns the path to the boot image profile.
+ getProfilePath() android.Path
}
var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
-// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files.
-//
-// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot
-// image is created.
-type bootImageFilesByArch map[android.ArchType]android.Paths
-
func bootclasspathFragmentFactory() android.Module {
m := &BootclasspathFragmentModule{}
m.AddProperties(&m.properties, &m.sourceOnlyProperties)
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
@@ -332,19 +328,6 @@
return
}
- // TODO(b/177892522): Prebuilts (versioned or not) should not use the image_name property.
- if android.IsModuleInVersionedSdk(m) {
- // The module is a versioned prebuilt so ignore it. This is done for a couple of reasons:
- // 1. There is no way to use this at the moment so ignoring it is safe.
- // 2. Attempting to initialize the contents property from the configuration will end up having
- // the versioned prebuilt depending on the unversioned prebuilt. That will cause problems
- // as the unversioned prebuilt could end up with an APEX variant created for the source
- // APEX which will prevent it from having an APEX variant for the prebuilt APEX which in
- // turn will prevent it from accessing the dex implementation jar from that which will
- // break hidden API processing, amongst others.
- return
- }
-
// Get the configuration for the art apex jars. Do not use getImageConfig(ctx) here as this is
// too early in the Soong processing for that to work.
global := dexpreopt.GetGlobalConfig(ctx)
@@ -383,19 +366,6 @@
func (b *BootclasspathFragmentModule) bootclasspathImageNameContentsConsistencyCheck(ctx android.BaseModuleContext) {
imageName := proptools.String(b.properties.Image_name)
if imageName == "art" {
- // TODO(b/177892522): Prebuilts (versioned or not) should not use the image_name property.
- if android.IsModuleInVersionedSdk(b) {
- // The module is a versioned prebuilt so ignore it. This is done for a couple of reasons:
- // 1. There is no way to use this at the moment so ignoring it is safe.
- // 2. Attempting to initialize the contents property from the configuration will end up having
- // the versioned prebuilt depending on the unversioned prebuilt. That will cause problems
- // as the unversioned prebuilt could end up with an APEX variant created for the source
- // APEX which will prevent it from having an APEX variant for the prebuilt APEX which in
- // turn will prevent it from accessing the dex implementation jar from that which will
- // break hidden API processing, amongst others.
- return
- }
-
// Get the configuration for the art apex jars.
modules := b.getImageConfig(ctx).modules
configuredJars := modules.CopyOfJars()
@@ -423,12 +393,6 @@
// set image_name: "art".
modules android.ConfiguredJarList
- // Map from arch type to the boot image files.
- bootImageFilesByArch bootImageFilesByArch
-
- // True if the boot image should be installed in the APEX.
- shouldInstallBootImageInApex bool
-
// Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the
// hidden API encoded dex jar path.
contentModuleDexJarPaths bootDexJarByModule
@@ -445,18 +409,6 @@
return i.modules
}
-// Get a map from ArchType to the associated boot image's contents for Android.
-//
-// Extension boot images only return their own files, not the files of the boot images they extend.
-func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() bootImageFilesByArch {
- return i.bootImageFilesByArch
-}
-
-// Return true if the boot image should be installed in the APEX.
-func (i *BootclasspathFragmentApexContentInfo) ShouldInstallBootImageInApex() bool {
- return i.shouldInstallBootImageInApex
-}
-
// DexBootJarPathForContentModule returns the path to the dex boot jar for specified module.
//
// The dex boot jar is one which has had hidden API encoding performed on it.
@@ -468,7 +420,7 @@
return dexJar, nil
} else {
return nil, fmt.Errorf("unknown bootclasspath_fragment content module %s, expected one of %s",
- name, strings.Join(android.SortedStringKeys(i.contentModuleDexJarPaths), ", "))
+ name, strings.Join(android.SortedKeys(i.contentModuleDexJarPaths), ", "))
}
}
@@ -527,12 +479,14 @@
for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
// Add a dependency onto a possibly scope specific stub library.
scopeSpecificDependency := apiScope.scopeSpecificStubModule(ctx, additionalStubModule)
+ // Use JavaApiLibraryName function to be redirected to stubs generated from .txt if applicable
+ scopeSpecificDependency = android.JavaApiLibraryName(ctx.Config(), scopeSpecificDependency)
tag := hiddenAPIStubsDependencyTag{apiScope: apiScope, fromAdditionalDependency: true}
ctx.AddVariationDependencies(nil, tag, scopeSpecificDependency)
}
}
- if SkipDexpreoptBootJars(ctx) {
+ if !dexpreopt.IsDex2oatNeeded(ctx) {
return
}
@@ -575,57 +529,30 @@
// prebuilt which will not use the image config.
imageConfig := b.getImageConfig(ctx)
- // A versioned prebuilt_bootclasspath_fragment cannot and does not need to perform hidden API
- // processing. It cannot do it because it is not part of a prebuilt_apex and so has no access to
- // the correct dex implementation jar. It does not need to because the platform-bootclasspath
- // always references the latest bootclasspath_fragments.
- if !android.IsModuleInVersionedSdk(ctx.Module()) {
- // Perform hidden API processing.
- hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
+ // Perform hidden API processing.
+ hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
- var bootImageFiles bootImageOutputs
- if imageConfig != nil {
- // Delegate the production of the boot image files to a module type specific method.
- common := ctx.Module().(commonBootclasspathFragment)
- bootImageFiles = common.produceBootImageFiles(ctx, imageConfig)
+ var bootImageFiles bootImageOutputs
+ if imageConfig != nil {
+ // Delegate the production of the boot image files to a module type specific method.
+ common := ctx.Module().(commonBootclasspathFragment)
+ bootImageFiles = common.produceBootImageFiles(ctx, imageConfig)
+ b.profilePath = bootImageFiles.profile
- if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
- // Zip the boot image files up, if available. This will generate the zip file in a
- // predefined location.
- buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFiles.byArch)
+ if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+ // Zip the boot image files up, if available. This will generate the zip file in a
+ // predefined location.
+ buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFiles.byArch)
- // Copy the dex jars of this fragment's content modules to their predefined locations.
- copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
- }
-
- for _, variant := range bootImageFiles.variants {
- archType := variant.config.target.Arch.ArchType
- arch := archType.String()
- for _, install := range variant.deviceInstalls {
- // 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)
- installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
-
- b.bootImageDeviceInstalls = append(b.bootImageDeviceInstalls, dexpreopterInstall{
- name: arch + "-" + installBase,
- moduleName: b.Name(),
- outputPathOnHost: install.From,
- installDirOnDevice: installPath,
- installFileOnDevice: installBase,
- })
- }
- }
+ // Copy the dex jars of this fragment's content modules to their predefined locations.
+ copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
}
+ }
- // A prebuilt fragment cannot contribute to an apex.
- if !android.IsModulePrebuilt(ctx.Module()) {
- // Provide the apex content info.
- b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFiles)
- }
- } else {
- // Versioned fragments are not needed by make.
- b.HideFromMake()
+ // A prebuilt fragment cannot contribute to an apex.
+ if !android.IsModulePrebuilt(ctx.Module()) {
+ // Provide the apex content info.
+ b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFiles)
}
// In order for information about bootclasspath_fragment modules to be added to module-info.json
@@ -678,12 +605,8 @@
info.profilePathOnHost = bootImageFiles.profile
info.profileInstallPathInApex = imageConfig.profileInstallPathInApex
}
-
- info.shouldInstallBootImageInApex = imageConfig.shouldInstallInApex()
}
- info.bootImageFilesByArch = bootImageFiles.byArch
-
// Make the apex content info available for other modules.
ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info)
}
@@ -717,11 +640,9 @@
// This is an exception to support end-to-end test for SdkExtensions, until such support exists.
if android.InList("test_framework-sdkextensions", possibleUpdatableModules) {
jars = jars.Append("com.android.sdkext", "test_framework-sdkextensions")
- } else if android.InList("AddNewActivity", possibleUpdatableModules) {
- jars = jars.Append("test_com.android.cts.frameworkresapkplits", "AddNewActivity")
} else if android.InList("test_framework-apexd", possibleUpdatableModules) {
jars = jars.Append("com.android.apex.test_package", "test_framework-apexd")
- } else if global.ApexBootJars.Len() != 0 && !android.IsModuleInVersionedSdk(ctx.Module()) {
+ } else if global.ApexBootJars.Len() != 0 {
unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents)
_, unknown = android.RemoveFromList("core-icu4j", unknown)
// This module only exists in car products.
@@ -748,7 +669,7 @@
imageName := *imageNamePtr
imageConfig := imageConfigs[imageName]
if imageConfig == nil {
- ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedStringKeys(imageConfigs), ", "))
+ ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedKeys(imageConfigs), ", "))
return nil
}
return imageConfig
@@ -938,10 +859,6 @@
// produceBootImageFiles builds the boot image files from the source if it is required.
func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
- if SkipDexpreoptBootJars(ctx) {
- return bootImageOutputs{}
- }
-
// Only generate the boot image if the configuration does not skip it.
return b.generateBootImageBuildActions(ctx, imageConfig)
}
@@ -963,14 +880,16 @@
return bootImageOutputs{}
}
- // Bootclasspath fragment modules that are versioned do not produce a boot image.
- if android.IsModuleInVersionedSdk(ctx.Module()) {
- return bootImageOutputs{}
- }
-
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
+ // If dexpreopt of boot image jars should be skipped, generate only a profile.
+ if SkipDexpreoptBootJars(ctx) {
+ return bootImageOutputs{
+ profile: profile,
+ }
+ }
+
// Build boot image files for the host variants.
buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
@@ -999,19 +918,15 @@
},
},
}}
- for _, install := range b.bootImageDeviceInstalls {
- entriesList = append(entriesList, install.ToMakeEntries())
- }
return entriesList
}
-// Returns the names of all Make modules that handle the installation of the boot image.
-func (b *BootclasspathFragmentModule) BootImageDeviceInstallMakeModules() []string {
- var makeModules []string
- for _, install := range b.bootImageDeviceInstalls {
- makeModules = append(makeModules, install.FullModuleName())
- }
- return makeModules
+func (b *BootclasspathFragmentModule) getImageName() *string {
+ return b.properties.Image_name
+}
+
+func (b *BootclasspathFragmentModule) getProfilePath() android.Path {
+ return b.profilePath
}
// Collect information for opening IDE project files in java/jdeps.go.
@@ -1301,59 +1216,25 @@
// built without a profile as the prebuilt modules do not provide a profile.
buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
- 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{}
- bootImageFiles := bootImageOutputs{
- byArch: files,
- profile: profile,
- }
- for _, variant := range imageConfig.apexVariants() {
- arch := variant.target.Arch.ArchType
- bootImageFiles.variants = append(bootImageFiles.variants, bootImageVariantOutputs{
- variant,
- // No device installs needed when installed in APEX.
- nil,
- })
- 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 bootImageFiles
- } else {
- if profile == nil {
- ctx.ModuleErrorf("Unable to produce boot image files: neither boot image files nor profiles exists in the prebuilt apex")
- return bootImageOutputs{}
- }
- // Build boot image files for the android variants from the dex files provided by the contents
- // of this module.
- return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+ if profile == nil && imageConfig.isProfileGuided() {
+ ctx.ModuleErrorf("Unable to produce boot image files: profiles not found in the prebuilt apex")
+ return bootImageOutputs{}
}
+ // Build boot image files for the android variants from the dex files provided by the contents
+ // of this module.
+ return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+}
+
+func (b *PrebuiltBootclasspathFragmentModule) getImageName() *string {
+ return b.properties.Image_name
+}
+
+func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path {
+ return b.profilePath
}
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.
-func createBootImageTag(arch android.ArchType, baseName string) string {
- tag := fmt.Sprintf(".bootimage-%s-%s", arch, baseName)
- return tag
-}
-
// RequiredFilesFromPrebuiltApex returns the list of all files the prebuilt_bootclasspath_fragment
// requires from a prebuilt .apex file.
//
@@ -1367,25 +1248,11 @@
// 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
}
return nil
}
-func apexRootRelativePathToBootImageFile(arch android.ArchType, base string) string {
- return filepath.Join("javalib", arch.String(), base)
-}
-
var _ android.RequiredFilesFromPrebuiltApex = (*PrebuiltBootclasspathFragmentModule)(nil)
func prebuiltBootclasspathFragmentFactory() android.Module {
@@ -1395,7 +1262,6 @@
// array.
android.InitPrebuiltModule(m, &[]string{"placeholder"})
android.InitApexModule(m)
- android.InitSdkAwareModule(m)
android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
// Initialize the contents property from the image_name.
diff --git a/java/builder.go b/java/builder.go
index c0fadd4..4626267 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -83,9 +83,9 @@
_ = pctx.VariableFunc("kytheCuJavaSourceMax",
func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuJavaSourceMax() })
_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
- // Run it with -add-opens=java.base/java.nio=ALL-UNNAMED to avoid JDK9's warning about
- // "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ...
- // to field java.nio.Buffer.address"
+ // Run it with several --add-exports to allow the classes in the
+ // com.google.devtools.kythe.extractors.java.standalone package access the packages in the
+ // jdk.compiler compiler module. Long live Java modules.
kytheExtract = pctx.AndroidStaticRule("kythe",
blueprint.RuleParams{
Command: `${config.ZipSyncCmd} -d $srcJarDir ` +
@@ -97,7 +97,17 @@
`KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
`KYTHE_JAVA_SOURCE_BATCH_SIZE=${kytheCuJavaSourceMax} ` +
`${config.SoongJavacWrapper} ${config.JavaCmd} ` +
+ // Avoid JDK9's warning about "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ...
+ // to field java.nio.Buffer.address"
`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
+ // Allow the classes in the com.google.devtools.kythe.extractors.java.standalone package
+ // access the packages in the jdk.compiler compiler module
+ `--add-opens=java.base/java.nio=ALL-UNNAMED ` +
+ `--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED ` +
+ `--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED ` +
+ `--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED ` +
+ `--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED ` +
+ `--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED ` +
`-jar ${config.JavaKytheExtractorJar} ` +
`${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
@@ -121,13 +131,13 @@
blueprint.RuleParams{
Command: `rm -rf "$out" && ` +
`${config.ExtractApksCmd} -o "${out}" -zip "${zip}" -allow-prereleased=${allow-prereleased} ` +
- `-sdk-version=${sdk-version} -abis=${abis} ` +
+ `-sdk-version=${sdk-version} -skip-sdk-check=${skip-sdk-check} -abis=${abis} ` +
`--screen-densities=${screen-densities} --stem=${stem} ` +
`-apkcerts=${apkcerts} -partition=${partition} ` +
`${in}`,
CommandDeps: []string{"${config.ExtractApksCmd}"},
},
- "abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition", "zip")
+ "abis", "allow-prereleased", "screen-densities", "sdk-version", "skip-sdk-check", "stem", "apkcerts", "partition", "zip")
turbine, turbineRE = pctx.RemoteStaticRules("turbine",
blueprint.RuleParams{
@@ -221,7 +231,7 @@
jetifier = pctx.AndroidStaticRule("jetifier",
blueprint.RuleParams{
- Command: "${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JetifierJar} -l error -o $out -i $in",
+ Command: "${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JetifierJar} -l error -o $out -i $in -t epoch",
CommandDeps: []string{"${config.JavaCmd}", "${config.JetifierJar}"},
},
)
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 259e977..bc9de50 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -111,7 +111,7 @@
ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name)
}
}
- return android.SortedStringKeys(set)
+ return android.SortedKeys(set)
}
// Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType.
@@ -130,17 +130,17 @@
if s, ok := m.(*SdkLibrary); ok {
// TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current"
if s.minSdkVersion.Specified() {
- if s.minSdkVersion.ApiLevel.IsCurrent() {
+ if s.minSdkVersion.IsCurrent() {
jar.minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
} else {
- jar.minSdkVersion = s.minSdkVersion.ApiLevel.String()
+ jar.minSdkVersion = s.minSdkVersion.String()
}
}
if s.maxSdkVersion.Specified() {
- if s.maxSdkVersion.ApiLevel.IsCurrent() {
+ if s.maxSdkVersion.IsCurrent() {
jar.maxSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
} else {
- jar.maxSdkVersion = s.maxSdkVersion.ApiLevel.String()
+ jar.maxSdkVersion = s.maxSdkVersion.String()
}
}
}
diff --git a/java/config/config.go b/java/config/config.go
index 95b841f..b82a137 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -43,6 +43,7 @@
InstrumentFrameworkModules = []string{
"framework",
"framework-minus-apex",
+ "ims-common",
"telephony-common",
"services",
"android.car",
@@ -68,24 +69,40 @@
"-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")
- exportedVars.ExportStringStaticVariable("JavacHeapSize", "2048M")
+ exportedVars.ExportStringStaticVariable("JavacHeapSize", "4096M")
exportedVars.ExportStringStaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
// ErrorProne can use significantly more memory than javac alone, give it a higher heap
// size (b/221480398).
- exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "4096M")
+ exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "8192M")
exportedVars.ExportStringStaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
- exportedVars.ExportStringListStaticVariable("DexFlags", []string{
- `-JXX:OnError="cat hs_err_pid%p.log"`,
- "-JXX:CICompilerCount=6",
- "-JXX:+UseDynamicNumberOfGCThreads",
- })
+ // 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{
+ "-JXmx4096M",
+ "-JXX:+TieredCompilation",
+ "-JXX:TieredStopAtLevel=1",
+ "-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
+ "-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
+ "-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
+ }, dexerJavaVmFlagsList...))
+ exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
+ "-JXmx2048M",
+ "-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
+ "-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
+ "-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
+ }, dexerJavaVmFlagsList...))
exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
`-Xmaxerrs 9999999`,
@@ -116,12 +133,7 @@
if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
return override
}
- switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
- case "true":
- return "17"
- default:
- return "11"
- }
+ return "17"
})
pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
@@ -136,7 +148,7 @@
pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar")
pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
- pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file.py")
+ pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file")
pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
pctx.SourcePathVariable("PackageCheckCmd", "build/soong/scripts/package-check.sh")
@@ -147,7 +159,8 @@
pctx.HostBinToolVariable("ZipSyncCmd", "zipsync")
pctx.HostBinToolVariable("ApiCheckCmd", "apicheck")
pctx.HostBinToolVariable("D8Cmd", "d8")
- pctx.HostBinToolVariable("R8Cmd", "r8-compat-proguard")
+ pctx.HostBinToolVariable("R8Cmd", "r8")
+ pctx.HostBinToolVariable("ResourceShrinkerCmd", "resourceshrinker")
pctx.HostBinToolVariable("HiddenAPICmd", "hiddenapi")
pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
pctx.VariableFunc("TurbineJar", func(ctx android.PackageVarContext) string {
@@ -165,7 +178,7 @@
pctx.HostJavaToolVariable("MetalavaJar", "metalava.jar")
pctx.HostJavaToolVariable("DokkaJar", "dokka.jar")
pctx.HostJavaToolVariable("JetifierJar", "jetifier.jar")
- pctx.HostJavaToolVariable("R8Jar", "r8-compat-proguard.jar")
+ pctx.HostJavaToolVariable("R8Jar", "r8.jar")
pctx.HostJavaToolVariable("D8Jar", "d8.jar")
pctx.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
@@ -188,6 +201,7 @@
pctx.HostBinToolVariable("ManifestMergerCmd", "manifest-merger")
pctx.HostBinToolVariable("Class2NonSdkList", "class2nonsdklist")
+ pctx.HostBinToolVariable("MergeCsvCommand", "merge_csv")
pctx.HostBinToolVariable("HiddenAPI", "hiddenapi")
hostBinToolVariableWithSdkToolsPrebuilt("Aapt2Cmd", "aapt2")
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..958f4ce 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -63,11 +63,6 @@
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
- // This one is not on device but it's needed when javac compiles code
- // containing @Generated annotations produced by some code generation
- // tools.
- // See http://b/123891440.
- "core-generated-annotation-stubs",
],
sdk_version: "none",
system_modules: "none",
@@ -84,12 +79,44 @@
],
}
+// Defaults module to strip out android annotations
+java_defaults {
+ name: "system-modules-no-annotations",
+ sdk_version: "none",
+ system_modules: "none",
+ jarjar_rules: "jarjar-strip-annotations-rules.txt",
+}
+
+// Same as core-current-stubs-for-system-modules, but android annotations are
+// stripped.
+java_library {
+ name: "core-current-stubs-for-system-modules-no-annotations",
+ visibility: ["//development/sdk"],
+ defaults: [
+ "system-modules-no-annotations",
+ ],
+ static_libs: [
+ "core-current-stubs-for-system-modules",
+ ],
+ dists: [
+ {
+ // Legacy dist location for the public file.
+ dest: "core-for-system-modules-no-annotations.jar",
+ targets: dist_targets,
+ },
+ {
+ dest: "system-modules/public/core-for-system-modules-no-annotations.jar",
+ targets: dist_targets,
+ },
+ ],
+}
+
// Used when compiling higher-level code against core.current.stubs.
java_system_modules {
name: "core-public-stubs-system-modules",
visibility: ["//visibility:public"],
libs: [
- "core-current-stubs-for-system-modules",
+ "core-current-stubs-for-system-modules-no-annotations",
],
}
@@ -124,11 +151,6 @@
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
- // This one is not on device but it's needed when javac compiles code
- // containing @Generated annotations produced by some code generation
- // tools.
- // See http://b/123891440.
- "core-generated-annotation-stubs",
],
sdk_version: "none",
system_modules: "none",
@@ -138,11 +160,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"],
+ defaults: [
+ "system-modules-no-annotations",
+ ],
+ static_libs: [
+ "core-module-lib-stubs-for-system-modules",
+ ],
+ dist: {
+ dest: "system-modules/module-lib/core-for-system-modules-no-annotations.jar",
+ targets: dist_targets,
+ },
+}
+
// 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 +214,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,
+ defaults: [
+ "system-modules-no-annotations",
+ ],
+ hostdex: true,
+ compile_dex: true,
+
+ static_libs: [
+ "legacy.core.platform.api.stubs",
+ ],
+ patch_module: "java.base",
+}
+
java_library {
name: "stable.core.platform.api.stubs",
visibility: core_platform_visibility,
@@ -191,20 +249,33 @@
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,
+ defaults: [
+ "system-modules-no-annotations",
+ ],
+ hostdex: true,
+ compile_dex: true,
+
+ static_libs: [
+ "stable.core.platform.api.stubs",
+ ],
+ patch_module: "java.base",
+}
+
// 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",
- // This one is not on device but it's needed when javac compiles code
- // containing @Generated annotations produced by some code generation
- // tools.
- // See http://b/123891440.
- "core-generated-annotation-stubs",
],
}
@@ -212,14 +283,75 @@
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",
- // This one is not on device but it's needed when javac compiles code
- // containing @Generated annotations produced by some code generation
- // tools.
- // See http://b/123891440.
- "core-generated-annotation-stubs",
],
}
+
+// Used when compiling higher-level code against art.module.public.api.stubs.
+// This abstraction should come from the inner tree linking against the stubs
+// and not from an "sdk", since parts of this abstraction do not belong to an
+// official API (e.g. stub-annotations).
+//
+// This is only intended for use within core libraries and must not be used
+// from outside.
+java_system_modules {
+ name: "art-module-public-api-stubs-system-modules",
+ visibility: [
+ "//art/build/sdk",
+ "//external/conscrypt",
+ "//external/icu/android_icu4j",
+ "//external/wycheproof",
+ ],
+ libs: [
+ "art.module.public.api.stubs",
+ // This one is not on device but it's needed when javac compiles code
+ // containing lambdas.
+ "core-lambda-stubs-for-system-modules",
+
+ // Ensure that core libraries that depend on the public API can access
+ // the UnsupportedAppUsage, CorePlatformApi and IntraCoreApi
+ // annotations.
+ "art.module.api.annotations.for.system.modules",
+ ],
+}
+
+// Used when compiling higher-level code against art.module.public.api.stubs.module_lib.
+//
+// This is only intended for use within core libraries and must not be used
+// from outside.
+java_system_modules {
+ name: "art-module-lib-api-stubs-system-modules",
+ visibility: [
+ "//art/build/sdk",
+ "//external/conscrypt",
+ "//external/icu/android_icu4j",
+ ],
+ libs: [
+ "art.module.public.api.stubs.module_lib",
+ ],
+}
+
+// Used when compiling against art.module.intra.core.api.stubs.
+java_system_modules {
+ name: "art-module-intra-core-api-stubs-system-modules",
+ visibility: [
+ "//art/build/sdk",
+ "//external/bouncycastle",
+ "//external/conscrypt",
+ "//external/icu/android_icu4j",
+ ],
+ libs: [
+ // The intra core API stubs library.
+ "art.module.intra.core.api.stubs",
+
+ // Additional classes needed by javac but which are not present in the stubs.
+ "art-module-intra-core-api-stubs-system-modules-lib",
+ ],
+}
+
+build = [
+ "TxtStubLibraries.bp",
+]
diff --git a/java/core-libraries/TxtStubLibraries.bp b/java/core-libraries/TxtStubLibraries.bp
new file mode 100644
index 0000000..813187e
--- /dev/null
+++ b/java/core-libraries/TxtStubLibraries.bp
@@ -0,0 +1,156 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file contains java_system_modules provided by the SDK.
+// These system modules transitively depend on core stub libraries generated from .txt files.
+
+// Same as core-public-stubs-system-modules, but the stubs are generated from .txt files
+java_system_modules {
+ name: "core-public-stubs-system-modules.from-text",
+ visibility: ["//visibility:public"],
+ libs: [
+ "core-current-stubs-for-system-modules-no-annotations.from-text",
+ ],
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
+
+java_library {
+ name: "core-current-stubs-for-system-modules-no-annotations.from-text",
+ visibility: ["//visibility:private"],
+ defaults: [
+ "system-modules-no-annotations",
+ ],
+ static_libs: [
+ "core.current.stubs.from-text",
+ "core-lambda-stubs.from-text",
+ ],
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
+
+// Same as core-module-lib-stubs-system-modules, but the stubs are generated from .txt files
+java_system_modules {
+ name: "core-module-lib-stubs-system-modules.from-text",
+ visibility: ["//visibility:public"],
+ libs: [
+ "core-module-lib-stubs-for-system-modules-no-annotations.from-text",
+ ],
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
+
+java_library {
+ name: "core-module-lib-stubs-for-system-modules-no-annotations.from-text",
+ visibility: ["//visibility:private"],
+ defaults: [
+ "system-modules-no-annotations",
+ ],
+ static_libs: [
+ "core.module_lib.stubs.from-text",
+ "core-lambda-stubs.from-text",
+ ],
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
+
+java_library {
+ name: "core.module_lib.stubs.from-text",
+ static_libs: [
+ "art.module.public.api.stubs.module_lib.from-text",
+
+ // Replace the following with the module-lib correspondence when Conscrypt or i18N module
+ // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides
+ // @SystemApi(MODULE_LIBRARIES).
+ "conscrypt.module.public.api.stubs.from-text",
+ "i18n.module.public.api.stubs.from-text",
+ ],
+ sdk_version: "none",
+ system_modules: "none",
+ visibility: ["//visibility:private"],
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
+
+// Same as legacy-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files
+java_system_modules {
+ name: "legacy-core-platform-api-stubs-system-modules.from-text",
+ visibility: core_platform_visibility,
+ libs: [
+ "legacy.core.platform.api.no.annotations.stubs.from-text",
+ "core-lambda-stubs.from-text",
+ ],
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
+
+java_library {
+ name: "legacy.core.platform.api.no.annotations.stubs.from-text",
+ visibility: core_platform_visibility,
+ defaults: [
+ "system-modules-no-annotations",
+ ],
+ hostdex: true,
+ compile_dex: true,
+
+ static_libs: [
+ "legacy.core.platform.api.stubs.from-text",
+ ],
+ patch_module: "java.base",
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
+
+// Same as stable-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files
+java_system_modules {
+ name: "stable-core-platform-api-stubs-system-modules.from-text",
+ visibility: core_platform_visibility,
+ libs: [
+ "stable.core.platform.api.no.annotations.stubs.from-text",
+ "core-lambda-stubs.from-text",
+ ],
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
+
+java_library {
+ name: "stable.core.platform.api.no.annotations.stubs.from-text",
+ visibility: core_platform_visibility,
+ defaults: [
+ "system-modules-no-annotations",
+ ],
+ hostdex: true,
+ compile_dex: true,
+
+ static_libs: [
+ "stable.core.platform.api.stubs.from-text",
+ ],
+ patch_module: "java.base",
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
+
+java_api_library {
+ name: "core-lambda-stubs.from-text",
+ api_surface: "toolchain",
+ api_contributions: [
+ "art.module.toolchain.api.api.contribution",
+ ],
+ libs: [
+ // LambdaMetaFactory depends on CallSite etc. which is part of the Core API surface
+ "core.current.stubs.from-text",
+ ],
+ // TODO: Enable after stub generation from .txt file is available
+ enabled: false,
+}
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/device_host_converter.go b/java/device_host_converter.go
index 4abdcc6..3581040 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -19,12 +19,16 @@
"io"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/dexpreopt"
+
+ "github.com/google/blueprint/proptools"
)
type DeviceHostConverter struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.BazelModuleBase
properties DeviceHostConverterProperties
@@ -76,6 +80,7 @@
module.AddProperties(&module.properties)
InitJavaModule(module, android.DeviceSupported)
+ android.InitBazelModule(module)
return module
}
@@ -186,3 +191,32 @@
},
}
}
+
+type bazelDeviceHostConverterAttributes struct {
+ Exports bazel.LabelListAttribute
+}
+
+func (d *DeviceHostConverter) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "java_host_for_device",
+ Bzl_load_location: "//build/bazel/rules/java:host_for_device.bzl",
+ },
+ android.CommonAttributes{Name: d.Name()},
+ &bazelDeviceHostConverterAttributes{
+ Exports: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, d.properties.Libs)),
+ },
+ )
+ neverLinkAttrs := &javaLibraryAttributes{
+ Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + d.Name()}),
+ Neverlink: bazel.BoolAttribute{Value: proptools.BoolPtr(true)},
+ javaCommonAttributes: &javaCommonAttributes{
+ Sdk_version: bazel.StringAttribute{Value: proptools.StringPtr("none")},
+ },
+ }
+ ctx.CreateBazelTargetModule(
+ javaLibraryBazelTargetModuleProperties(),
+ android.CommonAttributes{Name: d.Name() + "-neverlink"},
+ neverLinkAttrs)
+
+}
diff --git a/java/dex.go b/java/dex.go
index 1638376..4d6aa34 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"`
@@ -63,6 +63,9 @@
// classes referenced by the app manifest. Defaults to false.
No_aapt_flags *bool
+ // If true, optimize for size by removing unused resources. Defaults to false.
+ Shrink_resources *bool
+
// Flags to pass to proguard.
Proguard_flags []string
@@ -86,7 +89,10 @@
// list of extra proguard flag files
extraProguardFlagFiles android.Paths
proguardDictionary android.OptionalPath
+ proguardConfiguration android.OptionalPath
proguardUsageZip android.OptionalPath
+
+ providesTransitiveHeaderJars
}
func (d *dexer) effectiveOptimizeEnabled() bool {
@@ -98,7 +104,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{
@@ -127,17 +133,18 @@
var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
+ `rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
`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} ` +
+ `--pg-conf-output ${outConfig} ` +
`-printusage ${outUsage} ` +
`--deps-file ${out}.d ` +
`$r8Flags && ` +
- `touch "${outDict}" "${outUsage}" && ` +
+ `touch "${outDict}" "${outConfig}" "${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" && ` +
@@ -173,11 +180,11 @@
ExecStrategy: "${config.RER8ExecStrategy}",
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
},
- }, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
+ }, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
"r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"})
func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
- minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) {
+ dexParams *compileDexParams) (flags []string, deps android.Paths) {
flags = d.dexProperties.Dxflags
// Translate all the DX flags to D8 ones until all the build files have been migrated
@@ -200,7 +207,17 @@
"--verbose")
}
- effectiveVersion, err := minSdkVersion.EffectiveVersion(ctx)
+ // Supplying the platform build flag disables various features like API modeling and desugaring.
+ // For targets with a stable min SDK version (i.e., when the min SDK is both explicitly specified
+ // and managed+versioned), we suppress this flag to ensure portability.
+ // Note: Targets with a min SDK kind of core_platform (e.g., framework.jar) or unspecified (e.g.,
+ // services.jar), are not classified as stable, which is WAI.
+ // TODO(b/232073181): Expand to additional min SDK cases after validation.
+ if !dexParams.sdkVersion.Stable() {
+ flags = append(flags, "--android-platform-build")
+ }
+
+ effectiveVersion, err := dexParams.minSdkVersion.EffectiveVersion(ctx)
if err != nil {
ctx.PropertyErrorf("min_sdk_version", "%s", err)
}
@@ -236,13 +253,32 @@
})
r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
- r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
- r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
-
r8Deps = append(r8Deps, proguardRaiseDeps...)
+ r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
r8Deps = append(r8Deps, flags.bootClasspath...)
+ r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
r8Deps = append(r8Deps, flags.dexClasspath...)
+ transitiveStaticLibsLookupMap := map[android.Path]bool{}
+ if d.transitiveStaticLibsHeaderJars != nil {
+ for _, jar := range d.transitiveStaticLibsHeaderJars.ToList() {
+ transitiveStaticLibsLookupMap[jar] = true
+ }
+ }
+ transitiveHeaderJars := android.Paths{}
+ if d.transitiveLibsHeaderJars != nil {
+ for _, jar := range d.transitiveLibsHeaderJars.ToList() {
+ if _, ok := transitiveStaticLibsLookupMap[jar]; ok {
+ // don't include a lib if it is already packaged in the current JAR as a static lib
+ continue
+ }
+ transitiveHeaderJars = append(transitiveHeaderJars, jar)
+ }
+ }
+ transitiveClasspath := classpath(transitiveHeaderJars)
+ r8Flags = append(r8Flags, transitiveClasspath.FormJavaClassPath("-libraryjars"))
+ r8Deps = append(r8Deps, transitiveClasspath...)
+
flagFiles := android.Paths{
android.PathForSource(ctx, "build/make/core/proguard.flags"),
}
@@ -304,20 +340,27 @@
return r8Flags, r8Deps
}
-func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion android.SdkSpec,
- classesJar android.Path, jarName string) android.OutputPath {
+type compileDexParams struct {
+ flags javaBuilderFlags
+ sdkVersion android.SdkSpec
+ minSdkVersion android.ApiLevel
+ classesJar android.Path
+ jarName string
+}
+
+func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) android.OutputPath {
// Compile classes.jar into classes.dex and then javalib.jar
- javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath
+ javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
outDir := android.PathForModuleOut(ctx, "dex")
- tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", jarName)
+ tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", dexParams.jarName)
zipFlags := "--ignore_missing_files"
if proptools.Bool(d.dexProperties.Uncompress_dex) {
zipFlags += " -L 0"
}
- commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion)
+ commonFlags, commonDeps := d.dexCommonFlags(ctx, dexParams)
// Exclude kotlinc generated files when "exclude_kotlinc_generated_files" is set to true.
mergeZipsFlags := ""
@@ -329,18 +372,21 @@
if useR8 {
proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
+ proguardConfiguration := android.PathForModuleOut(ctx, "proguard_configuration")
+ d.proguardConfiguration = android.OptionalPathForPath(proguardConfiguration)
proguardUsageDir := android.PathForModuleOut(ctx, "proguard_usage")
proguardUsage := proguardUsageDir.Join(ctx, ctx.Namespace().Path,
android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip")
d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
- r8Flags, r8Deps := d.r8Flags(ctx, flags)
+ r8Flags, r8Deps := d.r8Flags(ctx, dexParams.flags)
r8Deps = append(r8Deps, commonDeps...)
rule := r8
args := map[string]string{
"r8Flags": strings.Join(append(commonFlags, r8Flags...), " "),
"zipFlags": zipFlags,
"outDict": proguardDictionary.String(),
+ "outConfig": proguardConfiguration.String(),
"outUsageDir": proguardUsageDir.String(),
"outUsage": proguardUsage.String(),
"outUsageZip": proguardUsageZip.String(),
@@ -357,12 +403,12 @@
Description: "r8",
Output: javalibJar,
ImplicitOutputs: android.WritablePaths{proguardDictionary, proguardUsageZip},
- Input: classesJar,
+ Input: dexParams.classesJar,
Implicits: r8Deps,
Args: args,
})
} else {
- d8Flags, d8Deps := d8Flags(flags)
+ d8Flags, d8Deps := d8Flags(dexParams.flags)
d8Deps = append(d8Deps, commonDeps...)
rule := d8
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
@@ -372,7 +418,7 @@
Rule: rule,
Description: "d8",
Output: javalibJar,
- Input: classesJar,
+ Input: dexParams.classesJar,
Implicits: d8Deps,
Args: map[string]string{
"d8Flags": strings.Join(append(commonFlags, d8Flags...), " "),
@@ -384,7 +430,7 @@
})
}
if proptools.Bool(d.dexProperties.Uncompress_dex) {
- alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName).OutputPath
+ alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", dexParams.jarName).OutputPath
TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
javalibJar = alignedJavalibJar
}
diff --git a/java/dex_test.go b/java/dex_test.go
index a3e2ded..2ba3831 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -18,10 +18,12 @@
"testing"
"android/soong/android"
+
+ "github.com/google/blueprint/proptools"
)
func TestR8(t *testing.T) {
- result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
android_app {
name: "app",
srcs: ["foo.java"],
@@ -30,6 +32,20 @@
platform_apis: true,
}
+ android_app {
+ name: "stable_app",
+ srcs: ["foo.java"],
+ sdk_version: "current",
+ min_sdk_version: "31",
+ }
+
+ android_app {
+ name: "core_platform_app",
+ srcs: ["foo.java"],
+ sdk_version: "core_platform",
+ min_sdk_version: "31",
+ }
+
java_library {
name: "lib",
srcs: ["foo.java"],
@@ -42,11 +58,15 @@
`)
app := result.ModuleForTests("app", "android_common")
+ stableApp := result.ModuleForTests("stable_app", "android_common")
+ corePlatformApp := result.ModuleForTests("core_platform_app", "android_common")
lib := result.ModuleForTests("lib", "android_common")
staticLib := result.ModuleForTests("static_lib", "android_common")
appJavac := app.Rule("javac")
appR8 := app.Rule("r8")
+ stableAppR8 := stableApp.Rule("r8")
+ corePlatformAppR8 := corePlatformApp.Rule("r8")
libHeader := lib.Output("turbine-combined/lib.jar").Output
staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
@@ -57,14 +77,188 @@
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",
+ android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
appR8.Args["r8Flags"], staticLibHeader.String())
android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
appR8.Args["r8Flags"], "-ignorewarnings")
+ android.AssertStringDoesContain(t, "expected --android-platform-build in app r8 flags",
+ appR8.Args["r8Flags"], "--android-platform-build")
+ android.AssertStringDoesNotContain(t, "expected no --android-platform-build in stable_app r8 flags",
+ stableAppR8.Args["r8Flags"], "--android-platform-build")
+ android.AssertStringDoesContain(t, "expected --android-platform-build in core_platform_app r8 flags",
+ corePlatformAppR8.Args["r8Flags"], "--android-platform-build")
+}
+
+func TestR8TransitiveDeps(t *testing.T) {
+ bp := `
+ override_android_app {
+ name: "override_app",
+ base: "app",
+ }
+
+ android_app {
+ name: "app",
+ srcs: ["foo.java"],
+ libs: [
+ "lib",
+ "uses_libs_dep_import",
+ ],
+ static_libs: [
+ "static_lib",
+ "repeated_dep",
+ ],
+ platform_apis: true,
+ }
+
+ java_library {
+ name: "static_lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "lib",
+ libs: [
+ "transitive_lib",
+ "repeated_dep",
+ "prebuilt_lib",
+ ],
+ static_libs: ["transitive_static_lib"],
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "repeated_dep",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "transitive_static_lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "transitive_lib",
+ srcs: ["foo.java"],
+ libs: ["transitive_lib_2"],
+ }
+
+ java_library {
+ name: "transitive_lib_2",
+ srcs: ["foo.java"],
+ }
+
+ java_import {
+ name: "lib",
+ jars: ["lib.jar"],
+ }
+
+ java_library {
+ name: "uses_lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "optional_uses_lib",
+ srcs: ["foo.java"],
+ }
+
+ android_library {
+ name: "uses_libs_dep",
+ uses_libs: ["uses_lib"],
+ optional_uses_libs: ["optional_uses_lib"],
+ }
+
+ android_library_import {
+ name: "uses_libs_dep_import",
+ aars: ["aar.aar"],
+ static_libs: ["uses_libs_dep"],
+ }
+ `
+
+ testcases := []struct {
+ name string
+ unbundled bool
+ }{
+ {
+ name: "non-unbundled build",
+ unbundled: false,
+ },
+ {
+ name: "unbundled build",
+ unbundled: true,
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.name, func(t *testing.T) {
+ fixturePreparer := PrepareForTestWithJavaDefaultModules
+ if tc.unbundled {
+ fixturePreparer = android.GroupFixturePreparers(
+ fixturePreparer,
+ android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.Unbundled_build = proptools.BoolPtr(true)
+ },
+ ),
+ )
+ }
+ result := fixturePreparer.RunTestWithBp(t, bp)
+
+ getHeaderJar := func(name string) android.Path {
+ mod := result.ModuleForTests(name, "android_common")
+ return mod.Output("turbine-combined/" + name + ".jar").Output
+ }
+
+ appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+ overrideAppR8 := result.ModuleForTests("app", "android_common_override_app").Rule("r8")
+ appHeader := getHeaderJar("app")
+ overrideAppHeader := result.ModuleForTests("app", "android_common_override_app").Output("turbine-combined/app.jar").Output
+ libHeader := getHeaderJar("lib")
+ transitiveLibHeader := getHeaderJar("transitive_lib")
+ transitiveLib2Header := getHeaderJar("transitive_lib_2")
+ staticLibHeader := getHeaderJar("static_lib")
+ transitiveStaticLibHeader := getHeaderJar("transitive_static_lib")
+ repeatedDepHeader := getHeaderJar("repeated_dep")
+ usesLibHeader := getHeaderJar("uses_lib")
+ optionalUsesLibHeader := getHeaderJar("optional_uses_lib")
+ prebuiltLibHeader := result.ModuleForTests("prebuilt_lib", "android_common").Output("combined/lib.jar").Output
+
+ for _, rule := range []android.TestingBuildParams{appR8, overrideAppR8} {
+ android.AssertStringDoesNotContain(t, "expected no app header jar in app r8 classpath",
+ rule.Args["r8Flags"], appHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no override_app header jar in app r8 classpath",
+ rule.Args["r8Flags"], overrideAppHeader.String())
+ android.AssertStringDoesContain(t, "expected transitive lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], transitiveLibHeader.String())
+ android.AssertStringDoesContain(t, "expected transitive lib ^2 header jar in app r8 classpath",
+ rule.Args["r8Flags"], transitiveLib2Header.String())
+ android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], libHeader.String())
+ android.AssertStringDoesContain(t, "expected uses_lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], usesLibHeader.String())
+ android.AssertStringDoesContain(t, "expected optional_uses_lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], optionalUsesLibHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], staticLibHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no transitive static_lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], transitiveStaticLibHeader.String())
+ // we shouldn't list this dep because it is already included as static_libs in the app
+ android.AssertStringDoesNotContain(t, "expected no repeated_dep header jar in app r8 classpath",
+ rule.Args["r8Flags"], repeatedDepHeader.String())
+ // skip a prebuilt transitive dep if the source is also a transitive dep
+ android.AssertStringDoesNotContain(t, "expected no prebuilt header jar in app r8 classpath",
+ rule.Args["r8Flags"], prebuiltLibHeader.String())
+ android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
+ rule.Args["r8Flags"], "-ignorewarnings")
+ android.AssertStringDoesContain(t, "expected --android-platform-build in app r8 flags",
+ rule.Args["r8Flags"], "--android-platform-build")
+ }
+ })
+ }
}
func TestR8Flags(t *testing.T) {
- result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
android_app {
name: "app",
srcs: ["foo.java"],
@@ -88,11 +282,12 @@
appR8.Args["r8Flags"], "-dontobfuscate")
android.AssertStringDoesNotContain(t, "expected no -ignorewarnings in app r8 flags",
appR8.Args["r8Flags"], "-ignorewarnings")
-
+ android.AssertStringDoesContain(t, "expected --android-platform-build in app r8 flags",
+ appR8.Args["r8Flags"], "--android-platform-build")
}
func TestD8(t *testing.T) {
- result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["foo.java"],
@@ -131,3 +326,57 @@
android.AssertStringDoesNotContain(t, "expected no static_lib header jar in foo javac classpath",
fooD8.Args["d8Flags"], staticLibHeader.String())
}
+
+func TestProguardFlagsInheritance(t *testing.T) {
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
+ android_app {
+ name: "app",
+ static_libs: [
+ "primary_android_lib",
+ "primary_lib",
+ ],
+ platform_apis: true,
+ }
+
+ java_library {
+ name: "primary_lib",
+ optimize: {
+ proguard_flags_files: ["primary.flags"],
+ },
+ }
+
+ android_library {
+ name: "primary_android_lib",
+ static_libs: ["secondary_lib"],
+ optimize: {
+ proguard_flags_files: ["primary_android.flags"],
+ },
+ }
+
+ java_library {
+ name: "secondary_lib",
+ static_libs: ["tertiary_lib"],
+ optimize: {
+ proguard_flags_files: ["secondary.flags"],
+ },
+ }
+
+ java_library {
+ name: "tertiary_lib",
+ optimize: {
+ proguard_flags_files: ["tertiary.flags"],
+ },
+ }
+ `)
+
+ app := result.ModuleForTests("app", "android_common")
+ appR8 := app.Rule("r8")
+ android.AssertStringDoesContain(t, "expected primary_lib's proguard flags from direct dep",
+ appR8.Args["r8Flags"], "primary.flags")
+ android.AssertStringDoesContain(t, "expected primary_android_lib's proguard flags from direct dep",
+ appR8.Args["r8Flags"], "primary_android.flags")
+ android.AssertStringDoesContain(t, "expected secondary_lib's proguard flags from inherited dep",
+ appR8.Args["r8Flags"], "secondary.flags")
+ android.AssertStringDoesContain(t, "expected tertiary_lib's proguard flags from inherited dep",
+ appR8.Args["r8Flags"], "tertiary.flags")
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 7c5f055..a96b312 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -23,10 +23,24 @@
)
type DexpreopterInterface interface {
- IsInstallable() bool // Structs that embed dexpreopter must implement this.
+ // True if the java module is to be dexed and installed on devices.
+ // Structs that embed dexpreopter must implement this.
+ IsInstallable() bool
+
+ // True if dexpreopt is disabled for the java module.
dexpreoptDisabled(ctx android.BaseModuleContext) bool
+
+ // If the java module is to be installed into an APEX, this list contains information about the
+ // dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
+ // outside of the APEX.
DexpreoptBuiltInstalledForApex() []dexpreopterInstall
+
+ // The Make entries to install the dexpreopt outputs. Derived from
+ // `DexpreoptBuiltInstalledForApex`.
AndroidMkEntriesForApex() []android.AndroidMkEntries
+
+ // See `dexpreopter.outputProfilePathOnHost`.
+ OutputProfilePathOnHost() android.Path
}
type dexpreopterInstall struct {
@@ -77,7 +91,8 @@
}
type dexpreopter struct {
- dexpreoptProperties DexpreoptProperties
+ dexpreoptProperties DexpreoptProperties
+ importDexpreoptProperties ImportDexpreoptProperties
installPath android.InstallPath
uncompressedDex bool
@@ -103,6 +118,14 @@
// - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
// dexpreopt another partition).
configPath android.WritablePath
+
+ // The path to the profile on host that dexpreopter generates. This is used as the input for
+ // dex2oat.
+ outputProfilePathOnHost android.Path
+
+ // The path to the profile that dexpreopter accepts. It must be in the binary format. If this is
+ // set, it overrides the profile settings in `dexpreoptProperties`.
+ inputProfilePathOnHost android.Path
}
type DexpreoptProperties struct {
@@ -123,6 +146,18 @@
// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
Profile *string `android:"path"`
}
+
+ Dex_preopt_result struct {
+ // True if profile-guided optimization is actually enabled.
+ Profile_guided bool
+ } `blueprint:"mutated"`
+}
+
+type ImportDexpreoptProperties struct {
+ Dex_preopt struct {
+ // If true, use the profile in the prebuilt APEX to guide optimization. Defaults to false.
+ Profile_guided *bool
+ }
}
func init() {
@@ -145,6 +180,8 @@
return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
}
+// Returns whether dexpreopt is applicable to the module.
+// When it returns true, neither profile nor dexpreopt artifacts will be generated.
func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
if !ctx.Device() {
return true
@@ -170,19 +207,10 @@
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() {
+ // Don't preopt APEX variant module unless the module is an APEX system server jar.
+ if !isApexSystemServerJar {
return true
}
} else {
@@ -198,7 +226,7 @@
}
func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
- if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
+ if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) || !dexpreopt.IsDex2oatNeeded(ctx) {
return
}
dexpreopt.RegisterToolDeps(ctx)
@@ -259,10 +287,12 @@
isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx))
bootImage := defaultBootImageConfig(ctx)
- if global.UseArtImage {
- bootImage = artBootImageConfig(ctx)
+ // When `global.PreoptWithUpdatableBcp` is true, `bcpForDexpreopt` below includes the mainline
+ // boot jars into bootclasspath, so we should include the mainline boot image as well because it's
+ // generated from those jars.
+ if global.PreoptWithUpdatableBcp {
+ bootImage = mainlineBootImageConfig(ctx)
}
-
dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
targets := ctx.MultiTargets()
@@ -273,8 +303,10 @@
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.
+ if isSystemServerJar && moduleName(ctx) != "com.android.location.provider" {
+ // If the module is a system server jar, only preopt for the primary arch because the jar can
+ // only be loaded by system server. "com.android.location.provider" is a special case because
+ // it's also used by apps as a shared library.
targets = targets[:1]
}
}
@@ -294,7 +326,9 @@
var profileClassListing android.OptionalPath
var profileBootListing android.OptionalPath
profileIsTextListing := false
- if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) {
+ if d.inputProfilePathOnHost != nil {
+ profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
+ } else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) {
// 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) != "" {
@@ -309,6 +343,8 @@
}
}
+ d.dexpreoptProperties.Dex_preopt_result.Profile_guided = profileClassListing.Valid()
+
// Full dexpreopt config, used to create dexpreopt build rules.
dexpreoptConfig := &dexpreopt.ModuleConfig{
Name: moduleName(ctx),
@@ -370,21 +406,29 @@
installBase := filepath.Base(install.To)
arch := filepath.Base(installDir)
installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+ isProfile := strings.HasSuffix(installBase, ".prof")
+
+ if isProfile {
+ d.outputProfilePathOnHost = install.From
+ }
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,
- })
+ // Profiles are handled separately because they are installed into the APEX.
+ if !isProfile {
+ // 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)
}
@@ -406,3 +450,7 @@
}
return entries
}
+
+func (d *dexpreopter) OutputProfilePathOnHost() android.Path {
+ return d.outputProfilePathOnHost
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 2deb472..f477f40 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -16,6 +16,7 @@
import (
"path/filepath"
+ "sort"
"strings"
"android/soong/android"
@@ -213,12 +214,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",
@@ -255,11 +250,12 @@
// 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
+ // The relative location where the image files are installed. On host, the location is relative to
+ // $ANDROID_PRODUCT_OUT.
+ //
+ // Only the configs that are built by platform_bootclasspath are installable on device. On device,
+ // the location is relative to "/".
+ installDir string
// Install path of the boot image profile if it needs to be installed in the APEX, or empty if not
// needed.
@@ -293,6 +289,17 @@
// Path of the preloaded classes file.
preloadedClassesFile string
+
+ // The "--compiler-filter" argument.
+ compilerFilter string
+
+ // The "--single-image" argument.
+ singleImage bool
+
+ // Profiles imported from other boot image configs. Each element must represent a
+ // `bootclasspath_fragment` of an APEX (i.e., the `name` field of each element must refer to the
+ // `image_name` property of a `bootclasspath_fragment`).
+ profileImports []*bootImageConfig
}
// Target-dependent description of a boot image.
@@ -315,17 +322,17 @@
// 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
+ // The path to the base image variant's imagePathOnHost field, where base 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
+ baseImages android.OutputPaths
- // The paths to the primary image variant's imagesDeps field, where primary image variant
+ // The paths to the base image variant's imagesDeps field, where base 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
+ baseImagesDeps android.Paths
// Rules which should be used in make to install the outputs on host.
//
@@ -400,6 +407,9 @@
for _, ext := range exts {
ret = append(ret, dir.Join(ctx, name+ext))
}
+ if image.singleImage {
+ break
+ }
}
return ret
}
@@ -417,11 +427,6 @@
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
@@ -449,6 +454,10 @@
append(imageLocationsOnDevice, dexpreopt.PathStringToLocation(image.imagePathOnDevice, image.target.Arch.ArchType))
}
+func (image *bootImageConfig) isProfileGuided() bool {
+ return image.compilerFilter == "speed-profile"
+}
+
func dexpreoptBootJarsFactory() android.SingletonModule {
m := &dexpreoptBootJars{}
android.InitAndroidModule(m)
@@ -492,15 +501,12 @@
// 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")
+ archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
+ d.dexpreoptConfigForMake = android.PathForOutput(ctx, toDexpreoptDirName(archType), "dexpreopt.config")
writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
global := dexpreopt.GetGlobalConfig(ctx)
@@ -510,8 +516,13 @@
defaultImageConfig := defaultBootImageConfig(ctx)
d.defaultBootImage = defaultImageConfig
- artBootImageConfig := artBootImageConfig(ctx)
- d.otherImages = []*bootImageConfig{artBootImageConfig}
+ imageConfigs := genBootImageConfigs(ctx)
+ d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1)
+ for _, config := range imageConfigs {
+ if config != defaultImageConfig {
+ d.otherImages = append(d.otherImages, config)
+ }
+ }
}
// shouldBuildBootImages determines whether boot images should be built.
@@ -531,8 +542,8 @@
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 = append(names, android.SortedKeys(srcBootDexJarsByModule)...)
+ names = append(names, android.SortedKeys(dstBootJarsByModule)...)
names = android.SortedUniqueStrings(names)
for _, name := range names {
src := srcBootDexJarsByModule[name]
@@ -586,6 +597,12 @@
buildBootImageForOsType(ctx, image, profile, ctx.Config().BuildOS)
}
+// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files.
+//
+// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot
+// image is created.
+type bootImageFilesByArch map[android.ArchType]android.Paths
+
// bootImageOutputs encapsulates information about boot images that were created/obtained by
// commonBootclasspathFragment.produceBootImageFiles.
type bootImageOutputs struct {
@@ -646,8 +663,7 @@
}
type bootImageVariantOutputs struct {
- config *bootImageVariant
- deviceInstalls android.RuleBuilderInstalls
+ config *bootImageVariant
}
// Generate boot image build rules for a specific target.
@@ -658,9 +674,9 @@
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())
+ symbolsDir := image.symbolsDir.Join(ctx, os, image.installDir, arch.String())
symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
- outputDir := image.dir.Join(ctx, os, image.installDirOnHost, arch.String())
+ outputDir := image.dir.Join(ctx, os, image.installDir, arch.String())
outputPath := outputDir.Join(ctx, image.stem+".oat")
oatLocation := dexpreopt.PathToLocation(outputPath, arch)
imagePath := outputPath.ReplaceExtension(ctx, "art")
@@ -700,6 +716,34 @@
cmd.FlagWithInput("--profile-file=", profile)
}
+ fragments := make(map[string]commonBootclasspathFragment)
+ ctx.VisitDirectDepsWithTag(bootclasspathFragmentDepTag, func(child android.Module) {
+ fragment := child.(commonBootclasspathFragment)
+ if fragment.getImageName() != nil && android.IsModulePreferred(child) {
+ fragments[*fragment.getImageName()] = fragment
+ }
+ })
+
+ for _, profileImport := range image.profileImports {
+ fragment := fragments[profileImport.name]
+ if fragment == nil {
+ ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but a "+
+ "bootclasspath_fragment with image name '%[2]s' doesn't exist or is not added as a "+
+ "dependency of '%[1]s'",
+ image.name,
+ profileImport.name)
+ return bootImageVariantOutputs{}
+ }
+ if fragment.getProfilePath() == nil {
+ ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+
+ "doesn't provide a profile",
+ image.name,
+ profileImport.name)
+ return bootImageVariantOutputs{}
+ }
+ cmd.FlagWithInput("--profile-file=", fragment.getProfilePath())
+ }
+
dirtyImageFile := "frameworks/base/config/dirty-image-objects"
dirtyImagePath := android.ExistentPathForSource(ctx, dirtyImageFile)
if dirtyImagePath.Valid() {
@@ -707,9 +751,11 @@
}
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
+ // It is a boot image extension, so it needs the boot images that it depends on.
+ baseImageLocations := make([]string, 0, len(image.baseImages))
+ for _, image := range image.baseImages {
+ baseImageLocations = append(baseImageLocations, dexpreopt.PathToLocation(image, arch))
+ }
cmd.
Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
@@ -717,21 +763,23 @@
// 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).
+ FlagWithList("--boot-image=", baseImageLocations, ":").Implicits(image.baseImages.Paths()).
// 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)
+ Implicits(image.baseImagesDeps)
} 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())
+ if len(image.preloadedClassesFile) > 0 {
+ // 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.
@@ -751,6 +799,16 @@
Flag("--force-determinism").
Flag("--abort-on-hard-verifier-error")
+ // If the image is profile-guided but the profile is disabled, we omit "--compiler-filter" to
+ // leave the decision to dex2oat to pick the compiler filter.
+ if !(image.isProfileGuided() && global.DisableGenerateProfile) {
+ cmd.FlagWithArg("--compiler-filter=", image.compilerFilter)
+ }
+
+ if image.singleImage {
+ cmd.Flag("--single-image")
+ }
+
// 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 {
@@ -758,6 +816,10 @@
cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch])
}
+ if global.EnableUffdGc {
+ cmd.Flag("--runtime-arg").Flag("-Xgc:CMC")
+ }
+
if global.BootFlags != "" {
cmd.Flag(global.BootFlags)
}
@@ -768,11 +830,10 @@
cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage))
- installDir := filepath.Join("/", image.installDirOnHost, arch.String())
+ installDir := filepath.Dir(image.imagePathOnDevice)
var vdexInstalls android.RuleBuilderInstalls
var unstrippedInstalls android.RuleBuilderInstalls
- var deviceInstalls android.RuleBuilderInstalls
for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
cmd.ImplicitOutput(artOrOat)
@@ -798,14 +859,6 @@
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
@@ -821,7 +874,6 @@
return bootImageVariantOutputs{
image,
- deviceInstalls,
}
}
@@ -830,6 +882,10 @@
Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
+ if !image.isProfileGuided() {
+ return nil
+ }
+
globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
global := dexpreopt.GetGlobalConfig(ctx)
@@ -838,24 +894,26 @@
}
defaultProfile := "frameworks/base/config/boot-image-profile.txt"
+ extraProfile := "frameworks/base/config/boot-image-profile-extra.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]
+ var profiles android.Paths
+ if len(global.BootImageProfiles) > 0 {
+ profiles = append(profiles, global.BootImageProfiles...)
} else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() {
- bootImageProfile = path.Path()
+ profiles = append(profiles, 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
}
+ if path := android.ExistentPathForSource(ctx, extraProfile); path.Valid() {
+ profiles = append(profiles, path.Path())
+ }
+ bootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt")
+ rule.Command().Text("cat").Inputs(profiles).Text(">").Output(bootImageProfile)
profile := image.dir.Join(ctx, "boot.prof")
@@ -965,7 +1023,7 @@
// (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 {
+ if d.dexpreoptConfigForMake != nil && !SkipDexpreoptBootJars(ctx) {
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())
}
@@ -977,17 +1035,18 @@
ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String())
}
+ if SkipDexpreoptBootJars(ctx) {
+ return
+ }
+
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, " "))
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 {
@@ -1010,6 +1069,8 @@
ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE"+current.name, strings.Join(imageLocationsOnDevice, ":"))
ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String())
}
+ // Ensure determinism.
+ sort.Strings(imageNames)
ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " "))
}
}
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
deleted file mode 100644
index bc7a55e..0000000
--- a/java/dexpreopt_bootjars_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// 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"
- "sort"
- "testing"
-
- "android/soong/android"
-)
-
-func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOutputs []string) {
- bp := `
- java_sdk_library {
- name: "foo",
- srcs: ["a.java"],
- api_packages: ["foo"],
- }
-
- java_library {
- name: "bar",
- srcs: ["b.java"],
- installable: true,
- system_ext_specific: true,
- }
-
- dex_import {
- name: "baz",
- jars: ["a.jar"],
- }
-
- platform_bootclasspath {
- name: "platform-bootclasspath",
- }
- `
-
- result := android.GroupFixturePreparers(
- prepareForJavaTest,
- PrepareForTestWithJavaSdkLibraryFiles,
- FixtureWithLastReleaseApis("foo"),
- FixtureConfigureBootJars("platform:foo", "system_ext:bar", "platform:baz"),
- ).RunTestWithBp(t, bp)
-
- platformBootclasspath := result.ModuleForTests("platform-bootclasspath", "android_common")
- rule := platformBootclasspath.Output(ruleFile)
-
- for i := range expectedInputs {
- expectedInputs[i] = filepath.Join("out/soong/test_device", expectedInputs[i])
- }
-
- for i := range expectedOutputs {
- expectedOutputs[i] = filepath.Join("out/soong/test_device", expectedOutputs[i])
- }
-
- inputs := rule.Implicits.Strings()
- sort.Strings(inputs)
- sort.Strings(expectedInputs)
-
- outputs := append(android.WritablePaths{rule.Output}, rule.ImplicitOutputs...).Strings()
- sort.Strings(outputs)
- sort.Strings(expectedOutputs)
-
- android.AssertStringPathsRelativeToTopEquals(t, "inputs", result.Config, expectedInputs, inputs)
-
- android.AssertStringPathsRelativeToTopEquals(t, "outputs", result.Config, expectedOutputs, outputs)
-}
-
-func TestDexpreoptBootJars(t *testing.T) {
- ruleFile := "boot-foo.art"
-
- expectedInputs := []string{
- "dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
- "dex_bootjars_input/foo.jar",
- "dex_bootjars_input/bar.jar",
- "dex_bootjars_input/baz.jar",
- }
-
- expectedOutputs := []string{
- "dex_bootjars/android/system/framework/arm64/boot.invocation",
- "dex_bootjars/android/system/framework/arm64/boot-foo.art",
- "dex_bootjars/android/system/framework/arm64/boot-bar.art",
- "dex_bootjars/android/system/framework/arm64/boot-baz.art",
- "dex_bootjars/android/system/framework/arm64/boot-foo.oat",
- "dex_bootjars/android/system/framework/arm64/boot-bar.oat",
- "dex_bootjars/android/system/framework/arm64/boot-baz.oat",
- "dex_bootjars/android/system/framework/arm64/boot-foo.vdex",
- "dex_bootjars/android/system/framework/arm64/boot-bar.vdex",
- "dex_bootjars/android/system/framework/arm64/boot-baz.vdex",
- "dex_bootjars_unstripped/android/system/framework/arm64/boot-foo.oat",
- "dex_bootjars_unstripped/android/system/framework/arm64/boot-bar.oat",
- "dex_bootjars_unstripped/android/system/framework/arm64/boot-baz.oat",
- }
-
- testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs)
-}
-
-// Changes to the boot.zip structure may break the ART APK scanner.
-func TestDexpreoptBootZip(t *testing.T) {
- ruleFile := "boot.zip"
-
- ctx := android.PathContextForTesting(android.TestArchConfig("", nil, "", nil))
- expectedInputs := []string{}
- for _, target := range ctx.Config().Targets[android.Android] {
- for _, ext := range []string{".art", ".oat", ".vdex"} {
- for _, jar := range []string{"foo", "bar", "baz"} {
- expectedInputs = append(expectedInputs,
- filepath.Join("dex_bootjars", target.Os.String(), "system/framework", target.Arch.ArchType.String(), "boot-"+jar+ext))
- }
- }
- }
-
- expectedOutputs := []string{
- "dex_bootjars/boot.zip",
- }
-
- testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs)
-}
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 4d0bd09..9100e87 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -44,6 +44,8 @@
bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw")
artBootImageName = "art"
frameworkBootImageName = "boot"
+ mainlineBootImageName = "mainline"
+ bootImageStem = "boot"
)
func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig {
@@ -51,36 +53,50 @@
global := dexpreopt.GetGlobalConfig(ctx)
artModules := global.ArtApexJars
- frameworkModules := global.BootJars.RemoveList(artModules)
+ frameworkModules := global.BootJars // This includes `artModules`.
+ mainlineBcpModules := global.ApexBootJars
+ frameworkSubdir := "system/framework"
// 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",
+ stem: bootImageStem,
+ installDir: "apex/art_boot_images/javalib",
profileInstallPathInApex: "etc/boot-image.prof",
modules: artModules,
preloadedClassesFile: "art/build/boot/preloaded-classes",
+ compilerFilter: "speed-profile",
+ singleImage: false,
}
// 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,
+ stem: bootImageStem,
+ installDir: frameworkSubdir,
modules: frameworkModules,
preloadedClassesFile: "frameworks/base/config/preloaded-classes",
+ compilerFilter: "speed-profile",
+ singleImage: false,
+ profileImports: []*bootImageConfig{&artCfg},
+ }
+
+ mainlineCfg := bootImageConfig{
+ extends: &frameworkCfg,
+ name: mainlineBootImageName,
+ stem: bootImageStem,
+ installDir: frameworkSubdir,
+ modules: mainlineBcpModules,
+ compilerFilter: "verify",
+ singleImage: true,
}
return map[string]*bootImageConfig{
artBootImageName: &artCfg,
frameworkBootImageName: &frameworkCfg,
+ mainlineBootImageName: &mainlineCfg,
}
}).(map[string]*bootImageConfig)
}
@@ -89,13 +105,11 @@
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())
+ archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
+ deviceDir := android.PathForOutput(ctx, toDexpreoptDirName(archType))
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")
@@ -115,12 +129,12 @@
// Create target-specific variants.
for _, target := range targets {
arch := target.Arch.ArchType
- imageDir := c.dir.Join(ctx, target.Os.String(), c.installDirOnHost, arch.String())
+ imageDir := c.dir.Join(ctx, target.Os.String(), c.installDir, arch.String())
variant := &bootImageVariant{
bootImageConfig: c,
target: target,
imagePathOnHost: imageDir.Join(ctx, imageName),
- imagePathOnDevice: filepath.Join("/", c.installDirOnDevice, arch.String(), imageName),
+ imagePathOnDevice: filepath.Join("/", c.installDir, arch.String(), imageName),
imagesDeps: c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"),
dexLocations: c.modules.DevicePaths(ctx.Config(), target.Os),
}
@@ -131,18 +145,42 @@
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...)
+ visited := make(map[string]bool)
+ for _, c := range configs {
+ calculateDepsRecursive(c, targets, visited)
}
return configs
}).(map[string]*bootImageConfig)
}
+// calculateDepsRecursive calculates the dependencies of the given boot image config and all its
+// ancestors, if they are not visited.
+// The boot images are supposed to form a tree, where the root is the primary boot image. We do not
+// expect loops (e.g., A extends B, B extends C, C extends A), and we let them crash soong with a
+// stack overflow.
+// Note that a boot image config only has a pointer to the parent, not to children. Therefore, we
+// first go up through the parent chain, and then go back down to visit every code along the path.
+// `visited` is a map where a key is a boot image name and the value indicates whether the boot
+// image config is visited. The boot image names are guaranteed to be unique because they come from
+// `genBootImageConfigRaw` above, which also returns a map and would fail in the first place if the
+// names were not unique.
+func calculateDepsRecursive(c *bootImageConfig, targets []android.Target, visited map[string]bool) {
+ if c.extends == nil || visited[c.name] {
+ return
+ }
+ if c.extends.extends != nil {
+ calculateDepsRecursive(c.extends, targets, visited)
+ }
+ visited[c.name] = true
+ c.dexPathsDeps = android.Concat(c.extends.dexPathsDeps, c.dexPathsDeps)
+ for i := range targets {
+ c.variants[i].baseImages = android.Concat(c.extends.variants[i].baseImages, android.OutputPaths{c.extends.variants[i].imagePathOnHost})
+ c.variants[i].baseImagesDeps = android.Concat(c.extends.variants[i].baseImagesDeps, c.extends.variants[i].imagesDeps.Paths())
+ c.variants[i].dexLocationsDeps = android.Concat(c.extends.variants[i].dexLocationsDeps, c.variants[i].dexLocationsDeps)
+ }
+}
+
func artBootImageConfig(ctx android.PathContext) *bootImageConfig {
return genBootImageConfigs(ctx)[artBootImageName]
}
@@ -151,6 +189,10 @@
return genBootImageConfigs(ctx)[frameworkBootImageName]
}
+func mainlineBootImageConfig(ctx android.PathContext) *bootImageConfig {
+ return genBootImageConfigs(ctx)[mainlineBootImageName]
+}
+
// 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.
@@ -176,8 +218,8 @@
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")
+ archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
+ dir := android.PathForOutput(ctx, toDexpreoptDirName(archType), "apex_bootjars")
dexPaths := apexBootJars.BuildPaths(ctx, dir)
dexPathsByModuleName := apexBootJars.BuildPathsByModule(ctx, dir)
@@ -217,3 +259,7 @@
func dexpreoptConfigMakevars(ctx android.MakeVarsContext) {
ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":"))
}
+
+func toDexpreoptDirName(arch android.ArchType) string {
+ return "dexpreopt_" + arch.String()
+}
diff --git a/java/dexpreopt_config_test.go b/java/dexpreopt_config_test.go
index b704d09..cd7f295 100644
--- a/java/dexpreopt_config_test.go
+++ b/java/dexpreopt_config_test.go
@@ -28,8 +28,10 @@
result := android.GroupFixturePreparers(
PrepareForBootImageConfigTest,
+ PrepareApexBootJarConfigs,
).RunTest(t)
CheckArtBootImageConfig(t, result)
CheckFrameworkBootImageConfig(t, result)
+ CheckMainlineBootImageConfig(t, result)
}
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 1c236d8..6f3aa2b 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -39,6 +39,78 @@
FixtureConfigureBootJars("com.android.art:core1", "com.android.art:core2", "platform:framework"),
)
+var PrepareApexBootJarConfigs = FixtureConfigureApexBootJars(
+ "com.android.foo:framework-foo", "com.android.bar:framework-bar")
+
+var PrepareApexBootJarConfigsAndModules = android.GroupFixturePreparers(
+ PrepareApexBootJarConfigs,
+ prepareApexBootJarModule("com.android.foo", "framework-foo"),
+ prepareApexBootJarModule("com.android.bar", "framework-bar"),
+)
+
+var ApexBootJarFragmentsForPlatformBootclasspath = fmt.Sprintf(`
+ {
+ apex: "%[1]s",
+ module: "%[1]s-bootclasspathfragment",
+ },
+ {
+ apex: "%[2]s",
+ module: "%[2]s-bootclasspathfragment",
+ },
+`, "com.android.foo", "com.android.bar")
+
+var ApexBootJarDexJarPaths = []string{
+ "out/soong/.intermediates/packages/modules/com.android.bar/framework-bar/android_common_apex10000/aligned/framework-bar.jar",
+ "out/soong/.intermediates/packages/modules/com.android.foo/framework-foo/android_common_apex10000/aligned/framework-foo.jar",
+}
+
+func prepareApexBootJarModule(apexName string, moduleName string) android.FixturePreparer {
+ moduleSourceDir := fmt.Sprintf("packages/modules/%s", apexName)
+ return android.GroupFixturePreparers(
+ android.FixtureAddTextFile(moduleSourceDir+"/Android.bp", fmt.Sprintf(`
+ apex {
+ name: "%[1]s",
+ key: "%[1]s.key",
+ bootclasspath_fragments: [
+ "%[1]s-bootclasspathfragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "%[1]s.key",
+ public_key: "%[1]s.avbpubkey",
+ private_key: "%[1]s.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "%[1]s-bootclasspathfragment",
+ contents: ["%[2]s"],
+ apex_available: ["%[1]s"],
+ hidden_api: {
+ split_packages: ["*"],
+ },
+ }
+
+ java_library {
+ name: "%[2]s",
+ srcs: ["%[2]s.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ apex_available: ["%[1]s"],
+ }
+ `, apexName, moduleName)),
+ android.FixtureMergeMockFs(android.MockFS{
+ fmt.Sprintf("%s/apex_manifest.json", moduleSourceDir): nil,
+ fmt.Sprintf("%s/%s.avbpubkey", moduleSourceDir, apexName): nil,
+ fmt.Sprintf("%s/%s.pem", moduleSourceDir, apexName): nil,
+ fmt.Sprintf("system/sepolicy/apex/%s-file_contexts", apexName): nil,
+ fmt.Sprintf("%s/%s.java", moduleSourceDir, moduleName): nil,
+ }),
+ )
+}
+
// normalizedInstall represents a android.RuleBuilderInstall that has been normalized to remove
// test specific parts of the From path.
type normalizedInstall struct {
@@ -75,8 +147,7 @@
stem string
dir string
symbolsDir string
- installDirOnDevice string
- installDirOnHost string
+ installDir string
profileInstallPathInApex string
modules android.ConfiguredJarList
dexPaths []string
@@ -100,8 +171,8 @@
imagePathOnHost string
imagePathOnDevice string
imagesDeps []string
- primaryImages string
- primaryImagesDeps []string
+ baseImages []string
+ baseImagesDeps []string
// Mutated fields
installs []normalizedInstall
@@ -135,65 +206,64 @@
expected := &expectedConfig{
name: "art",
stem: "boot",
- dir: "out/soong/test_device/dex_artjars",
- symbolsDir: "out/soong/test_device/dex_artjars_unstripped",
- installDirOnDevice: "system/framework",
- installDirOnHost: "apex/art_boot_images/javalib",
+ dir: "out/soong/dexpreopt_arm64/dex_artjars",
+ symbolsDir: "out/soong/dexpreopt_arm64/dex_artjars_unstripped",
+ installDir: "apex/art_boot_images/javalib",
profileInstallPathInApex: "etc/boot-image.prof",
modules: android.CreateTestConfiguredJarList([]string{"com.android.art:core1", "com.android.art:core2"}),
- dexPaths: []string{"out/soong/test_device/dex_artjars_input/core1.jar", "out/soong/test_device/dex_artjars_input/core2.jar"},
- dexPathsDeps: []string{"out/soong/test_device/dex_artjars_input/core1.jar", "out/soong/test_device/dex_artjars_input/core2.jar"},
- zip: "out/soong/test_device/dex_artjars/art.zip",
+ dexPaths: []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar"},
+ dexPathsDeps: []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar"},
+ zip: "out/soong/dexpreopt_arm64/dex_artjars/art.zip",
variants: []*expectedVariant{
{
archType: android.Arm64,
dexLocations: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
dexLocationsDeps: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
- imagePathOnHost: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
- imagePathOnDevice: "/system/framework/arm64/boot.art",
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+ imagePathOnDevice: "/apex/art_boot_images/javalib/arm64/boot.art",
imagesDeps: []string{
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
},
installs: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
to: "/apex/art_boot_images/javalib/arm64/boot.art",
},
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
to: "/apex/art_boot_images/javalib/arm64/boot.oat",
},
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
to: "/apex/art_boot_images/javalib/arm64/boot-core2.art",
},
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
to: "/apex/art_boot_images/javalib/arm64/boot-core2.oat",
},
},
vdexInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
to: "/apex/art_boot_images/javalib/arm64/boot.vdex",
},
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
to: "/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
},
},
unstrippedInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat",
to: "/apex/art_boot_images/javalib/arm64/boot.oat",
},
{
- from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
to: "/apex/art_boot_images/javalib/arm64/boot-core2.oat",
},
},
@@ -203,51 +273,51 @@
archType: android.Arm,
dexLocations: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
dexLocationsDeps: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
- imagePathOnHost: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
- imagePathOnDevice: "/system/framework/arm/boot.art",
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+ imagePathOnDevice: "/apex/art_boot_images/javalib/arm/boot.art",
imagesDeps: []string{
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
},
installs: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
to: "/apex/art_boot_images/javalib/arm/boot.art",
},
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
to: "/apex/art_boot_images/javalib/arm/boot.oat",
},
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
to: "/apex/art_boot_images/javalib/arm/boot-core2.art",
},
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
to: "/apex/art_boot_images/javalib/arm/boot-core2.oat",
},
},
vdexInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
to: "/apex/art_boot_images/javalib/arm/boot.vdex",
},
{
- from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
to: "/apex/art_boot_images/javalib/arm/boot-core2.vdex",
},
},
unstrippedInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat",
to: "/apex/art_boot_images/javalib/arm/boot.oat",
},
{
- from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
to: "/apex/art_boot_images/javalib/arm/boot-core2.oat",
},
},
@@ -257,49 +327,49 @@
archType: android.X86_64,
dexLocations: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
dexLocationsDeps: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
- imagePathOnHost: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
- imagePathOnDevice: "/system/framework/x86_64/boot.art",
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+ imagePathOnDevice: "/apex/art_boot_images/javalib/x86_64/boot.art",
imagesDeps: []string{
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
},
installs: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
to: "/apex/art_boot_images/javalib/x86_64/boot.art",
}, {
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
to: "/apex/art_boot_images/javalib/x86_64/boot.oat",
},
{
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
to: "/apex/art_boot_images/javalib/x86_64/boot-core2.art",
}, {
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
to: "/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
},
},
vdexInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
to: "/apex/art_boot_images/javalib/x86_64/boot.vdex",
},
{
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
to: "/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
},
},
unstrippedInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
to: "/apex/art_boot_images/javalib/x86_64/boot.oat",
},
{
- from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
to: "/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
},
},
@@ -309,49 +379,49 @@
archType: android.X86,
dexLocations: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
dexLocationsDeps: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
- imagePathOnHost: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
- imagePathOnDevice: "/system/framework/x86/boot.art",
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+ imagePathOnDevice: "/apex/art_boot_images/javalib/x86/boot.art",
imagesDeps: []string{
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
},
installs: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
to: "/apex/art_boot_images/javalib/x86/boot.art",
}, {
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
to: "/apex/art_boot_images/javalib/x86/boot.oat",
},
{
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
to: "/apex/art_boot_images/javalib/x86/boot-core2.art",
}, {
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
to: "/apex/art_boot_images/javalib/x86/boot-core2.oat",
},
},
vdexInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
to: "/apex/art_boot_images/javalib/x86/boot.vdex",
},
{
- from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
to: "/apex/art_boot_images/javalib/x86/boot-core2.vdex",
},
},
unstrippedInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
to: "/apex/art_boot_images/javalib/x86/boot.oat",
},
{
- from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
+ from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
to: "/apex/art_boot_images/javalib/x86/boot-core2.oat",
},
},
@@ -388,203 +458,346 @@
expected := &expectedConfig{
name: "boot",
stem: "boot",
- dir: "out/soong/test_device/dex_bootjars",
- symbolsDir: "out/soong/test_device/dex_bootjars_unstripped",
- installDirOnDevice: "system/framework",
- installDirOnHost: "system/framework",
+ dir: "out/soong/dexpreopt_arm64/dex_bootjars",
+ symbolsDir: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped",
+ installDir: "system/framework",
profileInstallPathInApex: "",
- modules: android.CreateTestConfiguredJarList([]string{"platform:framework"}),
- dexPaths: []string{"out/soong/test_device/dex_bootjars_input/framework.jar"},
- dexPathsDeps: []string{"out/soong/test_device/dex_artjars_input/core1.jar", "out/soong/test_device/dex_artjars_input/core2.jar", "out/soong/test_device/dex_bootjars_input/framework.jar"},
- zip: "out/soong/test_device/dex_bootjars/boot.zip",
+ modules: android.CreateTestConfiguredJarList([]string{
+ "com.android.art:core1",
+ "com.android.art:core2",
+ "platform:framework",
+ }),
+ dexPaths: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/core1.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/core2.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/framework.jar",
+ },
+ dexPathsDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/core1.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/core2.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/framework.jar",
+ },
+ zip: "out/soong/dexpreopt_arm64/dex_bootjars/boot.zip",
variants: []*expectedVariant{
{
- archType: android.Arm64,
- dexLocations: []string{"/system/framework/framework.jar"},
+ archType: android.Arm64,
+ dexLocations: []string{
+ "/apex/com.android.art/javalib/core1.jar",
+ "/apex/com.android.art/javalib/core2.jar",
+ "/system/framework/framework.jar",
+ },
dexLocationsDeps: []string{
"/apex/com.android.art/javalib/core1.jar",
"/apex/com.android.art/javalib/core2.jar",
"/system/framework/framework.jar",
},
- imagePathOnHost: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
- imagePathOnDevice: "/system/framework/arm64/boot-framework.art",
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+ imagePathOnDevice: "/system/framework/arm64/boot.art",
imagesDeps: []string{
- "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
- "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
- "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
- },
- primaryImages: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
- primaryImagesDeps: []string{
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
},
installs: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+ to: "/system/framework/arm64/boot.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+ to: "/system/framework/arm64/boot.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art",
+ to: "/system/framework/arm64/boot-core2.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat",
+ to: "/system/framework/arm64/boot-core2.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art",
to: "/system/framework/arm64/boot-framework.art",
},
{
- from: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
to: "/system/framework/arm64/boot-framework.oat",
},
},
vdexInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+ to: "/system/framework/arm64/boot.vdex",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex",
+ to: "/system/framework/arm64/boot-core2.vdex",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
to: "/system/framework/arm64/boot-framework.vdex",
},
},
unstrippedInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat",
+ to: "/system/framework/arm64/boot.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-core2.oat",
+ to: "/system/framework/arm64/boot-core2.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat",
to: "/system/framework/arm64/boot-framework.oat",
},
},
licenseMetadataFile: expectedLicenseMetadataFile,
},
{
- archType: android.Arm,
- dexLocations: []string{"/system/framework/framework.jar"},
+ archType: android.Arm,
+ dexLocations: []string{
+ "/apex/com.android.art/javalib/core1.jar",
+ "/apex/com.android.art/javalib/core2.jar",
+ "/system/framework/framework.jar",
+ },
dexLocationsDeps: []string{
"/apex/com.android.art/javalib/core1.jar",
"/apex/com.android.art/javalib/core2.jar",
"/system/framework/framework.jar",
},
- imagePathOnHost: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
- imagePathOnDevice: "/system/framework/arm/boot-framework.art",
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
+ imagePathOnDevice: "/system/framework/arm/boot.art",
imagesDeps: []string{
- "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
- "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat",
- "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
- },
- primaryImages: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
- primaryImagesDeps: []string{
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
- "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
},
installs: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
+ to: "/system/framework/arm/boot.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat",
+ to: "/system/framework/arm/boot.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art",
+ to: "/system/framework/arm/boot-core2.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat",
+ to: "/system/framework/arm/boot-core2.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art",
to: "/system/framework/arm/boot-framework.art",
},
{
- from: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat",
to: "/system/framework/arm/boot-framework.oat",
},
},
vdexInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex",
+ to: "/system/framework/arm/boot.vdex",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex",
+ to: "/system/framework/arm/boot-core2.vdex",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
to: "/system/framework/arm/boot-framework.vdex",
},
},
unstrippedInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot.oat",
+ to: "/system/framework/arm/boot.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-core2.oat",
+ to: "/system/framework/arm/boot-core2.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat",
to: "/system/framework/arm/boot-framework.oat",
},
},
licenseMetadataFile: expectedLicenseMetadataFile,
},
{
- archType: android.X86_64,
- dexLocations: []string{"host/linux-x86/system/framework/framework.jar"},
+ archType: android.X86_64,
+ dexLocations: []string{
+ "host/linux-x86/apex/com.android.art/javalib/core1.jar",
+ "host/linux-x86/apex/com.android.art/javalib/core2.jar",
+ "host/linux-x86/system/framework/framework.jar",
+ },
dexLocationsDeps: []string{
"host/linux-x86/apex/com.android.art/javalib/core1.jar",
"host/linux-x86/apex/com.android.art/javalib/core2.jar",
"host/linux-x86/system/framework/framework.jar",
},
- imagePathOnHost: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
- imagePathOnDevice: "/system/framework/x86_64/boot-framework.art",
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
+ imagePathOnDevice: "/system/framework/x86_64/boot.art",
imagesDeps: []string{
- "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
- "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
- "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
- },
- primaryImages: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
- primaryImagesDeps: []string{
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
},
installs: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
+ to: "/system/framework/x86_64/boot.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat",
+ to: "/system/framework/x86_64/boot.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art",
+ to: "/system/framework/x86_64/boot-core2.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat",
+ to: "/system/framework/x86_64/boot-core2.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
to: "/system/framework/x86_64/boot-framework.art",
},
{
- from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
to: "/system/framework/x86_64/boot-framework.oat",
},
},
vdexInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex",
+ to: "/system/framework/x86_64/boot.vdex",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex",
+ to: "/system/framework/x86_64/boot-core2.vdex",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
to: "/system/framework/x86_64/boot-framework.vdex",
},
},
unstrippedInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot.oat",
+ to: "/system/framework/x86_64/boot.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-core2.oat",
+ to: "/system/framework/x86_64/boot-core2.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat",
to: "/system/framework/x86_64/boot-framework.oat",
},
},
licenseMetadataFile: expectedLicenseMetadataFile,
},
{
- archType: android.X86,
- dexLocations: []string{"host/linux-x86/system/framework/framework.jar"},
+ archType: android.X86,
+ dexLocations: []string{
+ "host/linux-x86/apex/com.android.art/javalib/core1.jar",
+ "host/linux-x86/apex/com.android.art/javalib/core2.jar",
+ "host/linux-x86/system/framework/framework.jar",
+ },
dexLocationsDeps: []string{
"host/linux-x86/apex/com.android.art/javalib/core1.jar",
"host/linux-x86/apex/com.android.art/javalib/core2.jar",
"host/linux-x86/system/framework/framework.jar",
},
- imagePathOnHost: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
- imagePathOnDevice: "/system/framework/x86/boot-framework.art",
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
+ imagePathOnDevice: "/system/framework/x86/boot.art",
imagesDeps: []string{
- "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
- "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
- "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
- },
- primaryImages: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
- primaryImagesDeps: []string{
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
- "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
},
installs: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
+ to: "/system/framework/x86/boot.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat",
+ to: "/system/framework/x86/boot.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art",
+ to: "/system/framework/x86/boot-core2.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat",
+ to: "/system/framework/x86/boot-core2.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
to: "/system/framework/x86/boot-framework.art",
},
{
- from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
to: "/system/framework/x86/boot-framework.oat",
},
},
vdexInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex",
+ to: "/system/framework/x86/boot.vdex",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex",
+ to: "/system/framework/x86/boot-core2.vdex",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
to: "/system/framework/x86/boot-framework.vdex",
},
},
unstrippedInstalls: []normalizedInstall{
{
- from: "out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat",
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot.oat",
+ to: "/system/framework/x86/boot.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-core2.oat",
+ to: "/system/framework/x86/boot-core2.oat",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat",
to: "/system/framework/x86/boot-framework.oat",
},
},
@@ -592,8 +805,8 @@
},
},
profileInstalls: []normalizedInstall{
- {from: "out/soong/test_device/dex_bootjars/boot.bprof", to: "/system/etc/boot-image.bprof"},
- {from: "out/soong/test_device/dex_bootjars/boot.prof", to: "/system/etc/boot-image.prof"},
+ {from: "out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof", to: "/system/etc/boot-image.bprof"},
+ {from: "out/soong/dexpreopt_arm64/dex_bootjars/boot.prof", to: "/system/etc/boot-image.prof"},
},
profileLicenseMetadataFile: expectedLicenseMetadataFile,
}
@@ -601,6 +814,285 @@
checkBootImageConfig(t, imageConfig, mutated, expected)
}
+// getMainlineImageConfig gets the framework bootImageConfig that was created during the test.
+func getMainlineImageConfig(result *android.TestResult) *bootImageConfig {
+ pathCtx := &android.TestPathContext{TestResult: result}
+ imageConfig := mainlineBootImageConfig(pathCtx)
+ return imageConfig
+}
+
+// CheckMainlineBootImageConfig checks the status of the fields of the bootImageConfig and
+// bootImageVariant structures that are returned from mainlineBootImageConfig.
+//
+// This is before any fields are mutated.
+func CheckMainlineBootImageConfig(t *testing.T, result *android.TestResult) {
+ expectedLicenseMetadataFile := ""
+ imageConfig := getMainlineImageConfig(result)
+
+ expected := &expectedConfig{
+ name: "mainline",
+ stem: "boot",
+ dir: "out/soong/dexpreopt_arm64/dex_mainlinejars",
+ symbolsDir: "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped",
+ installDir: "system/framework",
+ profileInstallPathInApex: "",
+ modules: android.CreateTestConfiguredJarList([]string{
+ "com.android.foo:framework-foo",
+ "com.android.bar:framework-bar",
+ }),
+ dexPaths: []string{
+ "out/soong/dexpreopt_arm64/dex_mainlinejars_input/framework-foo.jar",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars_input/framework-bar.jar",
+ },
+ dexPathsDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/core1.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/core2.jar",
+ "out/soong/dexpreopt_arm64/dex_bootjars_input/framework.jar",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars_input/framework-foo.jar",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars_input/framework-bar.jar",
+ },
+ zip: "out/soong/dexpreopt_arm64/dex_mainlinejars/mainline.zip",
+ variants: []*expectedVariant{
+ {
+ archType: android.Arm64,
+ dexLocations: []string{
+ "/apex/com.android.foo/javalib/framework-foo.jar",
+ "/apex/com.android.bar/javalib/framework-bar.jar",
+ },
+ dexLocationsDeps: []string{
+ "/apex/com.android.art/javalib/core1.jar",
+ "/apex/com.android.art/javalib/core2.jar",
+ "/system/framework/framework.jar",
+ "/apex/com.android.foo/javalib/framework-foo.jar",
+ "/apex/com.android.bar/javalib/framework-bar.jar",
+ },
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
+ imagePathOnDevice: "/system/framework/arm64/boot-framework-foo.art",
+ imagesDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex",
+ },
+ baseImages: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+ },
+ baseImagesDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
+ },
+ installs: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
+ to: "/system/framework/arm64/boot-framework-foo.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat",
+ to: "/system/framework/arm64/boot-framework-foo.oat",
+ },
+ },
+ vdexInstalls: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex",
+ to: "/system/framework/arm64/boot-framework-foo.vdex",
+ },
+ },
+ unstrippedInstalls: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm64/boot-framework-foo.oat",
+ to: "/system/framework/arm64/boot-framework-foo.oat",
+ },
+ },
+ licenseMetadataFile: expectedLicenseMetadataFile,
+ },
+ {
+ archType: android.Arm,
+ dexLocations: []string{
+ "/apex/com.android.foo/javalib/framework-foo.jar",
+ "/apex/com.android.bar/javalib/framework-bar.jar",
+ },
+ dexLocationsDeps: []string{
+ "/apex/com.android.art/javalib/core1.jar",
+ "/apex/com.android.art/javalib/core2.jar",
+ "/system/framework/framework.jar",
+ "/apex/com.android.foo/javalib/framework-foo.jar",
+ "/apex/com.android.bar/javalib/framework-bar.jar",
+ },
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
+ imagePathOnDevice: "/system/framework/arm/boot-framework-foo.art",
+ imagesDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex",
+ },
+ baseImages: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
+ },
+ baseImagesDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
+ },
+ installs: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
+ to: "/system/framework/arm/boot-framework-foo.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat",
+ to: "/system/framework/arm/boot-framework-foo.oat",
+ },
+ },
+ vdexInstalls: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex",
+ to: "/system/framework/arm/boot-framework-foo.vdex",
+ },
+ },
+ unstrippedInstalls: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm/boot-framework-foo.oat",
+ to: "/system/framework/arm/boot-framework-foo.oat",
+ },
+ },
+ licenseMetadataFile: expectedLicenseMetadataFile,
+ },
+ {
+ archType: android.X86_64,
+ dexLocations: []string{
+ "host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
+ "host/linux-x86/apex/com.android.bar/javalib/framework-bar.jar",
+ },
+ dexLocationsDeps: []string{
+ "host/linux-x86/apex/com.android.art/javalib/core1.jar",
+ "host/linux-x86/apex/com.android.art/javalib/core2.jar",
+ "host/linux-x86/system/framework/framework.jar",
+ "host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
+ "host/linux-x86/apex/com.android.bar/javalib/framework-bar.jar",
+ },
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
+ imagePathOnDevice: "/system/framework/x86_64/boot-framework-foo.art",
+ imagesDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex",
+ },
+ baseImages: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
+ },
+ baseImagesDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
+ },
+ installs: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
+ to: "/system/framework/x86_64/boot-framework-foo.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
+ to: "/system/framework/x86_64/boot-framework-foo.oat",
+ },
+ },
+ vdexInstalls: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex",
+ to: "/system/framework/x86_64/boot-framework-foo.vdex",
+ },
+ },
+ unstrippedInstalls: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
+ to: "/system/framework/x86_64/boot-framework-foo.oat",
+ },
+ },
+ licenseMetadataFile: expectedLicenseMetadataFile,
+ },
+ {
+ archType: android.X86,
+ dexLocations: []string{
+ "host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
+ "host/linux-x86/apex/com.android.bar/javalib/framework-bar.jar",
+ },
+ dexLocationsDeps: []string{
+ "host/linux-x86/apex/com.android.art/javalib/core1.jar",
+ "host/linux-x86/apex/com.android.art/javalib/core2.jar",
+ "host/linux-x86/system/framework/framework.jar",
+ "host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
+ "host/linux-x86/apex/com.android.bar/javalib/framework-bar.jar",
+ },
+ imagePathOnHost: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
+ imagePathOnDevice: "/system/framework/x86/boot-framework-foo.art",
+ imagesDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat",
+ "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex",
+ },
+ baseImages: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
+ },
+ baseImagesDeps: []string{
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
+ "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
+ },
+ installs: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
+ to: "/system/framework/x86/boot-framework-foo.art",
+ },
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat",
+ to: "/system/framework/x86/boot-framework-foo.oat",
+ },
+ },
+ vdexInstalls: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex",
+ to: "/system/framework/x86/boot-framework-foo.vdex",
+ },
+ },
+ unstrippedInstalls: []normalizedInstall{
+ {
+ from: "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86/boot-framework-foo.oat",
+ to: "/system/framework/x86/boot-framework-foo.oat",
+ },
+ },
+ licenseMetadataFile: expectedLicenseMetadataFile,
+ },
+ },
+ profileInstalls: []normalizedInstall{},
+ profileLicenseMetadataFile: expectedLicenseMetadataFile,
+ }
+
+ checkBootImageConfig(t, imageConfig, false, expected)
+}
+
// clearMutatedFields clears fields in the expectedConfig that correspond to fields in the
// bootImageConfig/bootImageVariant structs which are mutated outside the call to
// genBootImageConfigs.
@@ -643,8 +1135,7 @@
android.AssertStringEquals(t, "stem", expected.stem, imageConfig.stem)
android.AssertPathRelativeToTopEquals(t, "dir", expected.dir, imageConfig.dir)
android.AssertPathRelativeToTopEquals(t, "symbolsDir", expected.symbolsDir, imageConfig.symbolsDir)
- android.AssertStringEquals(t, "installDirOnDevice", expected.installDirOnDevice, imageConfig.installDirOnDevice)
- android.AssertStringEquals(t, "installDirOnHost", expected.installDirOnHost, imageConfig.installDirOnHost)
+ android.AssertStringEquals(t, "installDir", expected.installDir, imageConfig.installDir)
android.AssertStringEquals(t, "profileInstallPathInApex", expected.profileInstallPathInApex, imageConfig.profileInstallPathInApex)
android.AssertDeepEquals(t, "modules", expected.modules, imageConfig.modules)
android.AssertPathsRelativeToTopEquals(t, "dexPaths", expected.dexPaths, imageConfig.dexPaths.Paths())
@@ -664,8 +1155,8 @@
android.AssertPathRelativeToTopEquals(t, "imagePathOnHost", expectedVariant.imagePathOnHost, variant.imagePathOnHost)
android.AssertStringEquals(t, "imagePathOnDevice", expectedVariant.imagePathOnDevice, variant.imagePathOnDevice)
android.AssertPathsRelativeToTopEquals(t, "imagesDeps", expectedVariant.imagesDeps, variant.imagesDeps.Paths())
- android.AssertPathRelativeToTopEquals(t, "primaryImages", expectedVariant.primaryImages, variant.primaryImages)
- android.AssertPathsRelativeToTopEquals(t, "primaryImagesDeps", expectedVariant.primaryImagesDeps, variant.primaryImagesDeps)
+ android.AssertPathsRelativeToTopEquals(t, "baseImages", expectedVariant.baseImages, variant.baseImages.Paths())
+ android.AssertPathsRelativeToTopEquals(t, "baseImagesDeps", expectedVariant.baseImagesDeps, variant.baseImagesDeps)
assertInstallsEqual(t, "installs", expectedVariant.installs, variant.installs)
assertInstallsEqual(t, "vdexInstalls", expectedVariant.vdexInstalls, variant.vdexInstalls)
assertInstallsEqual(t, "unstrippedInstalls", expectedVariant.unstrippedInstalls, variant.unstrippedInstalls)
@@ -700,26 +1191,34 @@
fmt.Fprintf(out, "%s=%s\n", v.Name(), android.StringRelativeToTop(result.Config, v.Value()))
}
format := `
-DEXPREOPT_BOOTCLASSPATH_DEX_FILES=out/soong/test_device/dex_artjars_input/core1.jar out/soong/test_device/dex_artjars_input/core2.jar out/soong/test_device/dex_bootjars_input/framework.jar
+DEXPREOPT_BOOTCLASSPATH_DEX_FILES=out/soong/dexpreopt_arm64/dex_bootjars_input/core1.jar out/soong/dexpreopt_arm64/dex_bootjars_input/core2.jar out/soong/dexpreopt_arm64/dex_bootjars_input/framework.jar
DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS=/apex/com.android.art/javalib/core1.jar /apex/com.android.art/javalib/core2.jar /system/framework/framework.jar
-DEXPREOPT_BOOT_JARS_MODULES=platform:framework
+DEXPREOPT_BOOT_JARS_MODULES=com.android.art:core1:com.android.art:core2:platform:framework
DEXPREOPT_GEN=out/host/linux-x86/bin/dexpreopt_gen
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art:/apex/art_boot_images/javalib/arm/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art:/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art:/apex/art_boot_images/javalib/arm64/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art:/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art:/apex/art_boot_images/javalib/x86/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art:/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art:/apex/art_boot_images/javalib/x86_64/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art:/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art:/system/framework/arm/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art:/system/framework/arm64/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art:/system/framework/x86/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art:/system/framework/x86_64/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
-DEXPREOPT_IMAGE_DEPS_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex
-DEXPREOPT_IMAGE_DEPS_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex
-DEXPREOPT_IMAGE_DEPS_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex
-DEXPREOPT_IMAGE_DEPS_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art:/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art:/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art:/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art:/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art:/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art:/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art:/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art:/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art:/system/framework/arm/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat:/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art:/system/framework/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat:/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art:/system/framework/arm/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art:/system/framework/arm64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat:/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art:/system/framework/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat:/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art:/system/framework/arm64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art:/system/framework/x86/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat:/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art:/system/framework/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat:/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art:/system/framework/x86/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art:/system/framework/x86_64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat:/system/framework/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art:/system/framework/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat:/system/framework/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art:/system/framework/x86_64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art:/system/framework/arm/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat:/system/framework/arm/boot-framework-foo.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art:/system/framework/arm64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art:/system/framework/x86/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art:/system/framework/x86_64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat
+DEXPREOPT_IMAGE_DEPS_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex
+DEXPREOPT_IMAGE_DEPS_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex
+DEXPREOPT_IMAGE_DEPS_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex
+DEXPREOPT_IMAGE_DEPS_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
+DEXPREOPT_IMAGE_DEPS_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex
+DEXPREOPT_IMAGE_DEPS_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex
+DEXPREOPT_IMAGE_DEPS_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex
+DEXPREOPT_IMAGE_DEPS_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex
DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm=%[1]s
DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm64=%[1]s
DEXPREOPT_IMAGE_LICENSE_METADATA_art_host_x86=%[1]s
@@ -728,39 +1227,58 @@
DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86_64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEart=/system/framework/boot.art
-DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEboot=/system/framework/boot.art:/system/framework/boot-framework.art
-DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTart=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/boot.art
-DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/boot.art:out/soong/test_device/dex_bootjars/android/system/framework/boot-framework.art
-DEXPREOPT_IMAGE_NAMES=art boot
-DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/test_device/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof out/soong/test_device/dex_bootjars/boot.prof:/system/etc/boot-image.prof
+DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86_64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
+DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEart=/apex/art_boot_images/javalib/boot.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEboot=/system/framework/boot.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEmainline=/system/framework/boot.art:/system/framework/boot-framework-foo.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTart=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/boot.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTmainline=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art:out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/boot-framework-foo.art
+DEXPREOPT_IMAGE_NAMES=art boot mainline
+DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof out/soong/dexpreopt_arm64/dex_bootjars/boot.prof:/system/etc/boot-image.prof
DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86_64=out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm=out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm64=out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86=out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86_64=out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex:/apex/art_boot_images/javalib/arm/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex:/apex/art_boot_images/javalib/arm/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex:/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex:/apex/art_boot_images/javalib/arm64/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex:/apex/art_boot_images/javalib/x86/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex:/apex/art_boot_images/javalib/x86/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex:/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex:/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex:/system/framework/arm/boot-framework.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex:/system/framework/arm64/boot-framework.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex:/system/framework/x86/boot-framework.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex:/system/framework/x86_64/boot-framework.vdex
-DEXPREOPT_IMAGE_ZIP_art=out/soong/test_device/dex_artjars/art.zip
-DEXPREOPT_IMAGE_ZIP_boot=out/soong/test_device/dex_bootjars/boot.zip
-DEXPREOPT_IMAGE_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art
-DEXPREOPT_IMAGE_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art
-DEXPREOPT_IMAGE_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art
-DEXPREOPT_IMAGE_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art
-DEXPREOPT_IMAGE_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art
-DEXPREOPT_IMAGE_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art
-DEXPREOPT_IMAGE_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art
-DEXPREOPT_IMAGE_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot.oat:/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-core2.oat:/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat:/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-core2.oat:/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot.oat:/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-core2.oat:/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot.oat:/system/framework/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-core2.oat:/system/framework/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm/boot-framework-foo.oat:/system/framework/arm/boot-framework-foo.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex:/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex:/apex/art_boot_images/javalib/arm/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex:/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex:/apex/art_boot_images/javalib/arm64/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex:/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex:/apex/art_boot_images/javalib/x86/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex:/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex:/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex:/system/framework/arm/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex:/system/framework/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex:/system/framework/arm/boot-framework.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex:/system/framework/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex:/system/framework/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex:/system/framework/arm64/boot-framework.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex:/system/framework/x86/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex:/system/framework/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex:/system/framework/x86/boot-framework.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex:/system/framework/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex:/system/framework/x86_64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex:/system/framework/x86_64/boot-framework.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex:/system/framework/arm/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex:/system/framework/arm64/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex:/system/framework/x86/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex:/system/framework/x86_64/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_ZIP_art=out/soong/dexpreopt_arm64/dex_artjars/art.zip
+DEXPREOPT_IMAGE_ZIP_boot=out/soong/dexpreopt_arm64/dex_bootjars/boot.zip
+DEXPREOPT_IMAGE_ZIP_mainline=out/soong/dexpreopt_arm64/dex_mainlinejars/mainline.zip
+DEXPREOPT_IMAGE_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art
+DEXPREOPT_IMAGE_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art
+DEXPREOPT_IMAGE_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art
+DEXPREOPT_IMAGE_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art
+DEXPREOPT_IMAGE_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art
+DEXPREOPT_IMAGE_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art
+DEXPREOPT_IMAGE_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art
+DEXPREOPT_IMAGE_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art
+DEXPREOPT_IMAGE_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art
+DEXPREOPT_IMAGE_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art
+DEXPREOPT_IMAGE_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art
+DEXPREOPT_IMAGE_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art
`
expected := strings.TrimSpace(fmt.Sprintf(format, expectedLicenseMetadataFile))
actual := strings.TrimSpace(out.String())
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 1c1070a..fedd564 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -63,6 +63,7 @@
java_binary {
name: "foo",
srcs: ["a.java"],
+ main_class: "foo.bar.jb",
}`,
enabled: true,
},
@@ -211,7 +212,7 @@
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
preparers := android.GroupFixturePreparers(
- PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithDexpreopt,
PrepareForTestWithFakeApexMutator,
dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
)
@@ -256,7 +257,7 @@
preparers := android.GroupFixturePreparers(
cc.PrepareForTestWithCcDefaultModules,
- PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd,
+ PrepareForTestWithDexpreoptWithoutFakeDex2oatd,
dexpreopt.PrepareForTestByEnablingDexpreopt)
testDex2oatToolDep := func(sourceEnabled, prebuiltEnabled, prebuiltPreferred bool,
@@ -298,7 +299,7 @@
func TestDexpreoptBuiltInstalledForApex(t *testing.T) {
preparers := android.GroupFixturePreparers(
- PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithDexpreopt,
PrepareForTestWithFakeApexMutator,
dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
)
@@ -385,7 +386,7 @@
func TestAndroidMkEntriesForApex(t *testing.T) {
preparers := android.GroupFixturePreparers(
- PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithDexpreopt,
PrepareForTestWithFakeApexMutator,
dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
)
@@ -437,3 +438,28 @@
android.AssertIntEquals(t, "entries count", 0, len(entriesList))
}
+
+func TestGenerateProfileEvenIfDexpreoptIsDisabled(t *testing.T) {
+ preparers := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithFakeApexMutator,
+ dexpreopt.FixtureDisableDexpreopt(true),
+ )
+
+ result := preparers.RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ installable: true,
+ dex_preopt: {
+ profile: "art-profile",
+ },
+ srcs: ["a.java"],
+ }`)
+
+ ctx := result.TestContext
+ dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
+
+ expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/profile.prof"}
+
+ android.AssertArrayString(t, "outputs", expected, dexpreopt.AllOutputs())
+}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 0c8908e..dbe021d 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -248,12 +248,16 @@
return proptools.String(j.properties.System_modules)
}
-func (j *Javadoc) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
- return j.SdkVersion(ctx)
+func (j *Javadoc) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ return j.SdkVersion(ctx).ApiLevel
}
-func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
- return j.SdkVersion(ctx)
+func (j *Javadoc) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
+ return j.SdkVersion(ctx).ApiLevel
+}
+
+func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ return j.SdkVersion(ctx).ApiLevel
}
func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
@@ -263,7 +267,7 @@
ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
- ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
+ ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
}
}
@@ -300,6 +304,9 @@
flags = append(flags, "-I"+src.String())
}
+ minSdkVersion := j.MinSdkVersion(ctx).FinalOrFutureInt()
+ flags = append(flags, fmt.Sprintf("--min_sdk_version=%v", minSdkVersion))
+
return strings.Join(flags, " "), deps
}
@@ -310,7 +317,7 @@
outSrcFiles := make(android.Paths, 0, len(srcFiles))
var aidlSrcs android.Paths
- aidlIncludeFlags := genAidlIncludeFlags(srcFiles)
+ aidlIncludeFlags := genAidlIncludeFlags(ctx, srcFiles, android.Paths{})
for _, srcFile := range srcFiles {
switch srcFile.Ext() {
@@ -363,7 +370,7 @@
} else {
panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
}
- case libTag:
+ case libTag, sdkLibTag:
if dep, ok := module.(SdkLibraryDependency); ok {
deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
} else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
@@ -595,8 +602,13 @@
Flag("-J-Xmx1600m").
Flag("-J-XX:-OmitStackTraceInFastThrow").
Flag("-XDignore.symbol.file").
+ Flag("--ignore-source-errors").
FlagWithArg("-doclet ", "com.google.doclava.Doclava").
FlagWithInputList("-docletpath ", docletPath.Paths(), ":").
+ FlagWithArg("-Xmaxerrs ", "10").
+ FlagWithArg("-Xmaxwarns ", "10").
+ Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED").
+ Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED").
FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-$(cat "+buildNumberFile.String()+")").OrderOnly(buildNumberFile).
FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
@@ -751,6 +763,7 @@
return rule.Command().
BuiltTool("dokka").
Flag(config.JavacVmFlags).
+ Flag("-J--add-opens=java.base/java.lang=ALL-UNNAMED").
Flag(srcJarDir.String()).
FlagWithInputList("-classpath ", dokkaClasspath, ":").
FlagWithArg("-format ", "dac").
diff --git a/java/droidstubs.go b/java/droidstubs.go
index ec554ac..8a521aa 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -17,17 +17,20 @@
import (
"fmt"
"path/filepath"
+ "regexp"
+ "sort"
"strings"
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/java/config"
"android/soong/remoteexec"
)
// The values allowed for Droidstubs' Api_levels_sdk_type
-var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib"}
+var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"}
func init() {
RegisterStubsBuildComponents(android.InitRegistrationContext)
@@ -45,7 +48,6 @@
// Droidstubs
type Droidstubs struct {
Javadoc
- android.SdkBase
properties DroidstubsProperties
apiFile android.Path
@@ -127,10 +129,13 @@
// 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
- // the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system' and 'module-lib' for now; defaults to public.
+ // the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system', 'module-lib' and 'system-server'; defaults to public.
Api_levels_sdk_type *string
// the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
@@ -139,6 +144,14 @@
// if set to true, collect the values used by the Dev tools and
// write them in files packaged with the SDK. Defaults to false.
Write_sdk_values *bool
+
+ // path or filegroup to file defining extension an SDK name <-> numerical ID mapping and
+ // what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info
+ Extensions_info_file *string `android:"path"`
+
+ // API surface of this module. If set, the module contributes to an API surface.
+ // For the full list of available API surfaces, refer to soong/android/sdk_version.go
+ Api_surface *string
}
// Used by xsd_config
@@ -169,7 +182,10 @@
&module.Javadoc.properties)
InitDroiddocModule(module, android.HostAndDeviceSupported)
- android.InitSdkAwareModule(module)
+
+ module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
+ module.createApiContribution(ctx)
+ })
return module
}
@@ -226,6 +242,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)
@@ -247,6 +264,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) {
@@ -357,28 +378,53 @@
}
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")
var dirs []string
+ var extensions_dir string
ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
if t, ok := m.(*ExportedDroiddocDir); ok {
+ extRegex := regexp.MustCompile(t.dir.String() + `/extensions/[0-9]+/public/.*\.jar`)
+
+ // Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps;
+ // ideally this should be read from prebuiltApis.properties.Extensions_*
for _, dep := range t.deps {
+ if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil {
+ if extensions_dir == "" {
+ extensions_dir = t.dir.String() + "/extensions"
+ }
+ cmd.Implicit(dep)
+ }
if dep.Base() == filename {
cmd.Implicit(dep)
}
@@ -407,6 +453,8 @@
// for older releases. Similarly, module-lib falls back to system API.
var sdkDirs []string
switch proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") {
+ case "system-server":
+ sdkDirs = []string{"system-server", "module-lib", "system", "public"}
case "module-lib":
sdkDirs = []string{"module-lib", "system", "public"}
case "system":
@@ -423,6 +471,16 @@
cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename))
}
}
+
+ if d.properties.Extensions_info_file != nil {
+ if extensions_dir == "" {
+ ctx.ModuleErrorf("extensions_info_file set, but no SDK extension dirs found")
+ }
+ info_file := android.PathForModuleSrc(ctx, *d.properties.Extensions_info_file)
+ cmd.Implicit(info_file)
+ cmd.FlagWithArg("--sdk-extensions-root ", extensions_dir)
+ cmd.FlagWithArg("--sdk-extensions-info ", info_file.String())
+ }
}
func metalavaUseRbe(ctx android.ModuleContext) bool {
@@ -784,6 +842,95 @@
}
}
+var _ android.ApiProvider = (*Droidstubs)(nil)
+
+type bazelJavaApiContributionAttributes struct {
+ Api bazel.LabelAttribute
+ Api_surface *string
+}
+
+func (d *Droidstubs) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "java_api_contribution",
+ Bzl_load_location: "//build/bazel/rules/apis:java_api_contribution.bzl",
+ }
+ apiFile := d.properties.Check_api.Current.Api_file
+ // Do not generate a target if check_api is not set
+ if apiFile == nil {
+ return
+ }
+ attrs := &bazelJavaApiContributionAttributes{
+ Api: *bazel.MakeLabelAttribute(
+ android.BazelLabelForModuleSrcSingle(ctx, proptools.String(apiFile)).Label,
+ ),
+ Api_surface: proptools.StringPtr(bazelApiSurfaceName(d.Name())),
+ }
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: android.ApiContributionTargetName(ctx.ModuleName()),
+ }, attrs)
+}
+
+func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) {
+ api_file := d.properties.Check_api.Current.Api_file
+ api_surface := d.properties.Api_surface
+
+ props := struct {
+ Name *string
+ Api_surface *string
+ Api_file *string
+ Visibility []string
+ }{}
+
+ props.Name = proptools.StringPtr(d.Name() + ".api.contribution")
+ props.Api_surface = api_surface
+ props.Api_file = api_file
+ props.Visibility = []string{"//visibility:override", "//visibility:public"}
+
+ ctx.CreateModule(ApiContributionFactory, &props)
+}
+
+// TODO (b/262014796): Export the API contributions of CorePlatformApi
+// A map to populate the api surface of a droidstub from a substring appearing in its name
+// This map assumes that droidstubs (either checked-in or created by java_sdk_library)
+// use a strict naming convention
+var (
+ droidstubsModuleNamingToSdkKind = map[string]android.SdkKind{
+ //public is commented out since the core libraries use public in their java_sdk_library names
+ "intracore": android.SdkIntraCore,
+ "intra.core": android.SdkIntraCore,
+ "system_server": android.SdkSystemServer,
+ "system-server": android.SdkSystemServer,
+ "system": android.SdkSystem,
+ "module_lib": android.SdkModule,
+ "module-lib": android.SdkModule,
+ "platform.api": android.SdkCorePlatform,
+ "test": android.SdkTest,
+ "toolchain": android.SdkToolchain,
+ }
+)
+
+// A helper function that returns the api surface of the corresponding java_api_contribution Bazel target
+// The api_surface is populated using the naming convention of the droidstubs module.
+func bazelApiSurfaceName(name string) string {
+ // Sort the keys so that longer strings appear first
+ // Otherwise substrings like system will match both system and system_server
+ sortedKeys := make([]string, 0)
+ for key := range droidstubsModuleNamingToSdkKind {
+ sortedKeys = append(sortedKeys, key)
+ }
+ sort.Slice(sortedKeys, func(i, j int) bool {
+ return len(sortedKeys[i]) > len(sortedKeys[j])
+ })
+ for _, sortedKey := range sortedKeys {
+ if strings.Contains(name, sortedKey) {
+ sdkKind := droidstubsModuleNamingToSdkKind[sortedKey]
+ return sdkKind.String() + "api"
+ }
+ }
+ // Default is publicapi
+ return android.SdkPublic.String() + "api"
+}
+
func StubsDefaultsFactory() android.Module {
module := &DocDefaults{}
@@ -807,7 +954,6 @@
android.ModuleBase
android.DefaultableModuleBase
prebuilt android.Prebuilt
- android.SdkBase
properties PrebuiltStubsSourcesProperties
@@ -887,7 +1033,6 @@
module.AddProperties(&module.properties)
android.InitPrebuiltModule(module, &module.properties.Srcs)
- android.InitSdkAwareModule(module)
InitDroiddocModule(module, android.HostAndDeviceSupported)
return module
}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 10d99f3..7a04d73 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")
@@ -105,7 +122,7 @@
"some-other-exported-dir",
],
api_levels_annotations_enabled: true,
- api_levels_sdk_type: "%s",
+ api_levels_sdk_type: "%s",
}
`, sdkType),
map[string][]byte{
@@ -152,6 +169,21 @@
}, patterns)
}
+func TestSystemServerDroidstubs(t *testing.T) {
+ patterns := getAndroidJarPatternsForDroidstubs(t, "system-server")
+
+ android.AssertArrayString(t, "order of patterns", []string{
+ "--android-jar-pattern somedir/%/system-server/android.jar",
+ "--android-jar-pattern someotherdir/%/system-server/android.jar",
+ "--android-jar-pattern somedir/%/module-lib/android.jar",
+ "--android-jar-pattern someotherdir/%/module-lib/android.jar",
+ "--android-jar-pattern somedir/%/system/android.jar",
+ "--android-jar-pattern someotherdir/%/system/android.jar",
+ "--android-jar-pattern somedir/%/public/android.jar",
+ "--android-jar-pattern someotherdir/%/public/android.jar",
+ }, patterns)
+}
+
func TestDroidstubsSandbox(t *testing.T) {
ctx, _ := testJavaWithFS(t, `
genrule {
@@ -242,3 +274,132 @@
t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
}
}
+
+func TestDroidstubsWithSdkExtensions(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ droiddoc_exported_dir {
+ name: "sdk-dir",
+ path: "sdk",
+ }
+
+ droidstubs {
+ name: "baz-stubs",
+ api_levels_annotations_dirs: ["sdk-dir"],
+ api_levels_annotations_enabled: true,
+ extensions_info_file: ":info-file",
+ }
+
+ filegroup {
+ name: "info-file",
+ srcs: ["sdk/extensions/info.txt"],
+ }
+ `,
+ map[string][]byte{
+ "sdk/extensions/1/public/some-mainline-module-stubs.jar": nil,
+ "sdk/extensions/info.txt": nil,
+ })
+ m := ctx.ModuleForTests("baz-stubs", "android_common")
+ manifest := m.Output("metalava.sbox.textproto")
+ cmdline := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
+ android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions")
+ android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt")
+}
+
+func TestApiSurfaceFromDroidStubsName(t *testing.T) {
+ testCases := []struct {
+ desc string
+ name string
+ expectedApiSurface string
+ }{
+ {
+ desc: "Default is publicapi",
+ name: "mydroidstubs",
+ expectedApiSurface: "publicapi",
+ },
+ {
+ desc: "name contains system substring",
+ name: "mydroidstubs.system.suffix",
+ expectedApiSurface: "systemapi",
+ },
+ {
+ desc: "name contains system_server substring",
+ name: "mydroidstubs.system_server.suffix",
+ expectedApiSurface: "system-serverapi",
+ },
+ {
+ desc: "name contains module_lib substring",
+ name: "mydroidstubs.module_lib.suffix",
+ expectedApiSurface: "module-libapi",
+ },
+ {
+ desc: "name contains test substring",
+ name: "mydroidstubs.test.suffix",
+ expectedApiSurface: "testapi",
+ },
+ {
+ desc: "name contains intra.core substring",
+ name: "mydroidstubs.intra.core.suffix",
+ expectedApiSurface: "intracoreapi",
+ },
+ }
+ for _, tc := range testCases {
+ android.AssertStringEquals(t, tc.desc, tc.expectedApiSurface, bazelApiSurfaceName(tc.name))
+ }
+}
+
+func TestDroidStubsApiContributionGeneration(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ droidstubs {
+ name: "foo",
+ srcs: ["A/a.java"],
+ api_surface: "public",
+ check_api: {
+ current: {
+ api_file: "A/current.txt",
+ removed_api_file: "A/removed.txt",
+ }
+ }
+ }
+ `,
+ map[string][]byte{
+ "A/a.java": nil,
+ "A/current.txt": nil,
+ "A/removed.txt": nil,
+ },
+ )
+
+ ctx.ModuleForTests("foo.api.contribution", "")
+}
+
+func TestGeneratedApiContributionVisibilityTest(t *testing.T) {
+ library_bp := `
+ java_api_library {
+ name: "bar",
+ api_surface: "public",
+ api_contributions: ["foo.api.contribution"],
+ }
+ `
+ ctx, _ := testJavaWithFS(t, `
+ droidstubs {
+ name: "foo",
+ srcs: ["A/a.java"],
+ api_surface: "public",
+ check_api: {
+ current: {
+ api_file: "A/current.txt",
+ removed_api_file: "A/removed.txt",
+ }
+ },
+ visibility: ["//a"],
+ }
+ `,
+ map[string][]byte{
+ "a/a.java": nil,
+ "a/current.txt": nil,
+ "a/removed.txt": nil,
+ "b/Android.bp": []byte(library_bp),
+ },
+ )
+
+ ctx.ModuleForTests("bar", "android_common")
+}
diff --git a/java/fuzz.go b/java/fuzz.go
index 257f343..2b9e22b 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -15,12 +15,21 @@
package java
import (
- "github.com/google/blueprint/proptools"
+ "path/filepath"
"sort"
"strings"
"android/soong/android"
+ "android/soong/cc"
"android/soong/fuzz"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+const (
+ hostString = "host"
+ targetString = "target"
)
func init() {
@@ -28,47 +37,30 @@
}
func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("java_fuzz_host", FuzzFactory)
+ ctx.RegisterModuleType("java_fuzz", JavaFuzzFactory)
ctx.RegisterSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory)
}
-type JavaFuzzLibrary struct {
- Library
+type JavaFuzzTest struct {
+ Test
fuzzPackagedModule fuzz.FuzzPackagedModule
+ jniFilePaths android.Paths
}
-func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- j.Library.GenerateAndroidBuildActions(ctx)
+// java_fuzz builds and links sources into a `.jar` file for the device.
+// This generates .class files in a jar which can then be instrumented before
+// fuzzing in Android Runtime (ART: Android OS on emulator or device)
+func JavaFuzzFactory() android.Module {
+ module := &JavaFuzzTest{}
- if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
- j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
- }
- if j.fuzzPackagedModule.FuzzProperties.Data != nil {
- j.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Data)
- }
- if j.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
- j.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *j.fuzzPackagedModule.FuzzProperties.Dictionary)
- }
-
- if j.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
- configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
- android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
- j.fuzzPackagedModule.Config = configPath
- }
-}
-
-// java_fuzz builds and links sources into a `.jar` file for the host.
-//
-// By default, a java_fuzz produces a `.jar` file containing `.class` files.
-// This jar is not suitable for installing on a device.
-func FuzzFactory() android.Module {
- module := &JavaFuzzLibrary{}
-
- module.addHostProperties()
- module.Module.properties.Installable = proptools.BoolPtr(false)
+ module.addHostAndDeviceProperties()
+ module.AddProperties(&module.testProperties)
module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
- // java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants.
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+ module.Module.dexpreopter.isTest = true
+ module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+
android.AddLoadHook(module, func(ctx android.LoadHookContext) {
disableLinuxBionic := struct {
Target struct {
@@ -81,14 +73,62 @@
ctx.AppendProperties(&disableLinuxBionic)
})
- module.initModuleAndImport(module)
- android.InitSdkAwareModule(module)
- InitJavaModule(module, android.HostSupported)
+ InitJavaModuleMultiTargets(module, android.HostAndDeviceSupported)
return module
}
-// Responsible for generating rules that package fuzz targets into
-// their architecture & target/host specific zip file.
+func (j *JavaFuzzTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+ if len(j.testProperties.Jni_libs) > 0 {
+ for _, target := range ctx.MultiTargets() {
+ sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
+ ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...)
+ }
+ }
+
+ j.deps(ctx)
+}
+
+func (j *JavaFuzzTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
+ j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
+ }
+ if j.fuzzPackagedModule.FuzzProperties.Data != nil {
+ j.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Data)
+ }
+ if j.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
+ j.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *j.fuzzPackagedModule.FuzzProperties.Dictionary)
+ }
+ if j.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
+ configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
+ android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
+ j.fuzzPackagedModule.Config = configPath
+ }
+
+ _, sharedDeps := cc.CollectAllSharedDependencies(ctx)
+ for _, dep := range sharedDeps {
+ sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo)
+ if sharedLibInfo.SharedLibrary != nil {
+ arch := "lib"
+ if sharedLibInfo.Target.Arch.ArchType.Multilib == "lib64" {
+ arch = "lib64"
+ }
+
+ libPath := android.PathForModuleOut(ctx, filepath.Join(arch, 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.Test.GenerateAndroidBuildActions(ctx)
+}
+
type javaFuzzPackager struct {
fuzz.FuzzPackager
}
@@ -101,49 +141,51 @@
// Map between each architecture + host/device combination.
archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
- // List of individual fuzz targets.
s.FuzzTargets = make(map[string]bool)
-
ctx.VisitAllModules(func(module android.Module) {
// Discard non-fuzz targets.
- javaModule, ok := module.(*JavaFuzzLibrary)
+ javaFuzzModule, ok := module.(*JavaFuzzTest)
if !ok {
return
}
- fuzzModuleValidator := fuzz.FuzzModule{
- javaModule.ModuleBase,
- javaModule.DefaultableModuleBase,
- javaModule.ApexModuleBase,
+ hostOrTargetString := "target"
+ if javaFuzzModule.Host() {
+ hostOrTargetString = "host"
}
- if ok := fuzz.IsValid(fuzzModuleValidator); !ok || *javaModule.Module.properties.Installable {
+ fuzzModuleValidator := fuzz.FuzzModule{
+ javaFuzzModule.ModuleBase,
+ javaFuzzModule.DefaultableModuleBase,
+ javaFuzzModule.ApexModuleBase,
+ }
+
+ if ok := fuzz.IsValid(fuzzModuleValidator); !ok {
return
}
- hostOrTargetString := "target"
- if javaModule.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()}
var files []fuzz.FileToZip
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)
+ // Package the artifacts (data, corpus, config and dictionary) into a zipfile.
+ files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder)
// Add .jar
- files = append(files, fuzz.FileToZip{javaModule.outputFile, ""})
+ files = append(files, fuzz.FileToZip{javaFuzzModule.implementationJarFile, ""})
- 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
}
-
})
s.CreateFuzzPackage(ctx, archDirs, fuzz.Java, pctx)
}
@@ -151,9 +193,7 @@
func (s *javaFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
packages := s.Packages.Strings()
sort.Strings(packages)
-
ctx.Strict("SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
-
// Preallocate the slice of fuzz targets to minimize memory allocations.
s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_TARGETS")
}
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
index cf063eb..dd1e96b 100644
--- a/java/fuzz_test.go
+++ b/java/fuzz_test.go
@@ -15,23 +15,32 @@
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),
)
func TestJavaFuzz(t *testing.T) {
result := prepForJavaFuzzTest.RunTestWithBp(t, `
- java_fuzz_host {
+ java_fuzz {
name: "foo",
srcs: ["a.java"],
+ host_supported: true,
+ device_supported: false,
libs: ["bar"],
static_libs: ["baz"],
+ jni_libs: [
+ "libjni",
+ ],
}
java_library_host {
@@ -42,9 +51,18 @@
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")
@@ -62,4 +80,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", osCommonTarget).Module().(*JavaFuzzTest)
+
+ expected := "lib64/libjni.so"
+ if runtime.GOOS == "darwin" {
+ expected = "lib64/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 1572bf0..638da25 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -15,6 +15,7 @@
package java
import (
+ "path/filepath"
"strconv"
"strings"
@@ -116,12 +117,31 @@
return javaFile
}
-func genAidlIncludeFlags(srcFiles android.Paths) string {
+// genAidlIncludeFlags returns additional include flags based on the relative path
+// of each .aidl file passed in srcFiles. excludeDirs is a list of paths relative to
+// the Android checkout root that should not be included in the returned flags.
+func genAidlIncludeFlags(ctx android.PathContext, srcFiles android.Paths, excludeDirs android.Paths) string {
var baseDirs []string
+ excludeDirsStrings := excludeDirs.Strings()
for _, srcFile := range srcFiles {
if srcFile.Ext() == ".aidl" {
baseDir := strings.TrimSuffix(srcFile.String(), srcFile.Rel())
- if baseDir != "" && !android.InList(baseDir, baseDirs) {
+ baseDir = filepath.Clean(baseDir)
+ baseDirSeen := android.InList(baseDir, baseDirs) || android.InList(baseDir, excludeDirsStrings)
+
+ // For go/bp2build mixed builds, a file may be listed under a
+ // directory in the Bazel output tree that is symlinked to a
+ // directory under the android source tree. We should only
+ // include one copy of this directory so that the AIDL tool
+ // doesn't find multiple definitions of the same AIDL class.
+ // This code comes into effect when filegroups are used in mixed builds.
+ bazelPathPrefix := android.PathForBazelOut(ctx, "").String()
+ bazelBaseDir, err := filepath.Rel(bazelPathPrefix, baseDir)
+ bazelBaseDirSeen := err == nil &&
+ android.InList(bazelBaseDir, baseDirs) ||
+ android.InList(bazelBaseDir, excludeDirsStrings)
+
+ if baseDir != "" && !baseDirSeen && !bazelBaseDirSeen {
baseDirs = append(baseDirs, baseDir)
}
}
@@ -136,8 +156,6 @@
var protoSrcs android.Paths
var aidlSrcs android.Paths
- aidlIncludeFlags := genAidlIncludeFlags(srcFiles)
-
for _, srcFile := range srcFiles {
switch srcFile.Ext() {
case ".aidl":
@@ -168,7 +186,7 @@
individualFlags[aidlSrc.String()] = flags
}
}
- srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, individualFlags, flags.aidlDeps)
+ srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags, individualFlags, flags.aidlDeps)
outSrcFiles = append(outSrcFiles, srcJarFiles...)
}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index cf9c7ad..d25096b 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -20,10 +20,17 @@
"android/soong/android"
)
-var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", blueprint.RuleParams{
- Command: "${config.Class2NonSdkList} --stub-api-flags ${stubAPIFlags} $in $outFlag $out",
- CommandDeps: []string{"${config.Class2NonSdkList}"},
-}, "outFlag", "stubAPIFlags")
+var (
+ hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", blueprint.RuleParams{
+ Command: "${config.Class2NonSdkList} --stub-api-flags ${stubAPIFlags} $in $outFlag $out",
+ CommandDeps: []string{"${config.Class2NonSdkList}"},
+ }, "outFlag", "stubAPIFlags")
+
+ hiddenAPIGenerateIndexRule = pctx.AndroidStaticRule("hiddenAPIGenerateIndex", blueprint.RuleParams{
+ Command: "${config.MergeCsvCommand} --zip_input --key_field signature --output=$out $in",
+ CommandDeps: []string{"${config.MergeCsvCommand}"},
+ })
+)
type hiddenAPI struct {
// True if the module containing this structure contributes to the hiddenapi information or has
@@ -66,7 +73,7 @@
android.Module
hiddenAPIIntf
- MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+ MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
}
type hiddenAPIIntf interface {
@@ -150,7 +157,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, android.SdkSpecNone, outputDir)
+ encodedDex := hiddenAPIEncodeDex(ctx, dexJar, flagsCSV, uncompressDex, android.NoneApiLevel, outputDir)
// Use the encoded dex jar from here onwards.
return encodedDex
@@ -216,14 +223,12 @@
// created by the unsupported app usage annotation processor during compilation of the class
// implementation jar.
func buildRuleToGenerateIndex(ctx android.ModuleContext, desc string, classesJars android.Paths, indexCSV android.WritablePath) {
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("merge_csv").
- Flag("--zip_input").
- Flag("--key_field signature").
- FlagWithOutput("--output=", indexCSV).
- Inputs(classesJars)
- rule.Build(desc, desc)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: hiddenAPIGenerateIndexRule,
+ Description: desc,
+ Inputs: classesJars,
+ Output: indexCSV,
+ })
}
var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
@@ -248,7 +253,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, minSdkVersion android.SdkSpec, outputDir android.OutputPath) android.OutputPath {
+func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Path, uncompressDex bool, minSdkVersion android.ApiLevel, 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())
@@ -278,7 +283,7 @@
// 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
+ minApiLevel := minSdkVersion
if !minApiLevel.IsNone() {
if minApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(ctx, "R")) {
hiddenapiFlags = hiddenapiFlags + " --max-hiddenapi-level=max-target-r"
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 5474ae1..c6176e6 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -236,9 +236,9 @@
testStubModules = append(testStubModules, "sdk_test_current_android")
} else {
// Use stub modules built from source
- publicStubModules = append(publicStubModules, "android_stubs_current")
- systemStubModules = append(systemStubModules, "android_system_stubs_current")
- testStubModules = append(testStubModules, "android_test_stubs_current")
+ publicStubModules = append(publicStubModules, android.SdkPublic.JavaLibraryName(config))
+ systemStubModules = append(systemStubModules, android.SdkSystem.JavaLibraryName(config))
+ testStubModules = append(testStubModules, android.SdkTest.JavaLibraryName(config))
}
// We do not have prebuilts of the core platform api yet
corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
@@ -697,7 +697,7 @@
// The relative width of APIs is determined by their order in hiddenAPIScopes.
func (s StubDexJarsByModule) StubDexJarsForWidestAPIScope() android.Paths {
stubDexJars := android.Paths{}
- modules := android.SortedStringKeys(s)
+ modules := android.SortedKeys(s)
for _, module := range modules {
stubDexJarsByScope := s[module]
@@ -714,7 +714,7 @@
// the returned list.
func (s StubDexJarsByModule) StubDexJarsForScope(scope *HiddenAPIScope) android.Paths {
stubDexJars := android.Paths{}
- modules := android.SortedStringKeys(s)
+ modules := android.SortedKeys(s)
for _, module := range modules {
stubDexJarsByScope := s[module]
// Not every module will have the same set of
@@ -917,7 +917,7 @@
// bootDexJars returns the boot dex jar paths sorted by their keys.
func (b bootDexJarByModule) bootDexJars() android.Paths {
paths := android.Paths{}
- for _, k := range android.SortedStringKeys(b) {
+ for _, k := range android.SortedKeys(b) {
paths = append(paths, b[k])
}
return paths
@@ -927,7 +927,7 @@
// libraries if present.
func (b bootDexJarByModule) bootDexJarsWithoutCoverage() android.Paths {
paths := android.Paths{}
- for _, k := range android.SortedStringKeys(b) {
+ for _, k := range android.SortedKeys(b) {
if k == "jacocoagent" {
continue
}
@@ -1217,7 +1217,7 @@
// Encode the flags into the boot dex files.
encodedBootDexJarsByModule := bootDexJarByModule{}
outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
- for _, name := range android.SortedStringKeys(bootDexInfoByModule) {
+ for _, name := range android.SortedKeys(bootDexInfoByModule) {
bootDexInfo := bootDexInfoByModule[name]
unencodedDex := bootDexInfo.path
encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, bootDexInfo.minSdkVersion, outputDir)
@@ -1278,7 +1278,7 @@
uncompressDex bool
// The minimum sdk version that the dex jar will be used on.
- minSdkVersion android.SdkSpec
+ minSdkVersion android.ApiLevel
}
// bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex
@@ -1288,7 +1288,7 @@
// bootDexJars returns the boot dex jar paths sorted by their keys.
func (b bootDexInfoByModule) bootDexJars() android.Paths {
paths := android.Paths{}
- for _, m := range android.SortedStringKeys(b) {
+ for _, m := range android.SortedKeys(b) {
paths = append(paths, b[m].path)
}
return paths
@@ -1352,14 +1352,6 @@
return true
}
- // A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there
- // is no equivalently versioned prebuilt APEX file from which it can be obtained. However,
- // versioned bootclasspath modules are processed by Soong so in order to avoid them causing build
- // failures missing boot dex jars need to be deferred.
- if android.IsModuleInVersionedSdk(ctx.Module()) {
- return true
- }
-
// This is called for both platform_bootclasspath and bootclasspath_fragment modules.
//
// A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules.
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 75b7bb7..ef792f9 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -39,7 +39,9 @@
`)
var hiddenApiFixtureFactory = android.GroupFixturePreparers(
- prepareForJavaTest, PrepareForTestWithHiddenApiBuildComponents)
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithHiddenApiBuildComponents,
+)
func TestHiddenAPISingleton(t *testing.T) {
result := android.GroupFixturePreparers(
diff --git a/java/java.go b/java/java.go
index a07565c..683e546 100644
--- a/java/java.go
+++ b/java/java.go
@@ -24,6 +24,7 @@
"strings"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
"android/soong/remoteexec"
"github.com/google/blueprint"
@@ -213,6 +214,14 @@
PropertyName: "java_tests",
},
}
+
+ // Rule for generating device binary default wrapper
+ deviceBinaryWrapper = pctx.StaticRule("deviceBinaryWrapper", blueprint.RuleParams{
+ Command: `echo -e '#!/system/bin/sh\n` +
+ `export CLASSPATH=/system/framework/$jar_name\n` +
+ `exec app_process /$partition/bin $main_class "$$@"'> ${out}`,
+ Description: "Generating device binary wrapper ${jar_name}",
+ }, "jar_name", "partition", "main_class")
)
// JavaInfo contains information about a java module for use by modules that depend on it.
@@ -221,6 +230,12 @@
// against this module. If empty, ImplementationJars should be used instead.
HeaderJars android.Paths
+ // set of header jars for all transitive libs deps
+ TransitiveLibsHeaderJars *android.DepSet
+
+ // set of header jars for all transitive static libs deps
+ TransitiveStaticLibsHeaderJars *android.DepSet
+
// ImplementationAndResourceJars is a list of jars that contain the implementations of classes
// in the module as well as any resources included in the module.
ImplementationAndResourcesJars android.Paths
@@ -285,6 +300,11 @@
ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
}
+// Provides transitive Proguard flag files to downstream DEX jars.
+type LibraryDependency interface {
+ ExportedProguardFlagFiles() android.Paths
+}
+
// TODO(jungjw): Move this to kythe.go once it's created.
type xref interface {
XrefJavaFiles() android.Paths
@@ -326,19 +346,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),
@@ -346,7 +358,6 @@
},
sdkVersion: sdkVersion,
optional: optional,
- implicit: implicit,
}
}
@@ -359,6 +370,7 @@
dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"}
staticLibTag = dependencyTag{name: "staticlib"}
libTag = dependencyTag{name: "javalib", runtimeLinked: true}
+ sdkLibTag = dependencyTag{name: "sdklib", runtimeLinked: true}
java9LibTag = dependencyTag{name: "java9lib", runtimeLinked: true}
pluginTag = dependencyTag{name: "plugin", toolchain: true}
errorpronePluginTag = dependencyTag{name: "errorprone-plugin", toolchain: true}
@@ -374,13 +386,21 @@
instrumentationForTag = dependencyTag{name: "instrumentation_for"}
extraLintCheckTag = dependencyTag{name: "extra-lint-check", toolchain: true}
jniLibTag = dependencyTag{name: "jnilib", runtimeLinked: true}
+ r8LibraryJarTag = dependencyTag{name: "r8-libraryjar", runtimeLinked: true}
syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
+ javaApiContributionTag = dependencyTag{name: "java-api-contribution"}
+ depApiSrcsTag = dependencyTag{name: "dep-api-srcs"}
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 {
- return depTag == libTag
+ return depTag == libTag || depTag == sdkLibTag
}
func IsStaticLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -426,6 +446,7 @@
target android.Target
coverageFile android.OptionalPath
unstrippedFile android.Path
+ partition string
}
func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) {
@@ -433,7 +454,7 @@
if sdkDep.useModule {
ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
- ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
+ ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
ctx.AddVariationDependencies(nil, proguardRaiseTag, config.LegacyCorePlatformBootclasspathLibraries...)
}
@@ -499,7 +520,7 @@
} else if ctx.Device() {
return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
} else {
- return JAVA_VERSION_11
+ return JAVA_VERSION_17
}
}
@@ -517,6 +538,7 @@
JAVA_VERSION_8 = 8
JAVA_VERSION_9 = 9
JAVA_VERSION_11 = 11
+ JAVA_VERSION_17 = 17
)
func (v javaVersion) String() string {
@@ -531,11 +553,27 @@
return "1.9"
case JAVA_VERSION_11:
return "11"
+ case JAVA_VERSION_17:
+ return "17"
default:
return "unsupported"
}
}
+func (v javaVersion) StringForKotlinc() string {
+ // $ ./external/kotlinc/bin/kotlinc -jvm-target foo
+ // error: unknown JVM target version: foo
+ // Supported versions: 1.6, 1.8, 9, 10, 11, 12, 13, 14, 15, 16, 17
+ switch v {
+ case JAVA_VERSION_7:
+ return "1.6"
+ case JAVA_VERSION_9:
+ return "9"
+ default:
+ return v.String()
+ }
+}
+
// Returns true if javac targeting this version uses system modules instead of a bootclasspath.
func (v javaVersion) usesJavaModules() bool {
return v >= 9
@@ -553,8 +591,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_17
+ 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")
@@ -569,9 +609,17 @@
type Library struct {
Module
+ exportedProguardFlagFiles android.Paths
+
InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths)
}
+var _ LibraryDependency = (*Library)(nil)
+
+func (j *Library) ExportedProguardFlagFiles() android.Paths {
+ return j.exportedProguardFlagFiles
+}
+
var _ android.ApexModule = (*Library)(nil)
// Provides access to the list of permitted packages from apex boot jars.
@@ -630,12 +678,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.
@@ -665,6 +715,15 @@
}
j.installFile = ctx.InstallFile(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...)
}
+
+ j.exportedProguardFlagFiles = append(j.exportedProguardFlagFiles,
+ android.PathsForModuleSrc(ctx, j.dexProperties.Optimize.Proguard_flags_files)...)
+ ctx.VisitDirectDeps(func(m android.Module) {
+ if lib, ok := m.(LibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag {
+ j.exportedProguardFlagFiles = append(j.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...)
+ }
+ })
+ j.exportedProguardFlagFiles = android.FirstUniquePaths(j.exportedProguardFlagFiles)
}
func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -735,6 +794,11 @@
// The list of permitted packages that need to be passed to the prebuilts as they are used to
// create the updatable-bcp-packages.txt file.
PermittedPackages []string
+
+ // The value of the min_sdk_version property, translated into a number where possible.
+ MinSdkVersion *string `supported_build_releases:"Tiramisu+"`
+
+ DexPreoptProfileGuided *bool `supported_build_releases:"UpsideDownCake+"`
}
func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -745,6 +809,17 @@
p.AidlIncludeDirs = j.AidlIncludeDirs()
p.PermittedPackages = j.PermittedPackagesForUpdatableBootJars()
+
+ // If the min_sdk_version was set then add the canonical representation of the API level to the
+ // snapshot.
+ if j.deviceProperties.Min_sdk_version != nil {
+ canonical := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.String())
+ p.MinSdkVersion = proptools.StringPtr(canonical)
+ }
+
+ if j.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
+ p.DexPreoptProfileGuided = proptools.BoolPtr(true)
+ }
}
func (p *librarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -763,10 +838,19 @@
propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
}
+ if p.MinSdkVersion != nil {
+ propertySet.AddProperty("min_sdk_version", *p.MinSdkVersion)
+ }
+
if len(p.PermittedPackages) > 0 {
propertySet.AddProperty("permitted_packages", p.PermittedPackages)
}
+ dexPreoptSet := propertySet.AddPropertySet("dex_preopt")
+ if p.DexPreoptProfileGuided != nil {
+ dexPreoptSet.AddProperty("profile_guided", proptools.Bool(p.DexPreoptProfileGuided))
+ }
+
// Do not copy anything else to the snapshot.
if memberType.onlyCopyJarToSnapshot {
return
@@ -806,7 +890,6 @@
module.initModuleAndImport(module)
android.InitApexModule(module)
- android.InitSdkAwareModule(module)
android.InitBazelModule(module)
InitJavaModule(module, android.HostAndDeviceSupported)
return module
@@ -829,7 +912,6 @@
module.Module.properties.Installable = proptools.BoolPtr(true)
android.InitApexModule(module)
- android.InitSdkAwareModule(module)
android.InitBazelModule(module)
InitJavaModule(module, android.HostSupported)
return module
@@ -841,11 +923,14 @@
// Test option struct.
type TestOptions struct {
+ android.CommonTestOptions
+
// a list of extra test configuration files that should be installed with the module.
Extra_test_configs []string `android:"path,arch_variant"`
- // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
- Unit_test *bool
+ // Extra <option> tags to add to the auto generated test xml file. The "key"
+ // is optional in each of these.
+ Tradefed_options []tradefed.Option
}
type testProperties struct {
@@ -1066,7 +1151,6 @@
}
j.addDataDeviceBinsDeps(ctx)
-
j.deps(ctx)
}
@@ -1123,9 +1207,18 @@
defaultUnitTest := !inList("tradefed", j.properties.Libs) && !inList("cts", j.testProperties.Test_suites)
j.testProperties.Test_options.Unit_test = proptools.BoolPtr(defaultUnitTest)
}
-
- j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template,
- j.testProperties.Test_suites, configs, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test)
+ j.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: j.testProperties.Test_config,
+ TestConfigTemplateProp: j.testProperties.Test_config_template,
+ TestSuites: j.testProperties.Test_suites,
+ Config: configs,
+ OptionsForAutogenerated: j.testProperties.Test_options.Tradefed_options,
+ AutoGenConfig: j.testProperties.Auto_gen_config,
+ UnitTest: j.testProperties.Test_options.Unit_test,
+ DeviceTemplate: "${JavaTestConfigTemplate}",
+ HostTemplate: "${JavaHostTestConfigTemplate}",
+ HostUnitTestTemplate: "${JavaHostUnitTestConfigTemplate}",
+ })
j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
@@ -1170,8 +1263,13 @@
}
func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.prebuiltTestProperties.Test_config, nil,
- j.prebuiltTestProperties.Test_suites, nil, nil, nil)
+ j.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: j.prebuiltTestProperties.Test_config,
+ TestSuites: j.prebuiltTestProperties.Test_suites,
+ DeviceTemplate: "${JavaTestConfigTemplate}",
+ HostTemplate: "${JavaHostTestConfigTemplate}",
+ HostUnitTestTemplate: "${JavaHostUnitTestConfigTemplate}",
+ })
j.Import.GenerateAndroidBuildActions(ctx)
}
@@ -1253,7 +1351,6 @@
module.Module.dexpreopter.isTest = true
module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
- android.InitSdkAwareModule(module)
InitJavaModule(module, android.HostAndDeviceSupported)
return module
}
@@ -1292,7 +1389,6 @@
android.InitPrebuiltModule(module, &module.properties.Jars)
android.InitApexModule(module)
- android.InitSdkAwareModule(module)
InitJavaModule(module, android.HostAndDeviceSupported)
return module
}
@@ -1381,7 +1477,31 @@
ctx.PropertyErrorf("wrapper", "wrapper is required for Windows")
}
- j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
+ if ctx.Device() {
+ // device binary should have a main_class property if it does not
+ // have a specific wrapper, so that a default wrapper can
+ // be generated for it.
+ if j.binaryProperties.Main_class == nil {
+ ctx.PropertyErrorf("main_class", "main_class property "+
+ "is required for device binary if no default wrapper is assigned")
+ } else {
+ wrapper := android.PathForModuleOut(ctx, ctx.ModuleName()+".sh")
+ jarName := j.Stem() + ".jar"
+ partition := j.PartitionTag(ctx.DeviceConfig())
+ ctx.Build(pctx, android.BuildParams{
+ Rule: deviceBinaryWrapper,
+ Output: wrapper,
+ Args: map[string]string{
+ "jar_name": jarName,
+ "partition": partition,
+ "main_class": String(j.binaryProperties.Main_class),
+ },
+ })
+ j.wrapperFile = wrapper
+ }
+ } else {
+ j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
+ }
}
ext := ""
@@ -1398,10 +1518,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...)
@@ -1480,20 +1600,38 @@
var JavaApiImportProvider = blueprint.NewProvider(JavaApiImportInfo{})
func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- apiFile := android.PathForModuleSrc(ctx, String(ap.properties.Api_file))
+ var apiFile android.Path = nil
+ if apiFileString := ap.properties.Api_file; apiFileString != nil {
+ apiFile = android.PathForModuleSrc(ctx, String(apiFileString))
+ }
+
ctx.SetProvider(JavaApiImportProvider, JavaApiImportInfo{
ApiFile: apiFile,
})
}
+type JavaApiLibraryDepsInfo struct {
+ JavaInfo
+ StubsSrcJar android.Path
+}
+
+var JavaApiLibraryDepsProvider = blueprint.NewProvider(JavaApiLibraryDepsInfo{})
+
type ApiLibrary struct {
android.ModuleBase
android.DefaultableModuleBase
+ hiddenAPI
+ dexer
+
properties JavaApiLibraryProperties
- stubsSrcJar android.WritablePath
- stubsJar android.WritablePath
+ stubsSrcJar android.WritablePath
+ stubsJar android.WritablePath
+ stubsJarWithoutStaticLibs android.WritablePath
+ extractedSrcJar android.WritablePath
+ // .dex of stubs, used for hiddenapi processing
+ dexJarFile OptionalDexJarPath
}
type JavaApiLibraryProperties struct {
@@ -1511,13 +1649,26 @@
// List of flags to be passed to the javac compiler to generate jar file
Javacflags []string
+
+ // List of shared java libs that this module has dependencies to and
+ // should be passed as classpath in javac invocation
+ Libs []string
+
+ // List of java libs that this module has static dependencies to and will be
+ // merge zipped after metalava invocation
+ Static_libs []string
+
+ // Java Api library to provide the full API surface text files and jar file.
+ // If this property is set, the provided full API surface text files and
+ // jar file are passed to metalava invocation.
+ Dep_api_srcs *string
}
func ApiLibraryFactory() android.Module {
module := &ApiLibrary{}
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
module.AddProperties(&module.properties)
+ android.InitDefaultableModule(module)
return module
}
@@ -1561,6 +1712,12 @@
Flag("--color").
Flag("--quiet").
Flag("--format=v2").
+ Flag("--include-annotations").
+ // The flag makes nullability issues as warnings rather than errors by replacing
+ // @Nullable/@NonNull in the listed packages APIs with @RecentlyNullable/@RecentlyNonNull,
+ // and these packages are meant to have everything annotated
+ // @RecentlyNullable/@RecentlyNonNull.
+ FlagWithArg("--force-convert-to-warning-nullability-annotations ", "+*:-android.*:+android.icu.*:-dalvik.*").
FlagWithArg("--repeat-errors-max ", "10").
FlagWithArg("--hide ", "UnresolvedImport").
FlagWithArg("--hide ", "InvalidNullabilityOverride").
@@ -1569,19 +1726,61 @@
return cmd
}
+func (al *ApiLibrary) HeaderJars() android.Paths {
+ return android.Paths{al.stubsJar}
+}
+
+func (al *ApiLibrary) OutputDirAndDeps() (android.Path, android.Paths) {
+ return nil, nil
+}
+
func (al *ApiLibrary) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
if stubsDir.Valid() {
cmd.FlagWithArg("--stubs ", stubsDir.String())
}
}
-var javaApiContributionTag = dependencyTag{name: "java-api-contribution"}
+// This method extracts the stub java files from the srcjar file provided from dep_api_srcs module
+// and replaces the java stubs generated by invoking metalava in this module.
+// This method is used because metalava can generate compilable from-text stubs only when
+// the codebase encompasses all classes listed in the input API text file, but a class can extend
+// a class that is not within the same API domain.
+func (al *ApiLibrary) extractApiSrcs(ctx android.ModuleContext, rule *android.RuleBuilder, stubsDir android.OptionalPath, depApiSrcsSrcJar android.Path) {
+ generatedStubsList := android.PathForModuleOut(ctx, "metalava", "sources.txt")
+ unzippedSrcJarDir := android.PathForModuleOut(ctx, "metalava", "unzipDir")
+
+ rule.Command().
+ BuiltTool("list_files").
+ Text(stubsDir.String()).
+ FlagWithOutput("--out ", generatedStubsList).
+ FlagWithArg("--extensions ", ".java").
+ FlagWithArg("--root ", unzippedSrcJarDir.String())
+
+ rule.Command().
+ Text("unzip").
+ Flag("-q").
+ Input(depApiSrcsSrcJar).
+ FlagWithArg("-d ", unzippedSrcJarDir.String())
+
+ rule.Command().
+ BuiltTool("soong_zip").
+ Flag("-srcjar").
+ Flag("-write_if_changed").
+ FlagWithArg("-C ", unzippedSrcJarDir.String()).
+ FlagWithInput("-l ", generatedStubsList).
+ FlagWithOutput("-o ", al.stubsSrcJar)
+}
func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
apiContributions := al.properties.Api_contributions
for _, apiContributionName := range apiContributions {
ctx.AddDependency(ctx.Module(), javaApiContributionTag, apiContributionName)
}
+ ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...)
+ ctx.AddVariationDependencies(nil, staticLibTag, al.properties.Static_libs...)
+ if al.properties.Dep_api_srcs != nil {
+ ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Dep_api_srcs))
+ }
}
func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1599,10 +1798,31 @@
homeDir := android.PathForModuleOut(ctx, "metalava", "home")
- var srcFiles []android.Path
- ctx.VisitDirectDepsWithTag(javaApiContributionTag, func(dep android.Module) {
- provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
- srcFiles = append(srcFiles, android.PathForSource(ctx, provider.ApiFile.String()))
+ var srcFiles android.Paths
+ var classPaths android.Paths
+ var staticLibs android.Paths
+ var depApiSrcsStubsSrcJar android.Path
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ tag := ctx.OtherModuleDependencyTag(dep)
+ switch tag {
+ case javaApiContributionTag:
+ provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
+ providerApiFile := provider.ApiFile
+ if providerApiFile == nil {
+ ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name())
+ }
+ srcFiles = append(srcFiles, android.PathForSource(ctx, providerApiFile.String()))
+ case libTag:
+ provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+ classPaths = append(classPaths, provider.HeaderJars...)
+ case staticLibTag:
+ provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+ staticLibs = append(staticLibs, provider.HeaderJars...)
+ case depApiSrcsTag:
+ provider := ctx.OtherModuleProvider(dep, JavaApiLibraryDepsProvider).(JavaApiLibraryDepsInfo)
+ classPaths = append(classPaths, provider.HeaderJars...)
+ depApiSrcsStubsSrcJar = provider.StubsSrcJar
+ }
})
// Add the api_files inputs
@@ -1612,33 +1832,106 @@
srcFiles = append(srcFiles, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), api))
}
+ if srcFiles == nil {
+ ctx.ModuleErrorf("Error: %s has an empty api file.", ctx.ModuleName())
+ }
+
cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir)
al.stubsFlags(ctx, cmd, stubsDir)
al.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
- rule.Command().
- BuiltTool("soong_zip").
- Flag("-write_if_changed").
- Flag("-jar").
- FlagWithOutput("-o ", al.stubsSrcJar).
- FlagWithArg("-C ", stubsDir.String()).
- FlagWithArg("-D ", stubsDir.String())
+
+ if depApiSrcsStubsSrcJar != nil {
+ al.extractApiSrcs(ctx, rule, stubsDir, depApiSrcsStubsSrcJar)
+ } else {
+ rule.Command().
+ BuiltTool("soong_zip").
+ Flag("-write_if_changed").
+ Flag("-jar").
+ FlagWithOutput("-o ", al.stubsSrcJar).
+ FlagWithArg("-C ", stubsDir.String()).
+ FlagWithArg("-D ", stubsDir.String())
+ }
rule.Build("metalava", "metalava merged")
- al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), "android.jar")
+ al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, ctx.ModuleName(), "stubs.jar")
+ al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName()))
var flags javaBuilderFlags
flags.javaVersion = getStubsJavaVersion()
flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
+ flags.classpath = classpath(classPaths)
- TransformJavaToClasses(ctx, al.stubsJar, 0, android.Paths{},
+ TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{},
android.Paths{al.stubsSrcJar}, flags, android.Paths{})
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().
+ BuiltTool("merge_zips").
+ Output(al.stubsJar).
+ Inputs(android.Paths{al.stubsJarWithoutStaticLibs}).
+ Inputs(staticLibs)
+ builder.Build("merge_zips", "merge jar files")
+
+ // compile stubs to .dex for hiddenapi processing
+ dexParams := &compileDexParams{
+ flags: javaBuilderFlags{},
+ sdkVersion: al.SdkVersion(ctx),
+ minSdkVersion: al.MinSdkVersion(ctx),
+ classesJar: al.stubsJar,
+ jarName: ctx.ModuleName() + ".jar",
+ }
+ dexOutputFile := al.dexer.compileDex(ctx, dexParams)
+ uncompressed := true
+ al.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), al.stubsJar, &uncompressed)
+ dexOutputFile = al.hiddenAPIEncodeDex(ctx, dexOutputFile)
+ al.dexJarFile = makeDexJarPathFromPath(dexOutputFile)
+
ctx.Phony(ctx.ModuleName(), al.stubsJar)
+
+ ctx.SetProvider(JavaInfoProvider, JavaInfo{
+ HeaderJars: android.PathsIfNonNil(al.stubsJar),
+ ImplementationAndResourcesJars: android.PathsIfNonNil(al.stubsJar),
+ ImplementationJars: android.PathsIfNonNil(al.stubsJar),
+ AidlIncludeDirs: android.Paths{},
+ })
+
+ ctx.SetProvider(JavaApiLibraryDepsProvider, JavaApiLibraryDepsInfo{
+ JavaInfo: JavaInfo{
+ HeaderJars: android.PathsIfNonNil(al.stubsJar),
+ },
+ StubsSrcJar: al.stubsSrcJar,
+ })
}
+func (al *ApiLibrary) DexJarBuildPath() OptionalDexJarPath {
+ return al.dexJarFile
+}
+
+func (al *ApiLibrary) DexJarInstallPath() android.Path {
+ return al.dexJarFile.Path()
+}
+
+func (al *ApiLibrary) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+ return nil
+}
+
+// java_api_library constitutes the sdk, and does not build against one
+func (al *ApiLibrary) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpecNone
+}
+
+// java_api_library is always at "current". Return FutureApiLevel
+func (al *ApiLibrary) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ return android.FutureApiLevel
+}
+
+// implement the following interfaces for hiddenapi processing
+var _ hiddenAPIModule = (*ApiLibrary)(nil)
+var _ UsesLibraryDependency = (*ApiLibrary)(nil)
+
//
// Java prebuilts
//
@@ -1654,6 +1947,10 @@
// specified.
Min_sdk_version *string
+ // The max sdk version placeholder used to replace maxSdkVersion attributes on permission
+ // and uses-permission tags in manifest_fixer.
+ Replace_max_sdk_version_placeholder *string
+
Installable *bool
// If not empty, classes are restricted to the specified packages and their sub-packages.
@@ -1687,7 +1984,6 @@
android.ApexModuleBase
android.BazelModuleBase
prebuilt android.Prebuilt
- android.SdkBase
// Functionality common to Module and Import.
embeddableInModuleAndImport
@@ -1709,7 +2005,7 @@
hideApexVariantFromMake bool
sdkVersion android.SdkSpec
- minSdkVersion android.SdkSpec
+ minSdkVersion android.ApiLevel
}
var _ PermittedPackagesForUpdatableBootJars = (*Import)(nil)
@@ -1726,15 +2022,23 @@
return "none"
}
-func (j *Import) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (j *Import) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
if j.properties.Min_sdk_version != nil {
- return android.SdkSpecFrom(ctx, *j.properties.Min_sdk_version)
+ return android.ApiLevelFrom(ctx, *j.properties.Min_sdk_version)
}
- return j.SdkVersion(ctx)
+ return j.SdkVersion(ctx).ApiLevel
}
-func (j *Import) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
- return j.SdkVersion(ctx)
+func (j *Import) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
+ if j.properties.Replace_max_sdk_version_placeholder != nil {
+ return android.ApiLevelFrom(ctx, *j.properties.Replace_max_sdk_version_placeholder)
+ }
+ // Default is PrivateApiLevel
+ return android.SdkSpecPrivate.ApiLevel
+}
+
+func (j *Import) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ return j.SdkVersion(ctx).ApiLevel
}
func (j *Import) Prebuilt() *android.Prebuilt {
@@ -1776,7 +2080,8 @@
}
}
-func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (j *Import) commonBuildActions(ctx android.ModuleContext) {
+ //TODO(b/231322772) these should come from Bazel once available
j.sdkVersion = j.SdkVersion(ctx)
j.minSdkVersion = j.MinSdkVersion(ctx)
@@ -1787,6 +2092,10 @@
if ctx.Windows() {
j.HideFromMake()
}
+}
+
+func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.commonBuildActions(ctx)
jars := android.PathsForModuleSrc(ctx, j.properties.Jars)
@@ -1804,13 +2113,13 @@
var flags javaBuilderFlags
+ j.collectTransitiveHeaderJars(ctx)
ctx.VisitDirectDeps(func(module android.Module) {
tag := ctx.OtherModuleDependencyTag(module)
-
if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
switch tag {
- case libTag:
+ case libTag, sdkLibTag:
flags.classpath = append(flags.classpath, dep.HeaderJars...)
flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...)
case staticLibTag:
@@ -1820,7 +2129,7 @@
}
} else if dep, ok := module.(SdkLibraryDependency); ok {
switch tag {
- case libTag:
+ case libTag, sdkLibTag:
flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
}
}
@@ -1828,19 +2137,7 @@
addCLCFromDep(ctx, module, j.classLoaderContexts)
})
- if Bool(j.properties.Installable) {
- var installDir android.InstallPath
- if ctx.InstallInTestcases() {
- var archDir string
- if !ctx.Host() {
- archDir = ctx.DeviceConfig().DeviceArch()
- }
- installDir = android.PathForModuleInstall(ctx, ctx.ModuleName(), archDir)
- } else {
- installDir = android.PathForModuleInstall(ctx, "framework")
- }
- ctx.InstallFile(installDir, jarName, outputFile)
- }
+ j.maybeInstall(ctx, jarName, outputFile)
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
@@ -1854,7 +2151,8 @@
if di == nil {
return // An error has been reported by FindDeapexerProviderForModule.
}
- if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil {
+ dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(j.BaseModuleName())
+ if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
dexJarFile := makeDexJarPathFromPath(dexOutputPath)
j.dexJarFile = dexJarFile
installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName()))
@@ -1863,6 +2161,11 @@
j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, installPath)
setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
+
+ if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil {
+ j.dexpreopter.inputProfilePathOnHost = profilePath
+ }
+
j.dexpreopt(ctx, dexOutputPath)
// Initialize the hiddenapi structure.
@@ -1890,7 +2193,15 @@
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
var dexOutputFile android.OutputPath
- dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
+ dexParams := &compileDexParams{
+ flags: flags,
+ sdkVersion: j.SdkVersion(ctx),
+ minSdkVersion: j.MinSdkVersion(ctx),
+ classesJar: outputFile,
+ jarName: jarName,
+ }
+
+ dexOutputFile = j.dexer.compileDex(ctx, dexParams)
if ctx.Failed() {
return
}
@@ -1908,12 +2219,32 @@
ctx.SetProvider(JavaInfoProvider, JavaInfo{
HeaderJars: android.PathsIfNonNil(j.combinedClasspathFile),
+ TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars,
+ TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile),
ImplementationJars: android.PathsIfNonNil(j.combinedClasspathFile),
AidlIncludeDirs: j.exportAidlIncludeDirs,
})
}
+func (j *Import) maybeInstall(ctx android.ModuleContext, jarName string, outputFile android.Path) {
+ if !Bool(j.properties.Installable) {
+ return
+ }
+
+ var installDir android.InstallPath
+ if ctx.InstallInTestcases() {
+ var archDir string
+ if !ctx.Host() {
+ archDir = ctx.DeviceConfig().DeviceArch()
+ }
+ installDir = android.PathForModuleInstall(ctx, ctx.ModuleName(), archDir)
+ } else {
+ installDir = android.PathForModuleInstall(ctx, "framework")
+ }
+ ctx.InstallFile(installDir, jarName, outputFile)
+}
+
func (j *Import) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "", ".jar":
@@ -1961,15 +2292,17 @@
// Implements android.ApexModule
func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
- sdkSpec := j.MinSdkVersion(ctx)
- if !sdkSpec.Specified() {
+ sdkVersionSpec := j.SdkVersion(ctx)
+ minSdkVersion := j.MinSdkVersion(ctx)
+ if !minSdkVersion.Specified() {
return fmt.Errorf("min_sdk_version is not specified")
}
- if sdkSpec.Kind == android.SdkCore {
+ // If the module is compiling against core (via sdk_version), skip comparison check.
+ if sdkVersionSpec.Kind == android.SdkCore {
return nil
}
- if sdkSpec.ApiLevel.GreaterThan(sdkVersion) {
- return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel)
+ if minSdkVersion.GreaterThan(sdkVersion) {
+ return fmt.Errorf("newer SDK(%v)", minSdkVersion)
}
return nil
}
@@ -1977,11 +2310,16 @@
// requiredFilesFromPrebuiltApexForImport returns information about the files that a java_import or
// java_sdk_library_import with the specified base module name requires to be exported from a
// prebuilt_apex/apex_set.
-func requiredFilesFromPrebuiltApexForImport(name string) []string {
+func requiredFilesFromPrebuiltApexForImport(name string, d *dexpreopter) []string {
+ dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(name)
// Add the dex implementation jar to the set of exported files.
- return []string{
- apexRootRelativePathToJavaLib(name),
+ files := []string{
+ dexJarFileApexRootRelative,
}
+ if BoolDefault(d.importDexpreoptProperties.Dex_preopt.Profile_guided, false) {
+ files = append(files, dexJarFileApexRootRelative+".prof")
+ }
+ return files
}
// apexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for
@@ -1994,7 +2332,7 @@
func (j *Import) RequiredFilesFromPrebuiltApex(_ android.BaseModuleContext) []string {
name := j.BaseModuleName()
- return requiredFilesFromPrebuiltApexForImport(name)
+ return requiredFilesFromPrebuiltApexForImport(name, &j.dexpreopter)
}
// Add compile time check for interface implementation
@@ -2035,6 +2373,7 @@
module.AddProperties(
&module.properties,
&module.dexer.dexProperties,
+ &module.importDexpreoptProperties,
)
module.initModuleAndImport(module)
@@ -2043,7 +2382,6 @@
android.InitPrebuiltModule(module, &module.properties.Jars)
android.InitApexModule(module)
- android.InitSdkAwareModule(module)
android.InitBazelModule(module)
InitJavaModule(module, android.HostAndDeviceSupported)
return module
@@ -2275,6 +2613,7 @@
&RuntimeResourceOverlayProperties{},
&LintProperties{},
&appTestHelperAppProperties{},
+ &JavaApiLibraryProperties{},
)
android.InitDefaultsModule(module)
@@ -2328,12 +2667,10 @@
}
depTag := ctx.OtherModuleDependencyTag(depModule)
- if depTag == libTag {
+ if IsLibDepTag(depTag) {
// 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,
@@ -2351,17 +2688,62 @@
// <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 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 {
- Srcs bazel.LabelListAttribute
- Plugins bazel.LabelListAttribute
- Javacopts bazel.StringListAttribute
+ *javaResourcesAttributes
+ *kotlinAttributes
+ Srcs bazel.LabelListAttribute
+ Plugins bazel.LabelListAttribute
+ Javacopts bazel.StringListAttribute
+ Sdk_version bazel.StringAttribute
+ Java_version bazel.StringAttribute
}
type javaDependencyLabels struct {
@@ -2371,16 +2753,39 @@
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) {
+type aidlLibraryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Tags bazel.StringListAttribute
+}
+
+type javaAidlLibraryAttributes struct {
+ Deps bazel.LabelListAttribute
+ Tags bazel.StringListAttribute
+}
+
+// bp2BuildJavaInfo has information needed for the conversion of java*_modules
+// that is needed bor Bp2Build conversion but that requires different handling
+// depending on the module type.
+type bp2BuildJavaInfo struct {
+ // separates dependencies into dynamic dependencies and static dependencies.
+ DepLabels *javaDependencyLabels
+ hasKotlin bool
+}
+
+// convertLibraryAttrsBp2Build returns a javaCommonAttributes struct with
+// converted attributes shared across java_* modules and a bp2BuildJavaInfo struct
+// which has other non-attribute information needed for bp2build conversion
+// that needs different handling depending on the module types, and thus needs
+// to be returned to the calling function.
+func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo) {
var srcs bazel.LabelListAttribute
+ var deps bazel.LabelListAttribute
+ var staticDeps bazel.LabelList
+
archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
for axis, configToProps := range archVariantProps {
for config, _props := range configToProps {
@@ -2390,39 +2795,86 @@
}
}
}
+ srcs.ResolveExcludes()
javaSrcPartition := "java"
protoSrcPartition := "proto"
logtagSrcPartition := "logtag"
+ aidlSrcPartition := "aidl"
+ kotlinPartition := "kotlin"
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,
+ aidlSrcPartition: android.AidlSrcLabelPartition,
+ kotlinPartition: bazel.LabelPartition{Extensions: []string{".kt"}},
})
javaSrcs := srcPartitions[javaSrcPartition]
+ kotlinSrcs := srcPartitions[kotlinPartition]
+ javaSrcs.Append(kotlinSrcs)
- 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",
+ Bzl_load_location: "//build/bazel/rules/java:event_log_tags.bzl",
},
android.CommonAttributes{Name: logtagsLibName},
&eventLogTagsAttributes{
Srcs: srcPartitions[logtagSrcPartition],
},
)
+
+ logtagsSrcs := bazel.MakeLabelList([]bazel.Label{{Label: ":" + logtagsLibName}})
+ javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs))
}
- javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs))
+
+ if !srcPartitions[aidlSrcPartition].IsEmpty() {
+ aidlLibs, aidlSrcs := srcPartitions[aidlSrcPartition].Partition(func(src bazel.Label) bool {
+ return android.IsConvertedToAidlLibrary(ctx, src.OriginalModuleName)
+ })
+
+ apexAvailableTags := android.ApexAvailableTags(ctx.Module())
+
+ if !aidlSrcs.IsEmpty() {
+ aidlLibName := m.Name() + "_aidl_library"
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "aidl_library",
+ Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl",
+ },
+ android.CommonAttributes{Name: aidlLibName},
+ &aidlLibraryAttributes{
+ Srcs: aidlSrcs,
+ Tags: apexAvailableTags,
+ },
+ )
+ aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
+ }
+
+ javaAidlLibName := m.Name() + "_java_aidl_library"
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "java_aidl_library",
+ Bzl_load_location: "//build/bazel/rules/java:java_aidl_library.bzl",
+ },
+ android.CommonAttributes{Name: javaAidlLibName},
+ &javaAidlLibraryAttributes{
+ Deps: aidlLibs,
+ Tags: apexAvailableTags,
+ },
+ )
+
+ staticDeps.Add(&bazel.Label{Label: ":" + javaAidlLibName})
+ }
var javacopts []string
if m.properties.Javacflags != nil {
javacopts = append(javacopts, m.properties.Javacflags...)
}
+
epEnabled := m.properties.Errorprone.Enabled
//TODO(b/227504307) add configuration that depends on RUN_ERROR_PRONE environment variable
if Bool(epEnabled) {
@@ -2430,23 +2882,28 @@
}
commonAttrs := &javaCommonAttributes{
- Srcs: javaSrcs,
+ Srcs: javaSrcs,
+ javaResourcesAttributes: m.convertJavaResourcesAttributes(ctx),
Plugins: bazel.MakeLabelListAttribute(
android.BazelLabelForModuleDeps(ctx, m.properties.Plugins),
),
- Javacopts: bazel.MakeStringListAttribute(javacopts),
+ Javacopts: bazel.MakeStringListAttribute(javacopts),
+ Java_version: bazel.StringAttribute{Value: m.properties.Java_version},
+ Sdk_version: bazel.StringAttribute{Value: m.deviceProperties.Sdk_version},
}
- depLabels := &javaDependencyLabels{}
-
- var deps bazel.LabelList
- if m.properties.Libs != nil {
- 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))
+ for axis, configToProps := range archVariantProps {
+ for config, _props := range configToProps {
+ if archProps, ok := _props.(*CommonProperties); ok {
+ var libLabels []bazel.Label
+ for _, d := range archProps.Libs {
+ neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d)
+ neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink"
+ libLabels = append(libLabels, neverlinkLabel)
+ }
+ deps.SetSelectValue(axis, config, bazel.MakeLabelList(libLabels))
+ }
+ }
}
protoDepLabel := bp2buildProto(ctx, &m.Module, srcPartitions[protoSrcPartition])
@@ -2458,46 +2915,100 @@
// and so this should be a static dependency.
staticDeps.Add(protoDepLabel)
- depLabels.Deps = bazel.MakeLabelListAttribute(deps)
- depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps)
+ depLabels := &javaDependencyLabels{}
+ depLabels.Deps = deps
- return commonAttrs, depLabels
+ for axis, configToProps := range archVariantProps {
+ for config, _props := range configToProps {
+ if archProps, ok := _props.(*CommonProperties); ok {
+ archStaticLibs := android.BazelLabelForModuleDeps(
+ ctx,
+ android.LastUniqueStrings(android.CopyOf(archProps.Static_libs)))
+ depLabels.StaticDeps.SetSelectValue(axis, config, archStaticLibs)
+ }
+ }
+ }
+ depLabels.StaticDeps.Value.Append(staticDeps)
+
+ hasKotlin := !kotlinSrcs.IsEmpty()
+ commonAttrs.kotlinAttributes = &kotlinAttributes{
+ Kotlincflags: &m.properties.Kotlincflags,
+ }
+ if len(m.properties.Common_srcs) != 0 {
+ hasKotlin = true
+ commonAttrs.kotlinAttributes.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
+ }
+
+ bp2BuildInfo := &bp2BuildJavaInfo{
+ DepLabels: depLabels,
+ hasKotlin: hasKotlin,
+ }
+
+ return commonAttrs, bp2BuildInfo
}
type javaLibraryAttributes struct {
*javaCommonAttributes
- Deps bazel.LabelListAttribute
- Exports bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Exports bazel.LabelListAttribute
+ Neverlink bazel.BoolAttribute
+}
+
+type kotlinAttributes struct {
+ Common_srcs bazel.LabelListAttribute
+ Kotlincflags *[]string
+}
+
+func ktJvmLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
+ return bazel.BazelTargetModuleProperties{
+ Rule_class: "kt_jvm_library",
+ Bzl_load_location: "//build/bazel/rules/kotlin:rules.bzl",
+ }
+}
+
+func javaLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
+ return bazel.BazelTargetModuleProperties{
+ Rule_class: "java_library",
+ Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+ }
}
func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
- commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx)
+ commonAttrs, bp2BuildInfo := m.convertLibraryAttrsBp2Build(ctx)
+ depLabels := bp2BuildInfo.DepLabels
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() {
+ } else if !deps.IsEmpty() {
ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
}
-
+ var props bazel.BazelTargetModuleProperties
attrs := &javaLibraryAttributes{
javaCommonAttributes: commonAttrs,
Deps: deps,
Exports: depLabels.StaticDeps,
}
+ name := m.Name()
- props := bazel.BazelTargetModuleProperties{
- Rule_class: "java_library",
- Bzl_load_location: "//build/bazel/rules/java:library.bzl",
+ if !bp2BuildInfo.hasKotlin {
+ props = javaLibraryBazelTargetModuleProperties()
+ } else {
+ props = ktJvmLibraryBazelTargetModuleProperties()
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+ neverlinkProp := true
+ neverLinkAttrs := &javaLibraryAttributes{
+ Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+ Neverlink: bazel.BoolAttribute{Value: &neverlinkProp},
+ javaCommonAttributes: &javaCommonAttributes{
+ Sdk_version: bazel.StringAttribute{Value: m.deviceProperties.Sdk_version},
+ Java_version: bazel.StringAttribute{Value: m.properties.Java_version},
+ },
+ }
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name + "-neverlink"}, neverLinkAttrs)
+
}
type javaBinaryHostAttributes struct {
@@ -2510,7 +3021,8 @@
// JavaBinaryHostBp2Build is for java_binary_host bp2build.
func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) {
- commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx)
+ commonAttrs, bp2BuildInfo := m.convertLibraryAttrsBp2Build(ctx)
+ depLabels := bp2BuildInfo.DepLabels
deps := depLabels.Deps
deps.Append(depLabels.StaticDeps)
@@ -2537,14 +3049,8 @@
mainClass = mainClassInManifest
}
- attrs := &javaBinaryHostAttributes{
- javaCommonAttributes: commonAttrs,
- Deps: deps,
- Runtime_deps: runtimeDeps,
- Main_class: mainClass,
- }
-
// Attribute jvm_flags
+ var jvmFlags bazel.StringListAttribute
if m.binaryProperties.Jni_libs != nil {
jniLibPackages := map[string]bool{}
for _, jniLibLabel := range android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs).Includes {
@@ -2567,19 +3073,47 @@
// See cs/f:.*/third_party/bazel/.*java_stub_template.txt for the use of RUNPATH
jniLibPaths = append(jniLibPaths, "$${RUNPATH}"+jniLibPackage)
}
- attrs.Jvm_flags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")})
+ jvmFlags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")})
}
props := bazel.BazelTargetModuleProperties{
- Rule_class: "java_binary",
+ Rule_class: "java_binary",
+ Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+ }
+ binAttrs := &javaBinaryHostAttributes{
+ Runtime_deps: runtimeDeps,
+ Main_class: mainClass,
+ Jvm_flags: jvmFlags,
}
+ if commonAttrs.Srcs.IsEmpty() {
+ binAttrs.javaCommonAttributes = commonAttrs
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, binAttrs)
+ return
+ }
+
+ libName := m.Name() + "_lib"
+ var libProps bazel.BazelTargetModuleProperties
+ if bp2BuildInfo.hasKotlin {
+ libProps = ktJvmLibraryBazelTargetModuleProperties()
+ } else {
+ libProps = javaLibraryBazelTargetModuleProperties()
+ }
+ libAttrs := &javaLibraryAttributes{
+ Deps: deps,
+ javaCommonAttributes: commonAttrs,
+ }
+
+ ctx.CreateBazelTargetModule(libProps, android.CommonAttributes{Name: libName}, libAttrs)
+ binAttrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + libName}})
+
// Create the BazelTargetModule.
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, binAttrs)
}
type bazelJavaImportAttributes struct {
- Jars bazel.LabelListAttribute
+ Jars bazel.LabelListAttribute
+ Exports bazel.LabelListAttribute
}
// java_import bp2Build converter.
@@ -2598,8 +3132,76 @@
attrs := &bazelJavaImportAttributes{
Jars: jars,
}
- props := bazel.BazelTargetModuleProperties{Rule_class: "java_import"}
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "java_import",
+ Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+ }
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: android.RemoveOptionalPrebuiltPrefix(i.Name())}, attrs)
+ name := android.RemoveOptionalPrebuiltPrefix(i.Name())
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+
+ neverlink := true
+ neverlinkAttrs := &javaLibraryAttributes{
+ Neverlink: bazel.BoolAttribute{Value: &neverlink},
+ Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+ javaCommonAttributes: &javaCommonAttributes{
+ Sdk_version: bazel.StringAttribute{Value: proptools.StringPtr("none")},
+ },
+ }
+ ctx.CreateBazelTargetModule(
+ javaLibraryBazelTargetModuleProperties(),
+ android.CommonAttributes{Name: name + "-neverlink"},
+ neverlinkAttrs)
+
+}
+
+var _ android.MixedBuildBuildable = (*Import)(nil)
+
+func (i *Import) getBazelModuleLabel(ctx android.BaseModuleContext) string {
+ return android.RemoveOptionalPrebuiltPrefixFromBazelLabel(i.GetBazelLabel(ctx, i))
+}
+
+func (i *Import) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+ i.commonBuildActions(ctx)
+
+ bazelCtx := ctx.Config().BazelContext
+ filePaths, err := bazelCtx.GetOutputFiles(i.getBazelModuleLabel(ctx), android.GetConfigKey(ctx))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ return
+ }
+
+ bazelJars := android.Paths{}
+ for _, bazelOutputFile := range filePaths {
+ bazelJars = append(bazelJars, android.PathForBazelOut(ctx, bazelOutputFile))
+ }
+
+ jarName := android.RemoveOptionalPrebuiltPrefix(i.Name()) + ".jar"
+ outputFile := android.PathForModuleOut(ctx, "bazelCombined", jarName)
+ TransformJarsToJar(ctx, outputFile, "combine prebuilt jars", bazelJars,
+ android.OptionalPath{}, // manifest
+ false, // stripDirEntries
+ []string{}, // filesToStrip
+ []string{}, // dirsToStrip
+ )
+ i.combinedClasspathFile = outputFile
+
+ ctx.SetProvider(JavaInfoProvider, JavaInfo{
+ HeaderJars: android.PathsIfNonNil(i.combinedClasspathFile),
+ ImplementationAndResourcesJars: android.PathsIfNonNil(i.combinedClasspathFile),
+ ImplementationJars: android.PathsIfNonNil(i.combinedClasspathFile),
+ //TODO(b/240308299) include AIDL information from Bazel
+ })
+
+ i.maybeInstall(ctx, jarName, outputFile)
+}
+
+func (i *Import) QueueBazelCall(ctx android.BaseModuleContext) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(i.getBazelModuleLabel(ctx), cquery.GetOutputFiles, android.GetConfigKey(ctx))
+}
+
+func (i *Import) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+ return true
}
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 2378a6c..2a4913e 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -30,7 +30,6 @@
"android/soong/cc"
"android/soong/dexpreopt"
"android/soong/genrule"
- "android/soong/python"
)
// Legacy preparer used for running tests within the java package.
@@ -47,13 +46,11 @@
// Get the CC build components but not default modules.
cc.PrepareForTestWithCcBuildComponents,
// Include all the default java modules.
- PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithDexpreopt,
PrepareForTestWithOverlayBuildComponents,
- python.PrepareForTestWithPythonBuildComponents,
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
}),
- PrepareForTestWithDexpreopt,
)
func TestMain(m *testing.M) {
@@ -588,8 +585,8 @@
sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output
fooLibrary := fooModule.Module().(*Library)
- assertDeepEquals(t, "foo java sources incorrect",
- []string{"a.java"}, fooLibrary.compiledJavaSrcs.Strings())
+ assertDeepEquals(t, "foo unique sources incorrect",
+ []string{"a.java"}, fooLibrary.uniqueSrcFiles.Strings())
assertDeepEquals(t, "foo java source jars incorrect",
[]string{".intermediates/stubs-source/android_common/stubs-source-stubs.srcjar"},
@@ -617,6 +614,13 @@
android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar)
ctx.ModuleForTests("qux", "android_common").Rule("Cp")
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, fooModule.Module())[0]
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_library", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+ entries = android.AndroidMkEntriesForTest(t, ctx, barModule.Module())[0]
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+ entries = android.AndroidMkEntriesForTest(t, ctx, ctx.ModuleForTests("sdklib", "android_common").Module())[0]
+ android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_sdk_library_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
}
func assertDeepEquals(t *testing.T, message string, expected interface{}, actual interface{}) {
@@ -723,9 +727,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")
}
}
@@ -1287,6 +1291,43 @@
}
}
+func TestAidlIncludeDirFromConvertedFileGroupWithPathPropInMixedBuilds(t *testing.T) {
+ // TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups
+ t.Skip("Re-enable once filegroups are corrected for mixed builds")
+ bp := `
+ filegroup {
+ name: "foo_aidl",
+ srcs: ["aidl/foo/IFoo.aidl"],
+ path: "aidl/foo",
+ bazel_module: { label: "//:foo_aidl" },
+ }
+ java_library {
+ name: "foo",
+ srcs: [":foo_aidl"],
+ }
+`
+
+ outBaseDir := "out/bazel/output"
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.PrepareForTestWithFilegroup,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToOutputFiles: map[string][]string{
+ "//:foo_aidl": []string{"aidl/foo/IFoo.aidl"},
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+
+ aidlCommand := result.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+ expectedAidlFlag := "-I" + outBaseDir + "/execroot/__main__/aidl/foo"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+}
+
func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) {
ctx, _ := testJava(t, `
java_library {
@@ -1333,6 +1374,39 @@
}
}
+func TestAidlFlagsMinSdkVersionDroidstubs(t *testing.T) {
+ bpTemplate := `
+ droidstubs {
+ name: "foo-stubs",
+ srcs: ["foo.aidl"],
+ %s
+ system_modules: "none",
+ }
+ `
+ testCases := []struct {
+ desc string
+ sdkVersionBp string
+ minSdkVersionExpected string
+ }{
+ {
+ desc: "sdk_version not set, module compiles against private platform APIs",
+ sdkVersionBp: ``,
+ minSdkVersionExpected: "10000",
+ },
+ {
+ desc: "sdk_version set to none, module does not build against an SDK",
+ sdkVersionBp: `sdk_version: "none",`,
+ minSdkVersionExpected: "10000",
+ },
+ }
+ for _, tc := range testCases {
+ ctx := prepareForJavaTest.RunTestWithBp(t, fmt.Sprintf(bpTemplate, tc.sdkVersionBp))
+ aidlCmd := ctx.ModuleForTests("foo-stubs", "android_common").Rule("aidl").RuleParams.Command
+ expected := "--min_sdk_version=" + tc.minSdkVersionExpected
+ android.AssertStringDoesContain(t, "aidl command conatins incorrect min_sdk_version for testCse: "+tc.desc, aidlCmd, expected)
+ }
+}
+
func TestAidlEnforcePermissions(t *testing.T) {
ctx, _ := testJava(t, `
java_library {
@@ -1370,24 +1444,26 @@
}
func TestDataNativeBinaries(t *testing.T) {
- ctx, _ := testJava(t, `
+ ctx := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.PrepareForTestWithAllowMissingDependencies).RunTestWithBp(t, `
java_test_host {
name: "foo",
srcs: ["a.java"],
data_native_bins: ["bin"]
}
- python_binary_host {
+ cc_binary_host {
name: "bin",
- srcs: ["bin.py"],
+ srcs: ["bin.cpp"],
}
- `)
+ `).TestContext
buildOS := ctx.Config().BuildOS.String()
test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
- expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
+ expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64/bin:bin"}
actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_COMPATIBILITY_SUPPORT_FILES", ctx.Config(), expected, actual)
}
@@ -1668,6 +1744,122 @@
}
}
+func TestImportMixedBuild(t *testing.T) {
+ bp := `
+ java_import {
+ name: "baz",
+ jars: [
+ "test1.jar",
+ "test2.jar",
+ ],
+ bazel_module: { label: "//foo/bar:baz" },
+ }
+ `
+
+ ctx := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToOutputFiles: map[string][]string{
+ "//foo/bar:baz": []string{"test1.jar", "test2.jar"},
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+
+ bazMod := ctx.ModuleForTests("baz", "android_common").Module()
+ producer := bazMod.(android.OutputFileProducer)
+ expectedOutputFiles := []string{".intermediates/baz/android_common/bazelCombined/baz.jar"}
+
+ outputFiles, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting java_import outputfiles %s", err)
+ }
+ actualOutputFiles := android.NormalizePathsForTesting(outputFiles)
+ android.AssertDeepEquals(t, "Output files are produced", expectedOutputFiles, actualOutputFiles)
+
+ javaInfoProvider := ctx.ModuleProvider(bazMod, JavaInfoProvider)
+ javaInfo, ok := javaInfoProvider.(JavaInfo)
+ if !ok {
+ t.Error("could not get JavaInfo from java_import module")
+ }
+ android.AssertDeepEquals(t, "Header JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.HeaderJars))
+ android.AssertDeepEquals(t, "Implementation/Resources JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.ImplementationAndResourcesJars))
+ android.AssertDeepEquals(t, "Implementation JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.ImplementationJars))
+}
+
+func TestGenAidlIncludeFlagsForMixedBuilds(t *testing.T) {
+ bazelOutputBaseDir := filepath.Join("out", "bazel")
+ result := android.GroupFixturePreparers(
+ PrepareForIntegrationTestWithJava,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: bazelOutputBaseDir,
+ }
+ }),
+ ).RunTest(t)
+
+ ctx := &android.TestPathContext{TestResult: result}
+
+ srcDirectory := filepath.Join("frameworks", "base")
+ srcDirectoryAlreadyIncluded := filepath.Join("frameworks", "base", "core", "java")
+ bazelSrcDirectory := android.PathForBazelOut(ctx, srcDirectory)
+ bazelSrcDirectoryAlreadyIncluded := android.PathForBazelOut(ctx, srcDirectoryAlreadyIncluded)
+ srcs := android.Paths{
+ android.PathForTestingWithRel(bazelSrcDirectory.String(), "bazelAidl.aidl"),
+ android.PathForTestingWithRel(bazelSrcDirectory.String(), "bazelAidl2.aidl"),
+ android.PathForTestingWithRel(bazelSrcDirectoryAlreadyIncluded.String(), "bazelAidlExclude.aidl"),
+ android.PathForTestingWithRel(bazelSrcDirectoryAlreadyIncluded.String(), "bazelAidl2Exclude.aidl"),
+ }
+ dirsAlreadyIncluded := android.Paths{
+ android.PathForTesting(srcDirectoryAlreadyIncluded),
+ }
+
+ expectedFlags := " -Iout/bazel/execroot/__main__/frameworks/base"
+ flags := genAidlIncludeFlags(ctx, srcs, dirsAlreadyIncluded)
+ if flags != expectedFlags {
+ t.Errorf("expected flags to be %q; was %q", expectedFlags, flags)
+ }
+}
+
+func TestDeviceBinaryWrapperGeneration(t *testing.T) {
+ // Scenario 1: java_binary has main_class property in its bp
+ ctx, _ := testJava(t, `
+ java_binary {
+ name: "foo",
+ srcs: ["foo.java"],
+ main_class: "foo.bar.jb",
+ }
+ `)
+ wrapperPath := fmt.Sprint(ctx.ModuleForTests("foo", "android_arm64_armv8-a").AllOutputs())
+ if !strings.Contains(wrapperPath, "foo.sh") {
+ t.Errorf("wrapper file foo.sh is not generated")
+ }
+
+ // Scenario 2: java_binary has neither wrapper nor main_class, its build
+ // is expected to be failed.
+ testJavaError(t, "main_class property is required for device binary if no default wrapper is assigned", `
+ java_binary {
+ name: "foo",
+ srcs: ["foo.java"],
+ }`)
+}
+
+func TestJavaApiContributionEmptyApiFile(t *testing.T) {
+ testJavaError(t,
+ "Error: foo has an empty api file.",
+ `java_api_contribution {
+ name: "foo",
+ }
+ java_api_library {
+ name: "bar",
+ api_surface: "public",
+ api_contributions: ["foo"],
+ }
+ `)
+}
+
func TestJavaApiLibraryAndProviderLink(t *testing.T) {
provider_bp_a := `
java_api_contribution {
@@ -1722,6 +1914,98 @@
}
}
+func TestJavaApiLibraryAndDefaultsLink(t *testing.T) {
+ provider_bp_a := `
+ java_api_contribution {
+ name: "foo1",
+ api_file: "foo1.txt",
+ }
+ `
+ provider_bp_b := `
+ java_api_contribution {
+ name: "foo2",
+ api_file: "foo2.txt",
+ }
+ `
+ provider_bp_c := `
+ java_api_contribution {
+ name: "foo3",
+ api_file: "foo3.txt",
+ }
+ `
+ provider_bp_d := `
+ java_api_contribution {
+ name: "foo4",
+ api_file: "foo4.txt",
+ }
+ `
+ ctx, _ := testJavaWithFS(t, `
+ java_defaults {
+ name: "baz1",
+ api_surface: "public",
+ api_contributions: ["foo1", "foo2"],
+ }
+
+ java_defaults {
+ name: "baz2",
+ api_surface: "system",
+ api_contributions: ["foo3"],
+ }
+
+ java_api_library {
+ name: "bar1",
+ api_surface: "public",
+ api_contributions: ["foo1"],
+ }
+
+ java_api_library {
+ name: "bar2",
+ api_surface: "public",
+ defaults:["baz1"],
+ }
+
+ java_api_library {
+ name: "bar3",
+ api_surface: "system",
+ defaults:["baz1", "baz2"],
+ api_contributions: ["foo4"],
+ api_files: ["api1/current.txt", "api2/current.txt"]
+ }
+ `,
+ map[string][]byte{
+ "a/Android.bp": []byte(provider_bp_a),
+ "b/Android.bp": []byte(provider_bp_b),
+ "c/Android.bp": []byte(provider_bp_c),
+ "d/Android.bp": []byte(provider_bp_d),
+ })
+
+ testcases := []struct {
+ moduleName string
+ sourceTextFileDirs []string
+ }{
+ {
+ moduleName: "bar1",
+ sourceTextFileDirs: []string{"a/foo1.txt"},
+ },
+ {
+ moduleName: "bar2",
+ sourceTextFileDirs: []string{"a/foo1.txt", "b/foo2.txt"},
+ },
+ {
+ moduleName: "bar3",
+ sourceTextFileDirs: []string{"c/foo3.txt", "a/foo1.txt", "b/foo2.txt", "d/foo4.txt", "api1/current.txt", "api2/current.txt"},
+ },
+ }
+ for _, c := range testcases {
+ m := ctx.ModuleForTests(c.moduleName, "android_common")
+ manifest := m.Output("metalava.sbox.textproto")
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+ manifestCommand := sboxProto.Commands[0].GetCommand()
+ sourceFilesFlag := "--source-files " + strings.Join(c.sourceTextFileDirs, " ")
+ android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag)
+ }
+}
+
func TestJavaApiLibraryJarGeneration(t *testing.T) {
provider_bp_a := `
java_api_contribution {
@@ -1729,7 +2013,8 @@
api_file: "foo1.txt",
}
`
- provider_bp_b := `java_api_contribution {
+ provider_bp_b := `
+ java_api_contribution {
name: "foo2",
api_file: "foo2.txt",
}
@@ -1758,11 +2043,11 @@
}{
{
moduleName: "bar1",
- outputJarName: "bar1/android.jar",
+ outputJarName: "bar1/bar1.jar",
},
{
moduleName: "bar2",
- outputJarName: "bar2/android.jar",
+ outputJarName: "bar2/bar2.jar",
},
}
for _, c := range testcases {
@@ -1773,3 +2058,220 @@
}
}
}
+
+func TestJavaApiLibraryLibsLink(t *testing.T) {
+ provider_bp_a := `
+ java_api_contribution {
+ name: "foo1",
+ api_file: "foo1.txt",
+ }
+ `
+ provider_bp_b := `
+ java_api_contribution {
+ name: "foo2",
+ api_file: "foo2.txt",
+ }
+ `
+ lib_bp_a := `
+ java_library {
+ name: "lib1",
+ srcs: ["Lib.java"],
+ }
+ `
+ lib_bp_b := `
+ java_library {
+ name: "lib2",
+ srcs: ["Lib.java"],
+ }
+ `
+
+ ctx, _ := testJavaWithFS(t, `
+ java_api_library {
+ name: "bar1",
+ api_surface: "public",
+ api_contributions: ["foo1"],
+ libs: ["lib1"],
+ }
+
+ java_api_library {
+ name: "bar2",
+ api_surface: "system",
+ api_contributions: ["foo1", "foo2"],
+ libs: ["lib1", "lib2", "bar1"],
+ }
+ `,
+ map[string][]byte{
+ "a/Android.bp": []byte(provider_bp_a),
+ "b/Android.bp": []byte(provider_bp_b),
+ "c/Android.bp": []byte(lib_bp_a),
+ "c/Lib.java": {},
+ "d/Android.bp": []byte(lib_bp_b),
+ "d/Lib.java": {},
+ })
+
+ testcases := []struct {
+ moduleName string
+ classPathJarNames []string
+ }{
+ {
+ moduleName: "bar1",
+ classPathJarNames: []string{"lib1.jar"},
+ },
+ {
+ moduleName: "bar2",
+ classPathJarNames: []string{"lib1.jar", "lib2.jar", "bar1/bar1.jar"},
+ },
+ }
+ for _, c := range testcases {
+ m := ctx.ModuleForTests(c.moduleName, "android_common")
+ javacRules := m.Rule("javac")
+ classPathArgs := javacRules.Args["classpath"]
+ for _, jarName := range c.classPathJarNames {
+ if !strings.Contains(classPathArgs, jarName) {
+ t.Errorf("Module output does not contain expected jar %s", jarName)
+ }
+ }
+ }
+}
+
+func TestJavaApiLibraryStaticLibsLink(t *testing.T) {
+ provider_bp_a := `
+ java_api_contribution {
+ name: "foo1",
+ api_file: "foo1.txt",
+ }
+ `
+ provider_bp_b := `
+ java_api_contribution {
+ name: "foo2",
+ api_file: "foo2.txt",
+ }
+ `
+ lib_bp_a := `
+ java_library {
+ name: "lib1",
+ srcs: ["Lib.java"],
+ }
+ `
+ lib_bp_b := `
+ java_library {
+ name: "lib2",
+ srcs: ["Lib.java"],
+ }
+ `
+
+ ctx, _ := testJavaWithFS(t, `
+ java_api_library {
+ name: "bar1",
+ api_surface: "public",
+ api_contributions: ["foo1"],
+ static_libs: ["lib1"],
+ }
+
+ java_api_library {
+ name: "bar2",
+ api_surface: "system",
+ api_contributions: ["foo1", "foo2"],
+ static_libs: ["lib1", "lib2", "bar1"],
+ }
+ `,
+ map[string][]byte{
+ "a/Android.bp": []byte(provider_bp_a),
+ "b/Android.bp": []byte(provider_bp_b),
+ "c/Android.bp": []byte(lib_bp_a),
+ "c/Lib.java": {},
+ "d/Android.bp": []byte(lib_bp_b),
+ "d/Lib.java": {},
+ })
+
+ testcases := []struct {
+ moduleName string
+ staticLibJarNames []string
+ }{
+ {
+ moduleName: "bar1",
+ staticLibJarNames: []string{"lib1.jar"},
+ },
+ {
+ moduleName: "bar2",
+ staticLibJarNames: []string{"lib1.jar", "lib2.jar", "bar1/bar1.jar"},
+ },
+ }
+ for _, c := range testcases {
+ m := ctx.ModuleForTests(c.moduleName, "android_common")
+ mergeZipsCommand := m.Rule("merge_zips").RuleParams.Command
+ for _, jarName := range c.staticLibJarNames {
+ if !strings.Contains(mergeZipsCommand, jarName) {
+ t.Errorf("merge_zips command does not contain expected jar %s", jarName)
+ }
+ }
+ }
+}
+
+func TestJavaApiLibraryDepApiSrcs(t *testing.T) {
+ provider_bp_a := `
+ java_api_contribution {
+ name: "foo1",
+ api_file: "foo1.txt",
+ }
+ `
+ provider_bp_b := `
+ java_api_contribution {
+ name: "foo2",
+ api_file: "foo2.txt",
+ }
+ `
+ lib_bp_a := `
+ java_api_library {
+ name: "lib1",
+ api_surface: "public",
+ api_contributions: ["foo1", "foo2"],
+ }
+ `
+
+ ctx, _ := testJavaWithFS(t, `
+ java_api_library {
+ name: "bar1",
+ api_surface: "public",
+ api_contributions: ["foo1"],
+ dep_api_srcs: "lib1",
+ }
+ `,
+ map[string][]byte{
+ "a/Android.bp": []byte(provider_bp_a),
+ "b/Android.bp": []byte(provider_bp_b),
+ "c/Android.bp": []byte(lib_bp_a),
+ })
+
+ m := ctx.ModuleForTests("bar1", "android_common")
+ manifest := m.Output("metalava.sbox.textproto")
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+ manifestCommand := sboxProto.Commands[0].GetCommand()
+
+ android.AssertStringDoesContain(t, "Command expected to contain module srcjar file", manifestCommand, "bar1-stubs.srcjar")
+ android.AssertStringDoesContain(t, "Command expected to contain output files list text file flag", manifestCommand, "--out __SBOX_SANDBOX_DIR__/out/sources.txt")
+}
+
+func TestTradefedOptions(t *testing.T) {
+ result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
+java_test_host {
+ name: "foo",
+ test_options: {
+ tradefed_options: [
+ {
+ name: "exclude-path",
+ value: "org/apache"
+ }
+ ]
+ }
+}
+`)
+
+ buildOS := result.Config.BuildOS.String()
+ args := result.ModuleForTests("foo", buildOS+"_common").
+ Output("out/soong/.intermediates/foo/" + buildOS + "_common/foo.config").Args
+ expected := proptools.NinjaAndShellEscape("<option name=\"exclude-path\" value=\"org/apache\" />")
+ if args["extraConfigs"] != expected {
+ t.Errorf("Expected args[\"extraConfigs\"] to equal %q, was %q", expected, args["extraConfigs"])
+ }
+}
diff --git a/java/jdeps.go b/java/jdeps.go
index 3734335..a52b867 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -52,6 +52,11 @@
return
}
+ // Prevent including both prebuilts and matching source modules when one replaces the other.
+ if !android.IsModulePreferred(module) {
+ return
+ }
+
ideInfoProvider, ok := module.(android.IDEInfo)
if !ok {
return
diff --git a/java/kotlin.go b/java/kotlin.go
index 903c624..9bff5ea 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -119,9 +119,8 @@
"srcJarDir": android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
"kotlinBuildFile": android.PathForModuleOut(ctx, "kotlinc-build.xml").String(),
"emptyDir": android.PathForModuleOut(ctx, "kotlinc", "empty").String(),
- // http://b/69160377 kotlinc only supports -jvm-target 1.6 and 1.8
- "kotlinJvmTarget": "1.8",
- "name": kotlinName,
+ "kotlinJvmTarget": flags.javaVersion.StringForKotlinc(),
+ "name": kotlinName,
},
})
}
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 435d782..933fc51 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -42,6 +42,15 @@
}
`)
+ kotlinStdlib := ctx.ModuleForTests("kotlin-stdlib", "android_common").
+ Output("turbine-combined/kotlin-stdlib.jar").Output
+ kotlinStdlibJdk7 := ctx.ModuleForTests("kotlin-stdlib-jdk7", "android_common").
+ Output("turbine-combined/kotlin-stdlib-jdk7.jar").Output
+ kotlinStdlibJdk8 := ctx.ModuleForTests("kotlin-stdlib-jdk8", "android_common").
+ Output("turbine-combined/kotlin-stdlib-jdk8.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")
@@ -69,6 +78,26 @@
fooJar.Inputs.Strings(), fooKotlincClasses.String())
}
+ if !inList(kotlinStdlib.String(), fooJar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %v",
+ fooJar.Inputs.Strings(), kotlinStdlib.String())
+ }
+
+ if !inList(kotlinStdlibJdk7.String(), fooJar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %v",
+ fooJar.Inputs.Strings(), kotlinStdlibJdk7.String())
+ }
+
+ if !inList(kotlinStdlibJdk8.String(), fooJar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %v",
+ fooJar.Inputs.Strings(), kotlinStdlibJdk8.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())
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 8e22491..6cb549e 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -20,202 +20,47 @@
)
var legacyCorePlatformApiModules = []string{
- "AAECarSystemUI",
- "AAECarSystemUI-tests",
"ArcSettings",
- "ahat-test-dump",
- "android.car",
- "android.test.mock",
- "android.test.mock.impl",
- "AoapTestDeviceApp",
- "AoapTestHostApp",
- "api-stubs-docs",
- "art_cts_jvmti_test_library",
- "art-gtest-jars-MyClassNatives",
- "BackupEncryption",
- "BackupFrameworksServicesRoboTests",
- "backuplib",
- "BandwidthEnforcementTest",
- "BlockedNumberProvider",
- "BluetoothInstrumentationTests",
- "BluetoothMidiLib",
- "BluetoothMidiService",
"BTTestApp",
- "CallEnhancement",
"CapCtrlInterface",
- "CarService",
- "CarServiceTest",
- "car-service-test-lib",
- "car-service-test-static-lib",
- "CertInstaller",
"com.qti.location.sdk",
- "com.qti.media.secureprocessor",
- "ConnectivityManagerTest",
- "ContactsProvider",
- "CorePerfTests",
- "core-tests-support",
- "cronet_impl_common_java",
- "cronet_impl_native_java",
- "cronet_impl_platform_java",
- "CtsAppExitTestCases",
- "CtsContentTestCases",
- "CtsLibcoreWycheproofBCTestCases",
- "CtsMediaTestCases",
- "CtsNetTestCases",
- "CtsNetTestCasesLatestSdk",
- "CtsSecurityTestCases",
- "CtsSuspendAppsTestCases",
- "CtsUsageStatsTestCases",
- "DeadpoolService",
- "DeadpoolServiceBtServices",
- "DeviceInfo",
- "DiagnosticTools",
- "DisplayCutoutEmulationEmu01Overlay",
- "DocumentsUIGoogleTests",
- "DocumentsUIPerfTests",
- "DocumentsUITests",
- "DocumentsUIUnitTests",
- "DownloadProvider",
- "DownloadProviderTests",
- "DownloadProviderUi",
- "ds-car-docs", // for AAOS API documentation only
- "DynamicSystemInstallationService",
- "EmergencyInfo-lib",
- "EthernetServiceTests",
- "ExternalStorageProvider",
"face-V1-0-javalib",
"FloralClocks",
"framework-jobscheduler",
"framework-minus-apex",
"framework-minus-apex-intdefs",
- "FrameworkOverlayG6QU3",
"FrameworksCoreTests",
- "FrameworksIkeTests",
- "FrameworksNetCommonTests",
- "FrameworksNetTests",
- "FrameworksServicesRoboTests",
- "FrameworksServicesTests",
- "FrameworksMockingServicesTests",
- "FrameworksUtilTests",
- "GtsIncrementalInstallTestCases",
- "GtsIncrementalInstallTriggerApp",
- "GtsInstallerV2TestCases",
"HelloOslo",
- "hid",
- "hidl_test_java_java",
- "hwbinder",
- "imssettings",
"izat.lib.glue",
- "KeyChain",
- "LocalSettingsLib",
- "LocalTransport",
- "lockagent",
- "mediaframeworktest",
"mediatek-ims-base",
- "MmsService",
"ModemTestMode",
"MtkCapCtrl",
- "MtpService",
- "MultiDisplayProvider",
"my.tests.snapdragonsdktest",
"NetworkSetting",
- "NetworkStackIntegrationTestsLib",
- "NetworkStackNextIntegrationTests",
- "NetworkStackNextTests",
- "NetworkStackTests",
- "NetworkStackTestsLib",
- "online-gcm-ref-docs",
- "online-gts-docs",
"PerformanceMode",
- "platform_library-docs",
- "PowerStatsService",
- "PrintSpooler",
"pxp-monitor",
"QColor",
"qcom.fmradio",
- "QDCMMobileApp",
"Qmmi",
"QPerformance",
- "remotesimlockmanagerlibrary",
- "RollbackTest",
"sam",
"saminterfacelibrary",
"sammanagerlibrary",
- "service-blobstore",
- "service-connectivity-pre-jarjar",
- "service-jobscheduler",
"services",
- "services.accessibility",
- "services.backup",
"services.core.unboosted",
- "services.devicepolicy",
- "services.print",
- "services.usage",
- "services.usb",
"Settings-core",
"SettingsGoogle",
"SettingsGoogleOverlayCoral",
"SettingsGoogleOverlayFlame",
"SettingsLib",
- "SettingsOverlayG020A",
- "SettingsOverlayG020B",
- "SettingsOverlayG020C",
- "SettingsOverlayG020D",
- "SettingsOverlayG020E",
- "SettingsOverlayG020E_VN",
- "SettingsOverlayG020F",
- "SettingsOverlayG020F_VN",
- "SettingsOverlayG020G",
- "SettingsOverlayG020G_VN",
- "SettingsOverlayG020H",
- "SettingsOverlayG020H_VN",
- "SettingsOverlayG020I",
- "SettingsOverlayG020I_VN",
- "SettingsOverlayG020J",
- "SettingsOverlayG020M",
- "SettingsOverlayG020N",
- "SettingsOverlayG020P",
- "SettingsOverlayG020Q",
- "SettingsOverlayG025H",
- "SettingsOverlayG025J",
- "SettingsOverlayG025M",
- "SettingsOverlayG025N",
- "SettingsOverlayG5NZ6",
- "SettingsProvider",
- "SettingsProviderTest",
"SettingsRoboTests",
- "Shell",
- "ShellTests",
"SimContact",
"SimContacts",
"SimSettings",
- "sl4a.Common",
- "StatementService",
- "SystemUI-core",
- "SystemUISharedLib",
- "SystemUI-tests",
"tcmiface",
- "Telecom",
- "TelecomUnitTests",
"telephony-common",
- "TelephonyProviderTests",
"TeleService",
- "testables",
- "TetheringTests",
- "TetheringTestsLib",
- "time_zone_distro_installer",
- "time_zone_distro_installer-tests",
- "time_zone_distro-tests",
- "time_zone_updater",
- "TMobilePlanProvider",
- "TvProvider",
- "uiautomator-stubs-docs",
- "uimgbamanagerlibrary",
- "UsbHostExternalManagementTestApp",
- "UserDictionaryProvider",
"UxPerformance",
- "WallpaperBackup",
- "WallpaperBackupAgentTests",
"WfdCommon",
}
diff --git a/java/lint.go b/java/lint.go
index fdc9dbd..40ef484 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -17,6 +17,7 @@
import (
"fmt"
"sort"
+ "strconv"
"strings"
"github.com/google/blueprint/proptools"
@@ -79,9 +80,9 @@
classes android.Path
extraLintCheckJars android.Paths
library bool
- minSdkVersion android.ApiLevel
- targetSdkVersion android.ApiLevel
- compileSdkVersion android.ApiLevel
+ minSdkVersion int
+ targetSdkVersion int
+ compileSdkVersion int
compileSdkKind android.SdkKind
javaLanguageLevel string
kotlinLanguageLevel string
@@ -95,9 +96,10 @@
}
type lintOutputs struct {
- html android.Path
- text android.Path
- xml android.Path
+ html android.Path
+ text android.Path
+ xml android.Path
+ referenceBaseline android.Path
depSets LintDepSets
}
@@ -158,6 +160,50 @@
}
}
+type lintDatabaseFiles struct {
+ apiVersionsModule string
+ apiVersionsCopiedName string
+ apiVersionsPrebuiltPath string
+ annotationsModule string
+ annotationCopiedName string
+ annotationPrebuiltpath string
+}
+
+var allLintDatabasefiles = map[android.SdkKind]lintDatabaseFiles{
+ android.SdkPublic: {
+ apiVersionsModule: "api_versions_public",
+ apiVersionsCopiedName: "api_versions_public.xml",
+ apiVersionsPrebuiltPath: "prebuilts/sdk/current/public/data/api-versions.xml",
+ annotationsModule: "sdk-annotations.zip",
+ annotationCopiedName: "annotations-public.zip",
+ annotationPrebuiltpath: "prebuilts/sdk/current/public/data/annotations.zip",
+ },
+ android.SdkSystem: {
+ apiVersionsModule: "api_versions_system",
+ apiVersionsCopiedName: "api_versions_system.xml",
+ apiVersionsPrebuiltPath: "prebuilts/sdk/current/system/data/api-versions.xml",
+ annotationsModule: "sdk-annotations-system.zip",
+ annotationCopiedName: "annotations-system.zip",
+ annotationPrebuiltpath: "prebuilts/sdk/current/system/data/annotations.zip",
+ },
+ android.SdkModule: {
+ apiVersionsModule: "api_versions_module_lib",
+ apiVersionsCopiedName: "api_versions_module_lib.xml",
+ apiVersionsPrebuiltPath: "prebuilts/sdk/current/module-lib/data/api-versions.xml",
+ annotationsModule: "sdk-annotations-module-lib.zip",
+ annotationCopiedName: "annotations-module-lib.zip",
+ annotationPrebuiltpath: "prebuilts/sdk/current/module-lib/data/annotations.zip",
+ },
+ android.SdkSystemServer: {
+ apiVersionsModule: "api_versions_system_server",
+ apiVersionsCopiedName: "api_versions_system_server.xml",
+ apiVersionsPrebuiltPath: "prebuilts/sdk/current/system-server/data/api-versions.xml",
+ annotationsModule: "sdk-annotations-system-server.zip",
+ annotationCopiedName: "annotations-system-server.zip",
+ annotationPrebuiltpath: "prebuilts/sdk/current/system-server/data/annotations.zip",
+ },
+}
+
func (l *linter) LintDepSets() LintDepSets {
return l.outputs.depSets
}
@@ -189,10 +235,8 @@
extraCheckModules := l.properties.Lint.Extra_check_modules
- if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
- if checkOnlyModules := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); checkOnlyModules != "" {
- extraCheckModules = strings.Split(checkOnlyModules, ",")
- }
+ if extraCheckModulesEnv := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); extraCheckModulesEnv != "" {
+ extraCheckModules = append(extraCheckModules, strings.Split(extraCheckModulesEnv, ",")...)
}
ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
@@ -213,7 +257,7 @@
return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
}
-func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
+func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path) lintPaths {
projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
// Lint looks for a lint.xml file next to the project.xml file, give it one.
configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
@@ -244,8 +288,7 @@
// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
// lint separately.
- srcsList := android.PathForModuleOut(ctx, "lint-srcs.list")
- cmd.FlagWithRspFileInputList("--srcs ", srcsList, l.srcs)
+ cmd.FlagWithInput("--srcs ", srcsList)
cmd.FlagWithInput("--generated_srcs ", srcJarList)
@@ -271,19 +314,25 @@
cmd.FlagWithInput("@",
android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
- cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
+ if l.compileSdkKind == android.SdkPublic {
+ cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
+ } else {
+ // TODO(b/268261262): Remove this branch. We're demoting NewApi to a warning due to pre-existing issues that need to be fixed.
+ cmd.FlagForEachArg("--warning_check ", l.extraMainlineLintErrors)
+ }
cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
- 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())
- cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
- }
- }
+ // TODO(b/193460475): Re-enable strict updatability linting
+ //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())
+ // cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
+ // }
+ //}
return lintPaths{
projectXML: projectXMLPath,
@@ -303,8 +352,8 @@
Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
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.String(), l.targetSdkVersion.String()).
+ Textf(`echo " <uses-sdk android:minSdkVersion='%d' android:targetSdkVersion='%d'/>" &&`,
+ l.minSdkVersion, l.targetSdkVersion).
Text(`echo "</manifest>"`).
Text(") >").Output(manifestPath)
@@ -329,18 +378,32 @@
return
}
- if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 {
+ if l.minSdkVersion != l.compileSdkVersion {
l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
- _, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
- if len(filtered) != 0 {
- ctx.PropertyErrorf("lint.warning_checks",
- "Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered)
+ // Skip lint warning checks for NewApi warnings for libcore where they come from source
+ // files that reference the API they are adding (b/208656169).
+ if !strings.HasPrefix(ctx.ModuleDir(), "libcore") {
+ _, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
+
+ if len(filtered) != 0 {
+ ctx.PropertyErrorf("lint.warning_checks",
+ "Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered)
+ }
}
- _, filtered = android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks)
+
+ _, filtered := android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks)
if len(filtered) != 0 {
ctx.PropertyErrorf("lint.disabled_checks",
"Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered)
}
+
+ // TODO(b/238784089): Remove this workaround when the NewApi issues have been addressed in PermissionController
+ if ctx.ModuleName() == "PermissionController" {
+ l.extraMainlineLintErrors = android.FilterListPred(l.extraMainlineLintErrors, func(s string) bool {
+ return s != "NewApi"
+ })
+ l.properties.Lint.Warning_checks = append(l.properties.Lint.Warning_checks, "NewApi")
+ }
}
extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
@@ -354,6 +417,9 @@
}
}
+ l.extraLintCheckJars = append(l.extraLintCheckJars, android.PathForSource(ctx,
+ "prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar"))
+
rule := android.NewRuleBuilder(pctx, ctx).
Sbox(android.PathForModuleOut(ctx, "lint"),
android.PathForModuleOut(ctx, "lint.sbox.textproto")).
@@ -376,12 +442,16 @@
rule.Temporary(manifest)
}
- lintPaths := l.writeLintProjectXML(ctx, rule)
+ srcsList := android.PathForModuleOut(ctx, "lint", "lint-srcs.list")
+ srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
+ rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList)
+
+ lintPaths := l.writeLintProjectXML(ctx, rule, srcsList)
html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
- baseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
+ referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
@@ -395,26 +465,17 @@
rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
- var apiVersionsName, apiVersionsPrebuilt string
- if l.compileSdkKind == android.SdkModule || l.compileSdkKind == android.SdkSystemServer {
- // When compiling an SDK module (or system server) we use the filtered
- // database because otherwise lint's
- // NewApi check produces too many false positives; This database excludes information
- // about classes created in mainline modules hence removing those false positives.
- apiVersionsName = "api_versions_public_filtered.xml"
- apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions-filtered.xml"
- } else {
- apiVersionsName = "api_versions.xml"
- apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions.xml"
+ files, ok := allLintDatabasefiles[l.compileSdkKind]
+ if !ok {
+ files = allLintDatabasefiles[android.SdkPublic]
}
-
var annotationsZipPath, apiVersionsXMLPath android.Path
if ctx.Config().AlwaysUsePrebuiltSdks() {
- annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
- apiVersionsXMLPath = android.PathForSource(ctx, apiVersionsPrebuilt)
+ annotationsZipPath = android.PathForSource(ctx, files.annotationPrebuiltpath)
+ apiVersionsXMLPath = android.PathForSource(ctx, files.apiVersionsPrebuiltPath)
} else {
- annotationsZipPath = copiedAnnotationsZipPath(ctx)
- apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx, apiVersionsName)
+ annotationsZipPath = copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName)
+ apiVersionsXMLPath = copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName)
}
cmd := rule.Command()
@@ -431,11 +492,11 @@
FlagWithOutput("--html ", html).
FlagWithOutput("--text ", text).
FlagWithOutput("--xml ", xml).
- FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()).
+ FlagWithArg("--compile-sdk-version ", strconv.Itoa(l.compileSdkVersion)).
FlagWithArg("--java-language-level ", l.javaLanguageLevel).
FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
- Flag("--exitcode").
+ Flag("--apply-suggestions"). // applies suggested fixes to files in the sandbox
Flags(l.properties.Lint.Flags).
Implicit(annotationsZipPath).
Implicit(apiVersionsXMLPath)
@@ -443,6 +504,10 @@
rule.Temporary(lintPaths.projectXML)
rule.Temporary(lintPaths.configXML)
+ if exitCode := ctx.Config().Getenv("ANDROID_LINT_SUPPRESS_EXIT_CODE"); exitCode == "" {
+ cmd.Flag("--exitcode")
+ }
+
if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
cmd.FlagWithArg("--check ", checkOnly)
}
@@ -452,9 +517,19 @@
cmd.FlagWithInput("--baseline ", lintBaseline.Path())
}
- cmd.FlagWithOutput("--write-reference-baseline ", baseline)
+ cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline)
- cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
+ cmd.Text("; EXITCODE=$?; ")
+
+ // The sources in the sandbox may have been modified by --apply-suggestions, zip them up and
+ // export them out of the sandbox. Do this before exiting so that the suggestions exit even after
+ // a fatal error.
+ cmd.BuiltTool("soong_zip").
+ FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")).
+ FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))).
+ FlagWithInput("-r ", srcsList)
+
+ cmd.Text("; if [ $EXITCODE != 0 ]; then if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit $EXITCODE; fi")
rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
@@ -464,9 +539,10 @@
rule.Build("lint", "lint")
l.outputs = lintOutputs{
- html: html,
- text: text,
- xml: xml,
+ html: html,
+ text: text,
+ xml: xml,
+ referenceBaseline: referenceBaseline,
depSets: depSetsBuilder.Build(),
}
@@ -498,9 +574,10 @@
}
type lintSingleton struct {
- htmlZip android.WritablePath
- textZip android.WritablePath
- xmlZip android.WritablePath
+ htmlZip android.WritablePath
+ textZip android.WritablePath
+ xmlZip android.WritablePath
+ referenceBaselineZip android.WritablePath
}
func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
@@ -528,46 +605,39 @@
return
}
- frameworkDocStubs := findModuleOrErr(ctx, "framework-doc-stubs")
- if frameworkDocStubs == nil {
- if !ctx.Config().AllowMissingDependencies() {
- ctx.Errorf("lint: missing framework-doc-stubs")
+ for _, sdk := range android.SortedKeys(allLintDatabasefiles) {
+ files := allLintDatabasefiles[sdk]
+ apiVersionsDb := findModuleOrErr(ctx, files.apiVersionsModule)
+ if apiVersionsDb == nil {
+ if !ctx.Config().AllowMissingDependencies() {
+ ctx.Errorf("lint: missing module api_versions_public")
+ }
+ return
}
- return
- }
- filteredDb := findModuleOrErr(ctx, "api-versions-xml-public-filtered")
- if filteredDb == nil {
- if !ctx.Config().AllowMissingDependencies() {
- ctx.Errorf("lint: missing api-versions-xml-public-filtered")
+ sdkAnnotations := findModuleOrErr(ctx, files.annotationsModule)
+ if sdkAnnotations == nil {
+ if !ctx.Config().AllowMissingDependencies() {
+ ctx.Errorf("lint: missing module sdk-annotations.zip")
+ }
+ return
}
- return
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.CpIfChanged,
+ Input: android.OutputFileForModule(ctx, sdkAnnotations, ""),
+ Output: copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName),
+ })
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.CpIfChanged,
+ Input: android.OutputFileForModule(ctx, apiVersionsDb, ".api_versions.xml"),
+ Output: copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName),
+ })
}
-
- ctx.Build(pctx, android.BuildParams{
- Rule: android.CpIfChanged,
- Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
- Output: copiedAnnotationsZipPath(ctx),
- })
-
- ctx.Build(pctx, android.BuildParams{
- Rule: android.CpIfChanged,
- Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
- Output: copiedAPIVersionsXmlPath(ctx, "api_versions.xml"),
- })
-
- ctx.Build(pctx, android.BuildParams{
- Rule: android.CpIfChanged,
- Input: android.OutputFileForModule(ctx, filteredDb, ""),
- Output: copiedAPIVersionsXmlPath(ctx, "api_versions_public_filtered.xml"),
- })
}
-func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
- return android.PathForOutput(ctx, "lint", "annotations.zip")
-}
-
-func copiedAPIVersionsXmlPath(ctx android.PathContext, name string) android.WritablePath {
+func copiedLintDatabaseFilesPath(ctx android.PathContext, name string) android.WritablePath {
return android.PathForOutput(ctx, "lint", name)
}
@@ -620,12 +690,15 @@
l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
- ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+ l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip")
+ zip(l.referenceBaselineZip, func(l *lintOutputs) android.Path { return l.referenceBaseline })
+
+ ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
}
func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
if !ctx.Config().UnbundledBuild() {
- ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+ ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
}
}
diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt
index 1eee354..1bb4996 100644
--- a/java/lint_defaults.txt
+++ b/java/lint_defaults.txt
@@ -1,11 +1,17 @@
# Treat LintError as fatal to catch invocation errors
--fatal_check LintError
+# ObsoleteLintCustomCheck is a warning by default, but lint ignores the
+# checks from the subject jar if this issue is raised.
+# This should be an error for AOSP. If we create a check, we expect it
+# to run, otherwise we want an error.
+--fatal_check ObsoleteLintCustomCheck
# Checks which do not apply to the platform (implementation
# in lint assumes that it's running on app code)
--disable_check AnimatorKeep
--disable_check AppBundleLocaleChanges
+--disable_check AppCompatCustomView
--disable_check BlockedPrivateApi
--disable_check CustomSplashScreen
--disable_check CustomX509TrustManager
@@ -22,6 +28,7 @@
--disable_check PrivateApi
--disable_check ProtectedPermissions
--disable_check QueryPermissionsNeeded
+--disable_check ReservedSystemPermission
--disable_check ScopedStorage
--disable_check ServiceCast
--disable_check SoonBlockedPrivateApi
@@ -33,10 +40,18 @@
# 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
+# Disable ChromeOS specific checks
+--disable_check PermissionImpliesUnsupportedChromeOsHardware
+# Disable UnsafeImplicitIntentLaunch until it can avoid false positives/crash
+# TODO(265425607)
+--disable_check UnsafeImplicitIntentLaunch
+# InvalidId will give errors on ids defined like android:id="@androidprv:id/contentPanel"
+--disable_check InvalidId
# Downgrade existing errors to warnings
--warning_check AppCompatResource # 55 occurences in 10 modules
--warning_check AppLinkUrlError # 111 occurences in 53 modules
+--warning_check BinderGetCallingInMainThread
--warning_check ByteOrderMark # 2 occurences in 2 modules
--warning_check DuplicateActivity # 3 occurences in 3 modules
--warning_check DuplicateDefinition # 3623 occurences in 48 modules
@@ -89,6 +104,7 @@
--warning_check StringFormatInvalid # 148 occurences in 11 modules
--warning_check StringFormatMatches # 4800 occurences in 30 modules
--warning_check UnknownId # 8 occurences in 7 modules
+--warning_check UnspecifiedImmutableFlag
--warning_check ValidFragment # 12 occurences in 5 modules
--warning_check ValidRestrictions # 5 occurences in 1 modules
--warning_check WebViewLayout # 3 occurences in 1 modules
@@ -100,6 +116,8 @@
--warning_check CoarseFineLocation
--warning_check IntentFilterExportedReceiver
+--warning_check MissingInflatedId
+--warning_check NotificationPermission
--warning_check QueryAllPackagesPermission
--warning_check RemoteViewLayout
--warning_check SupportAnnotationUsage
diff --git a/java/lint_test.go b/java/lint_test.go
index 456e6ba..ec901aa 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -113,8 +113,9 @@
t.Error("did not use the correct file for baseline")
}
- if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check NewApi") {
- t.Error("should check NewApi errors")
+ if !strings.Contains(*sboxProto.Commands[0].Command, "--warning_check NewApi") {
+ // TODO(b/268261262): Change this to check for --error_check
+ t.Error("should check NewApi warnings")
}
if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check SomeCheck") {
@@ -174,55 +175,83 @@
}
}
-func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
- bp := `
- java_library {
- name: "foo",
- srcs: [
- "a.java",
- ],
- static_libs: ["bar"],
- min_sdk_version: "29",
- sdk_version: "current",
- lint: {
- strict_updatability_linting: true,
- },
- }
-
- java_library {
- name: "bar",
- srcs: [
- "a.java",
- ],
- min_sdk_version: "29",
- sdk_version: "current",
- }
- `
- fs := android.MockFS{
- "lint-baseline.xml": nil,
- }
-
- result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
- RunTestWithBp(t, bp)
-
- foo := result.ModuleForTests("foo", "android_common")
- sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
- if !strings.Contains(*sboxProto.Commands[0].Command,
- "--baseline lint-baseline.xml --disallowed_issues NewApi") {
- t.Error("did not restrict baselining NewApi")
- }
-
- bar := result.ModuleForTests("bar", "android_common")
- sboxProto = android.RuleBuilderSboxProtoForTests(t, bar.Output("lint.sbox.textproto"))
- if !strings.Contains(*sboxProto.Commands[0].Command,
- "--baseline lint-baseline.xml --disallowed_issues NewApi") {
- t.Error("did not restrict baselining NewApi")
- }
-}
+// TODO(b/193460475): Re-enable this test
+//func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
+// bp := `
+// java_library {
+// name: "foo",
+// srcs: [
+// "a.java",
+// ],
+// static_libs: ["bar"],
+// min_sdk_version: "29",
+// sdk_version: "current",
+// lint: {
+// strict_updatability_linting: true,
+// },
+// }
+//
+// java_library {
+// name: "bar",
+// srcs: [
+// "a.java",
+// ],
+// min_sdk_version: "29",
+// sdk_version: "current",
+// }
+// `
+// fs := android.MockFS{
+// "lint-baseline.xml": nil,
+// }
+//
+// result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
+// RunTestWithBp(t, bp)
+//
+// foo := result.ModuleForTests("foo", "android_common")
+// sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+// if !strings.Contains(*sboxProto.Commands[0].Command,
+// "--baseline lint-baseline.xml --disallowed_issues NewApi") {
+// t.Error("did not restrict baselining NewApi")
+// }
+//
+// bar := result.ModuleForTests("bar", "android_common")
+// sboxProto = android.RuleBuilderSboxProtoForTests(t, bar.Output("lint.sbox.textproto"))
+// if !strings.Contains(*sboxProto.Commands[0].Command,
+// "--baseline lint-baseline.xml --disallowed_issues NewApi") {
+// t.Error("did not restrict baselining NewApi")
+// }
+//}
func TestJavaLintDatabaseSelectionFull(t *testing.T) {
- testCases := []string{
- "current", "core_platform", "system_current", "S", "30", "10000",
+ testCases := []struct {
+ sdk_version string
+ expected_file string
+ }{
+ {
+ "current",
+ "api_versions_public.xml",
+ }, {
+ "core_platform",
+ "api_versions_public.xml",
+ }, {
+ "system_current",
+ "api_versions_system.xml",
+ }, {
+ "module_current",
+ "api_versions_module_lib.xml",
+ }, {
+ "system_server_current",
+ "api_versions_system_server.xml",
+ }, {
+ "S",
+ "api_versions_public.xml",
+ }, {
+ "30",
+ "api_versions_public.xml",
+ }, {
+ "10000",
+ "api_versions_public.xml",
+ },
}
bp := `
java_library {
@@ -238,7 +267,7 @@
}
`
for _, testCase := range testCases {
- thisBp := strings.Replace(bp, "XXX", testCase, 1)
+ thisBp := strings.Replace(bp, "XXX", testCase.sdk_version, 1)
result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, FixtureWithPrebuiltApis(map[string][]string{
"30": {"foo"},
@@ -248,49 +277,8 @@
foo := result.ModuleForTests("foo", "android_common")
sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
- if strings.Contains(*sboxProto.Commands[0].Command,
- "/api_versions_public_filtered.xml") {
- t.Error("used public-filtered lint api database for case", testCase)
- }
- if !strings.Contains(*sboxProto.Commands[0].Command,
- "/api_versions.xml") {
+ if !strings.Contains(*sboxProto.Commands[0].Command, "/"+testCase.expected_file) {
t.Error("did not use full api database for case", testCase)
}
}
-
-}
-
-func TestJavaLintDatabaseSelectionPublicFiltered(t *testing.T) {
- testCases := []string{
- "module_current", "system_server_current",
- }
- bp := `
- java_library {
- name: "foo",
- srcs: [
- "a.java",
- ],
- min_sdk_version: "29",
- sdk_version: "module_current",
- lint: {
- strict_updatability_linting: true,
- },
- }
-`
- for _, testCase := range testCases {
- thisBp := strings.Replace(bp, "XXX", testCase, 1)
- result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules).
- RunTestWithBp(t, thisBp)
-
- foo := result.ModuleForTests("foo", "android_common")
- sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
- if !strings.Contains(*sboxProto.Commands[0].Command,
- "/api_versions_public_filtered.xml") {
- t.Error("did not use public-filtered lint api database for case", testCase)
- }
- if strings.Contains(*sboxProto.Commands[0].Command,
- "/api_versions.xml") {
- t.Error("used full api database for case", testCase)
- }
- }
}
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index f0de7a4..07fb92c 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -103,7 +103,7 @@
func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
b.hiddenAPIDepsMutator(ctx)
- if SkipDexpreoptBootJars(ctx) {
+ if !dexpreopt.IsDex2oatNeeded(ctx) {
return
}
@@ -129,8 +129,7 @@
// Add dependencies on all the non-updatable module configured in the "boot" boot image. That does
// not include modules configured in the "art" boot image.
- bootImageConfig := b.getImageConfig(ctx)
- addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules, platformBootclasspathBootJarDepTag)
+ addDependenciesOntoBootImageModules(ctx, b.platformJars(ctx), platformBootclasspathBootJarDepTag)
// Add dependencies on all the apex jars.
apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
@@ -187,12 +186,8 @@
bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
- // Nothing to do if skipping the dexpreopt of boot image jars.
- if SkipDexpreoptBootJars(ctx) {
- return
- }
-
- b.generateBootImageBuildActions(ctx, platformModules, apexModules)
+ b.generateBootImageBuildActions(ctx)
+ b.copyApexBootJarsForAppsDexpreopt(ctx, apexModules)
}
// Generate classpaths.proto config
@@ -205,7 +200,7 @@
func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
// Include all non APEX jars
- jars := b.getImageConfig(ctx).modules
+ jars := b.platformJars(ctx)
// Include jars from APEXes that don't populate their classpath proto config.
remainingJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
@@ -222,6 +217,10 @@
return jars
}
+func (b *platformBootclasspathModule) platformJars(ctx android.PathContext) android.ConfiguredJarList {
+ return defaultBootImageConfig(ctx).modules.RemoveList(artBootImageConfig(ctx).modules)
+}
+
// checkPlatformModules ensures that the non-updatable modules supplied are not part of an
// apex module.
func (b *platformBootclasspathModule) checkPlatformModules(ctx android.ModuleContext, modules []android.Module) {
@@ -231,7 +230,7 @@
fromUpdatableApex := apexInfo.Updatable
if fromUpdatableApex {
// error: this jar is part of an updatable apex
- ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the framework boot image", ctx.OtherModuleName(m), apexInfo.InApexVariants)
+ ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the platform bootclasspath", ctx.OtherModuleName(m), apexInfo.InApexVariants)
} else {
// ok: this jar is part of the platform or a non-updatable apex
}
@@ -266,10 +265,6 @@
}
}
-func (b *platformBootclasspathModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
- return defaultBootImageConfig(ctx)
-}
-
// generateHiddenAPIBuildActions generates all the hidden API related build rules.
func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
@@ -405,36 +400,41 @@
}
// generateBootImageBuildActions generates ninja rules related to the boot image creation.
-func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext, platformModules, apexModules []android.Module) {
+func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext) {
// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
// GenerateSingletonBuildActions method as it cannot create it for itself.
dexpreopt.GetGlobalSoongConfig(ctx)
- imageConfig := b.getImageConfig(ctx)
- if imageConfig == nil {
- return
- }
-
global := dexpreopt.GetGlobalConfig(ctx)
if !shouldBuildBootImages(ctx.Config(), global) {
return
}
- // Generate the framework profile rule
- bootFrameworkProfileRule(ctx, imageConfig)
+ frameworkBootImageConfig := defaultBootImageConfig(ctx)
+ bootFrameworkProfileRule(ctx, frameworkBootImageConfig)
+ b.generateBootImage(ctx, frameworkBootImageName)
+ b.generateBootImage(ctx, mainlineBootImageName)
+ dumpOatRules(ctx, frameworkBootImageConfig)
+}
- // Copy platform module dex jars to their predefined locations.
- platformBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, platformModules)
- copyBootJarsToPredefinedLocations(ctx, platformBootDexJarsByModule, imageConfig.dexPathsByModule)
+func (b *platformBootclasspathModule) generateBootImage(ctx android.ModuleContext, imageName string) {
+ imageConfig := genBootImageConfigs(ctx)[imageName]
- // Copy apex module dex jars to their predefined locations.
- config := GetApexBootConfig(ctx)
- apexBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, apexModules)
- copyBootJarsToPredefinedLocations(ctx, apexBootDexJarsByModule, config.dexPathsByModule)
+ modules := b.getModulesForImage(ctx, imageConfig)
+
+ // Copy module dex jars to their predefined locations.
+ bootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, modules)
+ copyBootJarsToPredefinedLocations(ctx, bootDexJarsByModule, imageConfig.dexPathsByModule)
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
+ // If dexpreopt of boot image jars should be skipped, generate only a profile.
+ global := dexpreopt.GetGlobalConfig(ctx)
+ if global.DisablePreoptBootImages {
+ return
+ }
+
// Build boot image files for the android variants.
androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
@@ -443,6 +443,34 @@
// Build boot image files for the host variants. There are use directly by ART host side tests.
buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
+}
- dumpOatRules(ctx, imageConfig)
+// Copy apex module dex jars to their predefined locations. They will be used for dexpreopt for apps.
+func (b *platformBootclasspathModule) copyApexBootJarsForAppsDexpreopt(ctx android.ModuleContext, apexModules []android.Module) {
+ config := GetApexBootConfig(ctx)
+ apexBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, apexModules)
+ copyBootJarsToPredefinedLocations(ctx, apexBootDexJarsByModule, config.dexPathsByModule)
+}
+
+func (b *platformBootclasspathModule) getModulesForImage(ctx android.ModuleContext, imageConfig *bootImageConfig) []android.Module {
+ modules := make([]android.Module, 0, imageConfig.modules.Len())
+ for i := 0; i < imageConfig.modules.Len(); i++ {
+ found := false
+ for _, module := range b.configuredModules {
+ name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+ if name == imageConfig.modules.Jar(i) {
+ modules = append(modules, module)
+ found = true
+ break
+ }
+ }
+ if !found && !ctx.Config().AllowMissingDependencies() {
+ ctx.ModuleErrorf(
+ "Boot image '%s' module '%s' not added as a dependency of platform_bootclasspath",
+ imageConfig.name,
+ imageConfig.modules.Jar(i))
+ return []android.Module{}
+ }
+ }
+ return modules
}
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index cb09020..ff2da4b 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -18,14 +18,12 @@
"testing"
"android/soong/android"
- "android/soong/dexpreopt"
)
// Contains some simple tests for platform_bootclasspath.
var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers(
PrepareForTestWithJavaDefaultModules,
- dexpreopt.PrepareForTestByEnablingDexpreopt,
)
func TestPlatformBootclasspath(t *testing.T) {
@@ -51,6 +49,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",
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 655021f..d417291 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -15,12 +15,11 @@
package java
import (
+ "fmt"
"path/filepath"
"android/soong/android"
"github.com/google/blueprint"
-
- "fmt"
)
func init() {
@@ -55,7 +54,6 @@
type platformCompatConfig struct {
android.ModuleBase
- android.SdkBase
properties platformCompatConfigProperties
installDirPath android.InstallPath
@@ -127,7 +125,6 @@
func PlatformCompatConfigFactory() android.Module {
module := &platformCompatConfig{}
module.AddProperties(&module.properties)
- android.InitSdkAwareModule(module)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
@@ -178,7 +175,6 @@
// A prebuilt version of the platform compat config module.
type prebuiltCompatConfigModule struct {
android.ModuleBase
- android.SdkBase
prebuilt android.Prebuilt
properties prebuiltCompatConfigProperties
@@ -213,7 +209,6 @@
m := &prebuiltCompatConfigModule{}
m.AddProperties(&m.properties)
android.InitSingleSourcePrebuiltModule(m, &m.properties, "Metadata")
- android.InitSdkAwareModule(m)
android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
return m
}
@@ -223,18 +218,6 @@
metadata android.Path
}
-// isModulePreferredByCompatConfig checks to see whether the module is preferred for use by
-// platform compat config.
-func isModulePreferredByCompatConfig(module android.Module) bool {
- // A versioned prebuilt_platform_compat_config, i.e. foo-platform-compat-config@current should be
- // ignored.
- if android.IsModuleInVersionedSdk(module) {
- return false
- }
-
- return android.IsModulePreferred(module)
-}
-
func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
var compatConfigMetadata android.Paths
@@ -244,7 +227,7 @@
return
}
if c, ok := module.(platformCompatConfigMetadataProvider); ok {
- if !isModulePreferredByCompatConfig(module) {
+ if !android.IsModulePreferred(module) {
return
}
metadata := c.compatConfigMetadata()
diff --git a/java/plugin.go b/java/plugin.go
index 123dbd4..731dfda 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -66,7 +66,8 @@
// ConvertWithBp2build is used to convert android_app to Bazel.
func (p *Plugin) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
pluginName := p.Name()
- commonAttrs, depLabels := p.convertLibraryAttrsBp2Build(ctx)
+ commonAttrs, bp2BuildInfo := p.convertLibraryAttrsBp2Build(ctx)
+ depLabels := bp2BuildInfo.DepLabels
deps := depLabels.Deps
deps.Append(depLabels.StaticDeps)
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c7d8999..206d995 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -135,19 +135,6 @@
mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
}
-func createLatestApiModuleExtensionVersionFile(mctx android.LoadHookContext, name string, version string) {
- genruleProps := struct {
- Name *string
- Srcs []string
- Out []string
- Cmd *string
- }{}
- genruleProps.Name = proptools.StringPtr(name)
- genruleProps.Out = []string{name}
- genruleProps.Cmd = proptools.StringPtr("echo " + version + " > $(out)")
- mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
-}
-
func createEmptyFile(mctx android.LoadHookContext, name string) {
props := struct {
Name *string
@@ -246,10 +233,9 @@
type latestApiInfo struct {
module, scope, path string
version int
- isExtensionApiFile bool
}
- getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo {
+ getLatest := func(files []string) map[string]latestApiInfo {
m := make(map[string]latestApiInfo)
for _, f := range files {
module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
@@ -259,35 +245,28 @@
key := module + "." + scope
info, exists := m[key]
if !exists || version > info.version {
- m[key] = latestApiInfo{module, scope, f, version, isExtensionApiFile}
+ m[key] = latestApiInfo{module, scope, f, version}
}
}
return m
}
- latest := getLatest(apiLevelFiles, false)
+ latest := getLatest(apiLevelFiles)
if p.properties.Extensions_dir != nil {
extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt")
- for k, v := range getLatest(extensionApiFiles, true) {
- 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
+ for k, v := range getLatest(extensionApiFiles) {
+ 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)
}
+ // The extension version is always at least as new as the last sdk int version (potentially identical)
+ latest[k] = v
}
}
// Sort the keys in order to make build.ninja stable
- for _, k := range android.SortedStringKeys(latest) {
+ for _, k := range android.SortedKeys(latest) {
info := latest[k]
name := PrebuiltApiModuleName(info.module, info.scope, "latest")
- latestExtensionVersionModuleName := PrebuiltApiModuleName(info.module, info.scope, "latest.extension_version")
- if info.isExtensionApiFile {
- createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, strconv.Itoa(info.version))
- } else {
- createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, "-1")
- }
createApiModule(mctx, name, info.path)
}
@@ -305,7 +284,7 @@
}
}
// Create empty incompatibilities files for remaining modules
- for _, k := range android.SortedStringKeys(latest) {
+ for _, k := range android.SortedKeys(latest) {
if _, ok := incompatibilities[k]; !ok {
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 75422ad..2b84353 100644
--- a/java/prebuilt_apis_test.go
+++ b/java/prebuilt_apis_test.go
@@ -61,7 +61,7 @@
}
func TestPrebuiltApis_WithExtensions(t *testing.T) {
- runTestWithBaseExtensionLevel := func(v int) (foo_input string, bar_input string) {
+ runTestWithBaseExtensionLevel := func(v int) (foo_input, bar_input, baz_input string) {
result := android.GroupFixturePreparers(
prepareForJavaTest,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -69,7 +69,7 @@
}),
FixtureWithPrebuiltApisAndExtensions(map[string][]string{
"31": {"foo"},
- "32": {"foo", "bar"},
+ "32": {"foo", "bar", "baz"},
"current": {"foo", "bar"},
}, map[string][]string{
"1": {"foo"},
@@ -78,15 +78,24 @@
).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()
+ baz_input = result.ModuleForTests("baz.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)
+ // Extension 2 is the latest for both foo and bar, finalized after the base extension version.
+ foo_input, bar_input, baz_input := runTestWithBaseExtensionLevel(1)
+ android.AssertStringEquals(t, "Expected latest foo = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
+ android.AssertStringEquals(t, "Expected latest bar = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+ android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_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)
+ // Extension 2 is the latest for both foo and bar, finalized together with 32
+ foo_input, bar_input, baz_input = runTestWithBaseExtensionLevel(2)
+ android.AssertStringEquals(t, "Expected latest foo = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
+ android.AssertStringEquals(t, "Expected latest bar = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+ android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input)
+
+ // Extension 3 is the current extension, but it has not yet been finalized.
+ foo_input, bar_input, baz_input = runTestWithBaseExtensionLevel(3)
+ android.AssertStringEquals(t, "Expected latest foo = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
+ android.AssertStringEquals(t, "Expected latest bar = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+ android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input)
}
diff --git a/java/proto.go b/java/proto.go
index 5ba486f..c732d98 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -91,7 +91,7 @@
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")
@@ -143,7 +143,9 @@
}
type protoAttributes struct {
- Deps bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Sdk_version bazel.StringAttribute
+ Java_version bazel.StringAttribute
}
func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) *bazel.Label {
@@ -175,8 +177,11 @@
}
protoLabel := bazel.Label{Label: ":" + m.Name() + "_proto"}
- var protoAttrs protoAttributes
- protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}})
+ protoAttrs := &protoAttributes{
+ Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
+ Java_version: bazel.StringAttribute{Value: m.properties.Java_version},
+ Sdk_version: bazel.StringAttribute{Value: m.deviceProperties.Sdk_version},
+ }
name := m.Name() + suffix
@@ -186,7 +191,7 @@
Bzl_load_location: "//build/bazel/rules/java:proto.bzl",
},
android.CommonAttributes{Name: name},
- &protoAttrs)
+ protoAttrs)
return &bazel.Label{Label: ":" + name}
}
diff --git a/java/resourceshrinker.go b/java/resourceshrinker.go
new file mode 100644
index 0000000..6d59601
--- /dev/null
+++ b/java/resourceshrinker.go
@@ -0,0 +1,43 @@
+// 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 (
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+var shrinkResources = pctx.AndroidStaticRule("shrinkResources",
+ blueprint.RuleParams{
+ Command: `${config.ResourceShrinkerCmd} --output $out --input $in --raw_resources $raw_resources`,
+ CommandDeps: []string{"${config.ResourceShrinkerCmd}"},
+ }, "raw_resources")
+
+func ShrinkResources(ctx android.ModuleContext, apk android.Path, outputFile android.WritablePath) {
+ protoFile := android.PathForModuleOut(ctx, apk.Base()+".proto.apk")
+ aapt2Convert(ctx, protoFile, apk, "proto")
+ strictModeFile := android.PathForSource(ctx, "prebuilts/cmdline-tools/shrinker.xml")
+ protoOut := android.PathForModuleOut(ctx, apk.Base()+".proto.out.apk")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: shrinkResources,
+ Input: protoFile,
+ Output: protoOut,
+ Args: map[string]string{
+ "raw_resources": strictModeFile.String(),
+ },
+ })
+ aapt2Convert(ctx, outputFile, protoOut, "binary")
+}
diff --git a/java/resourceshrinker_test.go b/java/resourceshrinker_test.go
new file mode 100644
index 0000000..3bbf116
--- /dev/null
+++ b/java/resourceshrinker_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 (
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestShrinkResourcesArgs(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ ).RunTestWithBp(t, `
+ android_app {
+ name: "app_shrink",
+ platform_apis: true,
+ optimize: {
+ shrink_resources: true,
+ }
+ }
+
+ android_app {
+ name: "app_no_shrink",
+ platform_apis: true,
+ optimize: {
+ shrink_resources: false,
+ }
+ }
+ `)
+
+ appShrink := result.ModuleForTests("app_shrink", "android_common")
+ appShrinkResources := appShrink.Rule("shrinkResources")
+ android.AssertStringDoesContain(t, "expected shrinker.xml in app_shrink resource shrinker flags",
+ appShrinkResources.Args["raw_resources"], "shrinker.xml")
+
+ appNoShrink := result.ModuleForTests("app_no_shrink", "android_common")
+ if appNoShrink.MaybeRule("shrinkResources").Rule != nil {
+ t.Errorf("unexpected shrinkResources rule for app_no_shrink")
+ }
+}
diff --git a/java/robolectric.go b/java/robolectric.go
index 999da6f..008b8b1 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -131,9 +131,14 @@
r.forceOSType = ctx.Config().BuildOS
r.forceArchType = ctx.Config().BuildArch
- r.testConfig = tradefed.AutoGenRobolectricTestConfig(ctx, r.testProperties.Test_config,
- r.testProperties.Test_config_template, r.testProperties.Test_suites,
- r.testProperties.Auto_gen_config)
+ r.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: r.testProperties.Test_config,
+ TestConfigTemplateProp: r.testProperties.Test_config_template,
+ TestSuites: r.testProperties.Test_suites,
+ AutoGenConfig: r.testProperties.Auto_gen_config,
+ DeviceTemplate: "${RobolectricTestConfigTemplate}",
+ HostTemplate: "${RobolectricTestConfigTemplate}",
+ })
r.data = android.PathsForModuleSrc(ctx, r.testProperties.Data)
roboTestConfig := android.PathForModuleGen(ctx, "robolectric").
@@ -175,7 +180,7 @@
instrumentedApp.implementationAndResourcesJar,
}
- for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
+ handleLibDeps := func(dep android.Module) {
m := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
r.libs = append(r.libs, ctx.OtherModuleName(dep))
if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) {
@@ -183,15 +188,22 @@
}
}
+ for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
+ handleLibDeps(dep)
+ }
+ for _, dep := range ctx.GetDirectDepsWithTag(sdkLibTag) {
+ handleLibDeps(dep)
+ }
+
r.combinedJar = android.PathForModuleOut(ctx, "robolectric_combined", r.outputFile.Base())
TransformJarsToJar(ctx, r.combinedJar, "combine jars", combinedJarJars, android.OptionalPath{},
false, nil, nil)
// TODO: this could all be removed if tradefed was used as the test runner, it will find everything
// annotated as a test and run it.
- for _, src := range r.compiledJavaSrcs {
+ for _, src := range r.uniqueSrcFiles {
s := src.Rel()
- if !strings.HasSuffix(s, "Test.java") {
+ if !strings.HasSuffix(s, "Test.java") && !strings.HasSuffix(s, "Test.kt") {
continue
} else if strings.HasSuffix(s, "/BaseRobolectricTest.java") {
continue
@@ -289,6 +301,10 @@
entries.ExtraEntries = append(entries.ExtraEntries,
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+ entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "robolectric-tests")
+ if r.testConfig != nil {
+ entries.SetPath("LOCAL_FULL_TEST_CONFIG", r.testConfig)
+ }
})
entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
@@ -320,13 +336,12 @@
func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, tests []string) {
fmt.Fprintln(w, "")
- fmt.Fprintln(w, "include $(CLEAR_VARS)")
+ fmt.Fprintln(w, "include $(CLEAR_VARS)", " # java.robolectricTest")
fmt.Fprintln(w, "LOCAL_MODULE :=", name)
- fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES :=", module)
- fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES += ", strings.Join(r.libs, " "))
+ android.AndroidMkEmitAssignList(w, "LOCAL_JAVA_LIBRARIES", []string{module}, r.libs)
fmt.Fprintln(w, "LOCAL_TEST_PACKAGE :=", String(r.robolectricProperties.Instrumentation_for))
fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String())
- fmt.Fprintln(w, "LOCAL_ROBOTEST_FILES :=", strings.Join(tests, " "))
+ android.AndroidMkEmitAssignList(w, "LOCAL_ROBOTEST_FILES", tests)
if t := r.robolectricProperties.Test_options.Timeout; t != nil {
fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
}
diff --git a/java/rro.go b/java/rro.go
index b136e3f..53faca0 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -142,11 +142,15 @@
aaptLinkFlags = append(aaptLinkFlags,
"--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
}
+ if r.overridableProperties.Category != nil {
+ aaptLinkFlags = append(aaptLinkFlags,
+ "--rename-overlay-category "+*r.overridableProperties.Category)
+ }
r.aapt.buildActions(ctx, r, nil, nil, false, aaptLinkFlags...)
// Sign the built package
- _, certificates := collectAppDeps(ctx, r, false, false)
- certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
+ _, _, certificates := collectAppDeps(ctx, r, false, false)
+ r.certificate, certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
var lineageFile android.Path
if lineage := String(r.properties.Lineage); lineage != "" {
@@ -156,7 +160,6 @@
rotationMinSdkVersion := String(r.properties.RotationMinSdkVersion)
SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile, rotationMinSdkVersion)
- r.certificate = certificates[0]
r.outputFile = signed
partition := rroPartition(ctx)
@@ -172,15 +175,19 @@
return ""
}
-func (r *RuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (r *RuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
if r.properties.Min_sdk_version != nil {
- return android.SdkSpecFrom(ctx, *r.properties.Min_sdk_version)
+ return android.ApiLevelFrom(ctx, *r.properties.Min_sdk_version)
}
- return r.SdkVersion(ctx)
+ return r.SdkVersion(ctx).ApiLevel
}
-func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
- return r.SdkVersion(ctx)
+func (r *RuntimeResourceOverlay) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
+ return android.SdkSpecPrivate.ApiLevel
+}
+
+func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ return r.SdkVersion(ctx).ApiLevel
}
func (r *RuntimeResourceOverlay) Certificate() Certificate {
@@ -217,6 +224,9 @@
// the target package name of this overlay app. The target package name in the manifest file is used if one was not given.
Target_package_name *string
+
+ // the rro category of this overlay. The category in the manifest file is used if one was not given.
+ Category *string
}
type OverrideRuntimeResourceOverlay struct {
diff --git a/java/rro_test.go b/java/rro_test.go
index 00ba5ba..8067a47 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -201,6 +201,7 @@
base: "foo_overlay",
package_name: "com.android.bar.overlay",
target_package_name: "com.android.bar",
+ category: "mycategory",
}
`)
@@ -212,6 +213,7 @@
targetVariant string
packageFlag string
targetPackageFlag string
+ categoryFlag string
}{
{
variantName: "android_common",
@@ -228,6 +230,7 @@
targetVariant: "android_common_bar",
packageFlag: "com.android.bar.overlay",
targetPackageFlag: "com.android.bar",
+ categoryFlag: "mycategory",
},
}
for _, expected := range expectedVariants {
@@ -249,6 +252,7 @@
checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
+ checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-category", expected.categoryFlag)
}
}
diff --git a/java/sdk.go b/java/sdk.go
index 0dddd40..8b4918a 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -57,8 +57,10 @@
return JAVA_VERSION_8
} else if sdk.FinalOrFutureInt() <= 31 {
return JAVA_VERSION_9
- } else {
+ } else if sdk.FinalOrFutureInt() <= 33 {
return JAVA_VERSION_11
+ } else {
+ return JAVA_VERSION_17
}
}
@@ -146,10 +148,11 @@
toModule := func(module string, aidl android.Path) sdkDep {
// Select the kind of system modules needed for the sdk version.
systemModulesKind := systemModuleKind(sdkVersion.Kind, android.FutureApiLevel)
+ systemModules := android.JavaApiLibraryName(ctx.Config(), fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind))
return sdkDep{
useModule: true,
- bootclasspath: []string{module, config.DefaultLambdaStubsLibrary},
- systemModules: fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind),
+ bootclasspath: []string{module, android.JavaApiLibraryName(ctx.Config(), config.DefaultLambdaStubsLibrary)},
+ systemModules: systemModules,
java9Classpath: []string{module},
frameworkResModule: "framework-res",
aidl: android.OptionalPathForPath(aidl),
@@ -189,25 +192,21 @@
bootclasspath: corePlatformBootclasspathLibraries(ctx),
noFrameworksLibs: true,
}
- case android.SdkPublic:
- return toModule("android_stubs_current", sdkFrameworkAidlPath(ctx))
- case android.SdkSystem:
- return toModule("android_system_stubs_current", sdkFrameworkAidlPath(ctx))
- case android.SdkTest:
- return toModule("android_test_stubs_current", sdkFrameworkAidlPath(ctx))
+ case android.SdkPublic, android.SdkSystem, android.SdkTest:
+ return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), sdkFrameworkAidlPath(ctx))
case android.SdkCore:
return sdkDep{
useModule: true,
- bootclasspath: []string{"core.current.stubs", config.DefaultLambdaStubsLibrary},
- systemModules: "core-public-stubs-system-modules",
+ bootclasspath: []string{android.SdkCore.JavaLibraryName(ctx.Config()), android.JavaApiLibraryName(ctx.Config(), config.DefaultLambdaStubsLibrary)},
+ systemModules: android.JavaApiLibraryName(ctx.Config(), "core-public-stubs-system-modules"),
noFrameworksLibs: true,
}
case android.SdkModule:
// TODO(146757305): provide .apk and .aidl that have more APIs for modules
- return toModule("android_module_lib_stubs_current", nonUpdatableFrameworkAidlPath(ctx))
+ return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), nonUpdatableFrameworkAidlPath(ctx))
case android.SdkSystemServer:
// TODO(146757305): provide .apk and .aidl that have more APIs for modules
- return toModule("android_system_server_stubs_current", sdkFrameworkAidlPath(ctx))
+ return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), sdkFrameworkAidlPath(ctx))
default:
panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw))
}
@@ -270,9 +269,9 @@
// Create framework.aidl by extracting anything that implements android.os.Parcelable from the SDK stubs modules.
func createSdkFrameworkAidl(ctx android.SingletonContext) {
stubsModules := []string{
- "android_stubs_current",
- "android_test_stubs_current",
- "android_system_stubs_current",
+ android.SdkPublic.JavaLibraryName(ctx.Config()),
+ android.SdkTest.JavaLibraryName(ctx.Config()),
+ android.SdkSystem.JavaLibraryName(ctx.Config()),
}
combinedAidl := sdkFrameworkAidlPath(ctx)
@@ -287,7 +286,7 @@
// Creates a version of framework.aidl for the non-updatable part of the platform.
func createNonUpdatableFrameworkAidl(ctx android.SingletonContext) {
- stubsModules := []string{"android_module_lib_stubs_current"}
+ stubsModules := []string{android.SdkModule.JavaLibraryName(ctx.Config())}
combinedAidl := nonUpdatableFrameworkAidlPath(ctx)
tempPath := tempPathForRestat(ctx, combinedAidl)
@@ -382,10 +381,7 @@
} else if ctx.Config().FrameworksBaseDirExists(ctx) && !ctx.Config().AlwaysUsePrebuiltSdks() {
cmd.Text("cat")
apiTxtFileModules := []string{
- "frameworks-base-api-current.txt",
- "frameworks-base-api-system-current.txt",
- "frameworks-base-api-module-lib-current.txt",
- "frameworks-base-api-system-server-current.txt",
+ "api_fingerprint",
}
count := 0
ctx.VisitAllModules(func(module android.Module) {
@@ -396,10 +392,10 @@
}
})
if count != len(apiTxtFileModules) {
- ctx.Errorf("Could not find all the expected API modules %v, found %d\n", apiTxtFileModules, count)
+ ctx.Errorf("Could not find expected API module %v, found %d\n", apiTxtFileModules, count)
return
}
- cmd.Text("| md5sum | cut -d' ' -f1 >").
+ cmd.Text(">").
Output(out)
} else {
// Unbundled build
diff --git a/java/sdk_library.go b/java/sdk_library.go
index a2fc044..103f1ac 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -28,6 +28,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/dexpreopt"
)
@@ -469,7 +470,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.
@@ -546,14 +547,14 @@
// The properties specific to the module-lib api scope
//
- // Unless explicitly specified by using test.enabled the module-lib api scope is
- // disabled by default.
+ // Unless explicitly specified by using module_lib.enabled the module_lib api
+ // scope is disabled by default.
Module_lib ApiScopeProperties
// The properties specific to the system-server api scope
//
- // Unless explicitly specified by using test.enabled the module-lib api scope is
- // disabled by default.
+ // Unless explicitly specified by using system_server.enabled the
+ // system_server api scope is disabled by default.
System_server ApiScopeProperties
// Determines if the stubs are preferred over the implementation library
@@ -752,7 +753,7 @@
// commonSdkLibraryAndImportModule defines the interface that must be provided by a module that
// embeds the commonToSdkLibraryAndImport struct.
type commonSdkLibraryAndImportModule interface {
- android.SdkAware
+ android.Module
BaseModuleName() string
}
@@ -831,18 +832,14 @@
// Name of the java_library module that compiles the stubs source.
func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
baseName := c.module.BaseModuleName()
- return c.module.SdkMemberComponentName(baseName, func(name string) string {
- return c.namingScheme.stubsLibraryModuleName(apiScope, name)
- })
+ return c.namingScheme.stubsLibraryModuleName(apiScope, baseName)
}
// Name of the droidstubs module that generates the stubs source and may also
// generate/check the API.
func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
baseName := c.module.BaseModuleName()
- return c.module.SdkMemberComponentName(baseName, func(name string) string {
- return c.namingScheme.stubsSourceModuleName(apiScope, name)
- })
+ return c.namingScheme.stubsSourceModuleName(apiScope, baseName)
}
// The component names for different outputs of the java_sdk_library.
@@ -1167,6 +1164,8 @@
type SdkLibrary struct {
Library
+ android.BazelModuleBase
+
sdkLibraryProperties sdkLibraryProperties
// Map from api scope to the scope specific property structure.
@@ -1232,7 +1231,7 @@
var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil)
func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) {
- android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx).ApiLevel, func(c android.ModuleContext, do android.PayloadDepsCallback) {
+ android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.ModuleContext, do android.PayloadDepsCallback) {
ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
isExternal := !module.depIsInSameApex(ctx, child)
if am, ok := child.(android.ApexModule); ok {
@@ -1268,7 +1267,10 @@
func (module *SdkLibrary) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
for _, apiScope := range module.getGeneratedApiScopes(ctx) {
// Add dependencies to the stubs library
- ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsLibraryModuleName(apiScope))
+ stubModuleName := module.stubsLibraryModuleName(apiScope)
+ // Use JavaApiLibraryName function to be redirected to stubs generated from .txt if applicable
+ stubModuleName = android.JavaApiLibraryName(ctx.Config(), stubModuleName)
+ ctx.AddVariationDependencies(nil, apiScope.stubsTag, stubModuleName)
// 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))
@@ -1380,7 +1382,7 @@
})
// Make the set of components exported by this module available for use elsewhere.
- exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)}
+ exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedKeys(exportedComponents)}
ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo)
// Provide additional information for inclusion in an sdk's generated .info file.
@@ -1603,6 +1605,7 @@
Srcs []string
Installable *bool
Sdk_version *string
+ Api_surface *string
System_modules *string
Libs []string
Output_javadoc_comments *bool
@@ -1642,12 +1645,14 @@
props.Srcs = append(props.Srcs, module.properties.Srcs...)
props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
props.Sdk_version = module.deviceProperties.Sdk_version
+ props.Api_surface = &apiScope.name
props.System_modules = module.deviceProperties.System_modules
props.Installable = proptools.BoolPtr(false)
// A droiddoc module has only one Libs property and doesn't distinguish between
// shared libs and static libs. So we need to add both of these libs to Libs property.
props.Libs = module.properties.Libs
props.Libs = append(props.Libs, module.properties.Static_libs...)
+ props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...)
props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
props.Java_version = module.properties.Java_version
@@ -1750,7 +1755,7 @@
}
}
- mctx.CreateModule(DroidstubsFactory, &props)
+ mctx.CreateModule(DroidstubsFactory, &props).(*Droidstubs).CallHookIfAvailable(mctx)
}
func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
@@ -1773,7 +1778,7 @@
// Creates the xml file that publicizes the runtime library
func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
- moduleMinApiLevel := module.Library.MinSdkVersion(mctx).ApiLevel
+ moduleMinApiLevel := module.Library.MinSdkVersion(mctx)
var moduleMinApiLevelStr = moduleMinApiLevel.String()
if moduleMinApiLevel == android.NoneApiLevel {
moduleMinApiLevelStr = "current"
@@ -2051,7 +2056,6 @@
module.InitSdkLibraryProperties()
android.InitApexModule(module)
- android.InitSdkAwareModule(module)
InitJavaModule(module, android.HostAndDeviceSupported)
// Initialize the map from scope to scope specific properties.
@@ -2083,9 +2087,48 @@
module.CreateInternalModules(ctx)
}
})
+ android.InitBazelModule(module)
return module
}
+type bazelSdkLibraryAttributes struct {
+ Public bazel.StringAttribute
+ System bazel.StringAttribute
+ Test bazel.StringAttribute
+ Module_lib bazel.StringAttribute
+ System_server bazel.StringAttribute
+}
+
+// java_sdk_library bp2build converter
+func (module *SdkLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ if ctx.ModuleType() != "java_sdk_library" {
+ return
+ }
+
+ nameToAttr := make(map[string]bazel.StringAttribute)
+
+ for _, scope := range module.getGeneratedApiScopes(ctx) {
+ apiSurfaceFile := path.Join(module.getApiDir(), scope.apiFilePrefix+"current.txt")
+ var scopeStringAttribute bazel.StringAttribute
+ scopeStringAttribute.SetValue(apiSurfaceFile)
+ nameToAttr[scope.name] = scopeStringAttribute
+ }
+
+ attrs := bazelSdkLibraryAttributes{
+ Public: nameToAttr["public"],
+ System: nameToAttr["system"],
+ Test: nameToAttr["test"],
+ Module_lib: nameToAttr["module-lib"],
+ System_server: nameToAttr["system-server"],
+ }
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "java_sdk_library",
+ Bzl_load_location: "//build/bazel/rules/java:sdk_library.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, &attrs)
+}
+
//
// SDK library prebuilts
//
@@ -2129,7 +2172,6 @@
android.DefaultableModuleBase
prebuilt android.Prebuilt
android.ApexModuleBase
- android.SdkBase
hiddenAPI
dexpreopter
@@ -2204,14 +2246,13 @@
allScopeProperties, scopeToProperties := createPropertiesInstance()
module.scopeProperties = scopeToProperties
- module.AddProperties(&module.properties, allScopeProperties)
+ module.AddProperties(&module.properties, allScopeProperties, &module.importDexpreoptProperties)
// Initialize information common between source and prebuilt.
module.initCommon(module)
android.InitPrebuiltModule(module, &[]string{""})
android.InitApexModule(module)
- android.InitSdkAwareModule(module)
InitJavaModule(module, android.HostAndDeviceSupported)
module.SetDefaultableHook(func(mctx android.DefaultableHookContext) {
@@ -2376,8 +2417,8 @@
}
// MinSdkVersion - Implements hiddenAPIModule
-func (module *SdkLibraryImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
- return android.SdkSpecNone
+func (module *SdkLibraryImport) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+ return android.NoneApiLevel
}
var _ hiddenAPIModule = (*SdkLibraryImport)(nil)
@@ -2449,18 +2490,24 @@
if di == nil {
return // An error has been reported by FindDeapexerProviderForModule.
}
- if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
+ dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(module.BaseModuleName())
+ if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
dexJarFile := makeDexJarPathFromPath(dexOutputPath)
module.dexJarFile = dexJarFile
installPath := android.PathForModuleInPartitionInstall(
- ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
+ ctx, "apex", ai.ApexVariationName, dexJarFileApexRootRelative)
module.installFile = installPath
module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
- // Dexpreopting.
module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath)
module.dexpreopter.isSDKLibrary = true
module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &module.dexpreopter)
+
+ if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil {
+ module.dexpreopter.inputProfilePathOnHost = profilePath
+ }
+
+ // Dexpreopting.
module.dexpreopt(ctx, dexOutputPath)
} else {
// This should never happen as a variant for a prebuilt_apex is only created if the
@@ -2589,7 +2636,7 @@
func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
name := module.BaseModuleName()
- return requiredFilesFromPrebuiltApexForImport(name)
+ return requiredFilesFromPrebuiltApexForImport(name, &module.dexpreopter)
}
// java_sdk_library_xml
@@ -2998,6 +3045,8 @@
//
// This means that the device won't recognise this library as installed.
Max_device_sdk *string
+
+ DexPreoptProfileGuided *bool `supported_build_releases:"UpsideDownCake+"`
}
type scopeProperties struct {
@@ -3051,6 +3100,10 @@
s.On_bootclasspath_before = sdk.commonSdkLibraryProperties.On_bootclasspath_before
s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk
s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk
+
+ if sdk.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
+ s.DexPreoptProfileGuided = proptools.BoolPtr(true)
+ }
}
func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -3066,6 +3119,10 @@
if len(s.Permitted_packages) > 0 {
propertySet.AddProperty("permitted_packages", s.Permitted_packages)
}
+ dexPreoptSet := propertySet.AddPropertySet("dex_preopt")
+ if s.DexPreoptProfileGuided != nil {
+ dexPreoptSet.AddProperty("profile_guided", proptools.Bool(s.DexPreoptProfileGuided))
+ }
stem := s.Stem
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 096bca8..1d0c13d 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -120,6 +120,7 @@
result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
+ result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
result.ModuleForTests("foo.api.public.28", "")
result.ModuleForTests("foo.api.system.28", "")
@@ -1385,3 +1386,29 @@
}
`)
}
+
+func TestJavaSdkLibrary_StubOnlyLibs_PassedToDroidstubs(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ public: {
+ enabled: true,
+ },
+ stub_only_libs: ["bar-lib"],
+ }
+
+ java_library {
+ name: "bar-lib",
+ srcs: ["b.java"],
+ }
+ `)
+
+ // The foo.stubs.source should depend on bar-lib
+ fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
+ android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs, "bar-lib")
+}
diff --git a/java/system_modules.go b/java/system_modules.go
index fec8eba..0efa1a4 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -114,7 +114,6 @@
module.AddProperties(&module.properties)
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
- android.InitSdkAwareModule(module)
return module
}
@@ -130,7 +129,6 @@
type SystemModules struct {
android.ModuleBase
android.DefaultableModuleBase
- android.SdkBase
properties SystemModulesProperties
@@ -215,7 +213,6 @@
android.InitPrebuiltModule(module, &module.properties.Libs)
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
- android.InitSdkAwareModule(module)
return module
}
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index a2cd261..17d301b 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -83,7 +83,6 @@
type SystemServerClasspathModule struct {
android.ModuleBase
android.ApexModuleBase
- android.SdkBase
ClasspathFragmentBase
@@ -113,7 +112,6 @@
m := &SystemServerClasspathModule{}
m.AddProperties(&m.properties)
android.InitApexModule(m)
- android.InitSdkAwareModule(m)
initClasspathFragment(m, SYSTEMSERVERCLASSPATH)
android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
return m
@@ -160,7 +158,7 @@
// This is an exception to support end-to-end test for ApexdUnitTests, until such support exists.
if android.InList("test_service-apexd", possibleUpdatableModules) {
jars = jars.Append("com.android.apex.test_package", "test_service-apexd")
- } else if global.ApexSystemServerJars.Len() > 0 && len(unknown) > 0 && !android.IsModuleInVersionedSdk(ctx.Module()) {
+ } else if global.ApexSystemServerJars.Len() > 0 && len(unknown) > 0 {
// For non test apexes, make sure that all contents are actually declared in make.
ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_SYSTEM_SERVER_JARS", unknown)
}
@@ -331,7 +329,6 @@
// array.
android.InitPrebuiltModule(m, &[]string{"placeholder"})
android.InitApexModule(m)
- android.InitSdkAwareModule(m)
android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
return m
}
diff --git a/java/testing.go b/java/testing.go
index 49430ee..6671bf0 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -56,6 +56,8 @@
"build/make/target/product/security": nil,
// Required to generate Java used-by API coverage
"build/soong/scripts/gen_java_usedby_apex.sh": nil,
+ // Needed for the global lint checks provided from frameworks/base
+ "prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar": nil,
}.AddToFixture(),
)
@@ -74,12 +76,11 @@
// Needed for R8 rules on apps
"build/make/core/proguard.flags": nil,
"build/make/core/proguard_basic_keeps.flags": nil,
+ "prebuilts/cmdline-tools/shrinker.xml": nil,
}.AddToFixture(),
)
-// Test fixture preparer that will define all default java modules except the
-// fake_tool_binary for dex2oatd.
-var PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd = android.GroupFixturePreparers(
+var prepareForTestWithJavaDefaultModulesBase = android.GroupFixturePreparers(
// Make sure that all the module types used in the defaults are registered.
PrepareForTestWithJavaBuildComponents,
prepareForTestWithFrameworkDeps,
@@ -89,13 +90,21 @@
// Test fixture preparer that will define default java modules, e.g. standard prebuilt modules.
var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers(
- PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd,
- dexpreopt.PrepareForTestWithFakeDex2oatd,
+ prepareForTestWithJavaDefaultModulesBase,
+ dexpreopt.FixtureDisableDexpreoptBootImages(true),
+ dexpreopt.FixtureDisableDexpreopt(true),
)
// Provides everything needed by dexpreopt.
var PrepareForTestWithDexpreopt = android.GroupFixturePreparers(
- PrepareForTestWithJavaDefaultModules,
+ prepareForTestWithJavaDefaultModulesBase,
+ dexpreopt.PrepareForTestWithFakeDex2oatd,
+ dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
+
+// Provides everything needed by dexpreopt except the fake_tool_binary for dex2oatd.
+var PrepareForTestWithDexpreoptWithoutFakeDex2oatd = android.GroupFixturePreparers(
+ prepareForTestWithJavaDefaultModulesBase,
dexpreopt.PrepareForTestByEnablingDexpreopt,
)
@@ -195,7 +204,7 @@
imports_sdk_version: "none",
imports_compile_dex: true,
}
- `, strings.Join(android.SortedStringKeys(apiLevel2Modules), `", "`))
+ `, strings.Join(android.SortedKeys(apiLevel2Modules), `", "`))
for release, modules := range apiLevel2Modules {
mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
@@ -365,6 +374,7 @@
"core.current.stubs",
"legacy.core.platform.api.stubs",
"stable.core.platform.api.stubs",
+
"kotlin-stdlib",
"kotlin-stdlib-jdk7",
"kotlin-stdlib-jdk8",
@@ -384,6 +394,28 @@
`, extra)
}
+ extraApiLibraryModules := map[string]string{
+ "android_stubs_current.from-text": "api/current.txt",
+ "android_system_stubs_current.from-text": "api/system-current.txt",
+ "android_test_stubs_current.from-text": "api/test-current.txt",
+ "android_module_lib_stubs_current.from-text": "api/module-lib-current.txt",
+ "android_module_lib_stubs_current_full.from-text": "api/module-lib-current.txt",
+ "android_system_server_stubs_current.from-text": "api/system-server-current.txt",
+ "core.current.stubs.from-text": "api/current.txt",
+ "legacy.core.platform.api.stubs.from-text": "api/current.txt",
+ "stable.core.platform.api.stubs.from-text": "api/current.txt",
+ "core-lambda-stubs.from-text": "api/current.txt",
+ }
+
+ for libName, apiFile := range extraApiLibraryModules {
+ bp += fmt.Sprintf(`
+ java_api_library {
+ name: "%s",
+ api_files: ["%s"],
+ }
+ `, libName, apiFile)
+ }
+
bp += `
java_library {
name: "framework",
@@ -406,6 +438,10 @@
"core-module-lib-stubs-system-modules",
"legacy-core-platform-api-stubs-system-modules",
"stable-core-platform-api-stubs-system-modules",
+ "core-public-stubs-system-modules.from-text",
+ "core-module-lib-stubs-system-modules.from-text",
+ "legacy-core-platform-api-stubs-system-modules.from-text",
+ "stable-core-platform-api-stubs-system-modules.from-text",
}
for _, extra := range systemModules {
@@ -574,9 +610,9 @@
})
}
-// Sets the value of `installDirOnDevice` of the boot image config with the given name.
+// Sets the value of `installDir` of the boot image config with the given name.
func FixtureSetBootImageInstallDirOnDevice(name string, installDir string) android.FixturePreparer {
return FixtureModifyBootImageConfig(name, func(config *bootImageConfig) {
- config.installDirOnDevice = installDir
+ config.installDir = installDir
})
}
diff --git a/licenses/Android.bp b/licenses/Android.bp
index 8db001f..dee72ed 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -32,8 +32,17 @@
}
license_kind {
+ name: "BSD-Like-Binary-Only",
+ conditions: [
+ "notice",
+ "by_exception_only",
+ "proprietary",
+ ],
+}
+
+license_kind {
name: "SPDX-license-identifier-0BSD",
- conditions: ["unencumbered"],
+ conditions: ["permissive"],
url: "https://spdx.org/licenses/0BSD",
}
@@ -753,7 +762,7 @@
license_kind {
name: "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
- conditions: ["restricted"],
+ conditions: ["permissive"],
url: "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html",
}
@@ -801,7 +810,7 @@
license_kind {
name: "SPDX-license-identifier-GPL-with-classpath-exception",
- conditions: ["restricted"],
+ conditions: ["permissive"],
}
license_kind {
@@ -830,88 +839,99 @@
license_kind {
name: "SPDX-license-identifier-LGPL",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
}
license_kind {
name: "SPDX-license-identifier-LGPL-2.0",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-2.0.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-2.0+",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-2.0+.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-2.0-only",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-2.0-only.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-2.0-or-later",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-2.0-or-later.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-2.1",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-2.1.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-2.1+",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-2.1+.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-2.1-only",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-2.1-only.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-2.1-or-later",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-2.1-or-later.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-3.0",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-3.0.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-3.0+",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-3.0+.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-3.0-only",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-3.0-only.html",
}
license_kind {
name: "SPDX-license-identifier-LGPL-3.0-or-later",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPL-3.0-or-later.html",
}
license_kind {
name: "SPDX-license-identifier-LGPLLR",
- conditions: ["restricted"],
+ conditions: ["restricted_if_statically_linked"],
url: "https://spdx.org/licenses/LGPLLR.html",
}
license_kind {
+ name: "SPDX-license-identifier-Linux-syscall-note",
+ // expanding visibility requires approval from an OSPO lawyer or pcounsel
+ visibility: [
+ "//external/libbpf:__subpackages__",
+ "//prebuilts/vsdk:__subpackages__",
+ ],
+ conditions: ["permissive"],
+ url: "https://spdx.org/licenses/Linux-syscall-note.html",
+}
+
+license_kind {
name: "SPDX-license-identifier-LPL-1.02",
conditions: ["notice"],
url: "https://spdx.org/licenses/LPL-1.02.html",
@@ -924,7 +944,7 @@
license_kind {
name: "SPDX-license-identifier-MIT-0",
- conditions: ["notice"],
+ conditions: ["permissive"],
url: "https://spdx.org/licenses/MIT-0.html",
}
@@ -1143,7 +1163,7 @@
license_kind {
name: "SPDX-license-identifier-WTFPL",
- conditions: ["notice"],
+ conditions: ["permissive"],
url: "https://spdx.org/licenses/WTFPL.html",
}
diff --git a/licenses/OWNERS b/licenses/OWNERS
deleted file mode 100644
index fddfc48..0000000
--- a/licenses/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file * = bbadour@google.com
-
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 003b275..412a23b 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -22,6 +22,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/cc"
"android/soong/etc"
)
@@ -36,7 +37,7 @@
}
func registerLinkerConfigBuildComponent(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("linker_config", linkerConfigFactory)
+ ctx.RegisterModuleType("linker_config", LinkerConfigFactory)
}
type linkerConfigProperties struct {
@@ -52,6 +53,7 @@
type linkerConfig struct {
android.ModuleBase
+ android.BazelModuleBase
properties linkerConfigProperties
outputFilePath android.OutputPath
@@ -100,6 +102,28 @@
ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
}
+type linkerConfigAttributes struct {
+ Src bazel.LabelAttribute
+}
+
+func (l *linkerConfig) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ if l.properties.Src == nil {
+ ctx.PropertyErrorf("src", "empty src is not supported")
+ return
+ }
+ src := android.BazelLabelForModuleSrcSingle(ctx, *l.properties.Src)
+ targetModuleProperties := bazel.BazelTargetModuleProperties{
+ Rule_class: "linker_config",
+ Bzl_load_location: "//build/bazel/rules:linker_config.bzl",
+ }
+ ctx.CreateBazelTargetModule(
+ targetModuleProperties,
+ android.CommonAttributes{Name: l.Name()},
+ &linkerConfigAttributes{
+ Src: bazel.LabelAttribute{Value: &src},
+ })
+}
+
func BuildLinkerConfig(ctx android.ModuleContext, builder *android.RuleBuilder,
input android.Path, otherModules []android.Module, output android.OutputPath) {
@@ -141,10 +165,11 @@
// linker_config generates protobuf file from json file. This protobuf file will be used from
// linkerconfig while generating ld.config.txt. Format of this file can be found from
// https://android.googlesource.com/platform/system/linkerconfig/+/master/README.md
-func linkerConfigFactory() android.Module {
+func LinkerConfigFactory() android.Module {
m := &linkerConfig{}
m.AddProperties(&m.properties)
android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibFirst)
+ android.InitBazelModule(m)
return m
}
diff --git a/linkerconfig/proto/Android.bp b/linkerconfig/proto/Android.bp
index 3b1e4ab..754e7bf 100644
--- a/linkerconfig/proto/Android.bp
+++ b/linkerconfig/proto/Android.bp
@@ -19,14 +19,6 @@
python_library_host {
name: "linker_config_proto",
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- },
- },
srcs: [
"linker_config.proto",
],
diff --git a/linkerconfig/proto/OWNERS b/linkerconfig/proto/OWNERS
deleted file mode 100644
index 31f0460..0000000
--- a/linkerconfig/proto/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-kiyoungkim@google.com
-jiyong@google.com
-jooyung@google.com
diff --git a/mk2rbc/Android.bp b/mk2rbc/Android.bp
index 4fa3eb6..cd80a4d 100644
--- a/mk2rbc/Android.bp
+++ b/mk2rbc/Android.bp
@@ -19,7 +19,7 @@
blueprint_go_binary {
name: "mk2rbc",
- srcs: ["cmd/mk2rbc.go"],
+ srcs: ["mk2rbc/mk2rbc.go"],
deps: [
"mk2rbc-lib",
"androidmk-parser",
diff --git a/mk2rbc/android_products_test.go b/mk2rbc/android_products_test.go
index f8c930a..5f55f6a 100644
--- a/mk2rbc/android_products_test.go
+++ b/mk2rbc/android_products_test.go
@@ -29,7 +29,6 @@
}
expectedProducts := map[string]string{
"aosp_cf_x86_tv": abspath("vsoc_x86/tv/device.mk"),
- "aosp_tv_arm": abspath("aosp_tv_arm.mk"),
"aosp_tv_arm64": abspath("aosp_tv_arm64.mk"),
}
if !reflect.DeepEqual(actualProducts, expectedProducts) {
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
index 9266520..6a6eb46 100644
--- a/mk2rbc/expr.go
+++ b/mk2rbc/expr.go
@@ -741,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 {
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 8dc0149..77394d9 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -77,6 +77,8 @@
"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},
+ "and": &andOrParser{isAnd: true},
+ "clear-var-list": &simpleCallParser{name: baseName + ".clear_var_list", returnType: starlarkTypeVoid, addGlobals: true, addHandle: true},
"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},
@@ -86,7 +88,7 @@
"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": &firstOrLastwordCallParser{isLastWord: false},
+ "firstword": &simpleCallParser{name: baseName + ".first_word", returnType: starlarkTypeString},
"foreach": &foreachCallParser{},
"if": &ifCallParser{},
"info": &makeControlFuncParser{name: baseName + ".mkinfo"},
@@ -97,7 +99,7 @@
"is-product-in-list": &isProductInListCallParser{},
"is-vendor-board-platform": &isVendorBoardPlatformCallParser{},
"is-vendor-board-qcom": &isVendorBoardQcomCallParser{},
- "lastword": &firstOrLastwordCallParser{isLastWord: true},
+ "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"},
@@ -105,6 +107,7 @@
"math_gt": &mathComparisonCallParser{op: ">"},
"math_lt": &mathComparisonCallParser{op: "<"},
"my-dir": &myDirCallParser{},
+ "or": &andOrParser{isAnd: false},
"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},
@@ -114,8 +117,11 @@
"sort": &simpleCallParser{name: baseName + ".mksort", returnType: starlarkTypeList},
"strip": &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString},
"subst": &substCallParser{fname: "subst"},
+ "to-lower": &lowerUpperParser{isUpper: false},
+ "to-upper": &lowerUpperParser{isUpper: true},
"warning": &makeControlFuncParser{name: baseName + ".mkwarning"},
"word": &wordCallParser{},
+ "words": &wordsCallParser{},
"wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList},
}
@@ -130,6 +136,14 @@
"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
// we allow seeing their definitions in the product config files.
var ignoredDefines = map[string]bool{
@@ -459,6 +473,7 @@
predefined := []struct{ name, value string }{
{"SRC_TARGET_DIR", filepath.Join("build", "make", "target")},
{"LOCAL_PATH", filepath.Dir(ss.mkFile)},
+ {"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"},
@@ -531,7 +546,7 @@
func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) []starlarkNode {
// Handle only simple variables
- if !a.Name.Const() {
+ if !a.Name.Const() || a.Target != nil {
return []starlarkNode{ctx.newBadNode(a, "Only simple variables are handled")}
}
name := a.Name.Strings[0]
@@ -542,6 +557,12 @@
if strings.HasPrefix(name, "override ") {
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) {
@@ -556,9 +577,6 @@
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 {
- return []starlarkNode{&exprNode{xBad}}
- }
inferred_type := asgn.value.typ()
if inferred_type != starlarkTypeUnknown {
lhs.setValueType(inferred_type)
@@ -567,21 +585,19 @@
if lhs.valueType() == starlarkTypeList {
xConcat, xBad := ctx.buildConcatExpr(a)
if xBad != nil {
- return []starlarkNode{&exprNode{expr: xBad}}
- }
- switch len(xConcat.items) {
- case 0:
- asgn.value = &listExpr{}
- case 1:
- asgn.value = xConcat.items[0]
- default:
- asgn.value = xConcat
+ 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 {
- return []starlarkNode{&exprNode{expr: xBad}}
- }
}
if asgn.lhs.valueType() == starlarkTypeString &&
@@ -811,35 +827,32 @@
// rblf.inherit(handle, _e[0], _e[1])
//
var matchingPaths []string
- varPath, ok := pathExpr.(*interpolateExpr)
- if !ok {
+ 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 len(pathPattern) == 1 {
+ pathPattern = append(pathPattern, "")
+ }
+ matchingPaths = ctx.findMatchingPaths(pathPattern)
+ needsWarning = pathPattern[0] == "" && len(ctx.includeTops) == 0
+ } else if len(ctx.includeTops) > 0 {
+ matchingPaths = append(matchingPaths, ctx.findMatchingPaths([]string{"", ""})...)
+ } else {
return []starlarkNode{ctx.newBadNode(v, "inherit-product/include argument is too complex")}
}
- pathPattern := []string{varPath.chunks[0]}
- for _, chunk := range varPath.chunks[1:] {
- if chunk != "" {
- pathPattern = append(pathPattern, chunk)
- }
- }
- 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)
- }
// Safeguard against $(call inherit-product,$(PRODUCT_PATH))
const maxMatchingFiles = 150
if len(matchingPaths) > maxMatchingFiles {
return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)}
}
- needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0
- res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
+ 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
@@ -856,17 +869,31 @@
}
// Create regular expression from the pattern
- s_regexp := "^" + regexp.QuoteMeta(pattern[0])
+ regexString := "^" + regexp.QuoteMeta(pattern[0])
for _, s := range pattern[1:] {
- s_regexp += ".*" + regexp.QuoteMeta(s)
+ regexString += ".*" + regexp.QuoteMeta(s)
}
- s_regexp += "$"
- rex := regexp.MustCompile(s_regexp)
+ regexString += "$"
+ rex := regexp.MustCompile(regexString)
+
+ includeTopRegexString := ""
+ if len(ctx.includeTops) > 0 {
+ for i, top := range ctx.includeTops {
+ if i > 0 {
+ includeTopRegexString += "|"
+ }
+ includeTopRegexString += "^" + regexp.QuoteMeta(top)
+ }
+ } else {
+ includeTopRegexString = ".*"
+ }
+
+ includeTopRegex := regexp.MustCompile(includeTopRegexString)
// Now match
var res []string
for _, p := range files {
- if rex.MatchString(p) {
+ if rex.MatchString(p) && includeTopRegex.MatchString(p) {
res = append(res, p)
}
}
@@ -889,8 +916,9 @@
})
}
-func (ctx *parseContext) handleInclude(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) []starlarkNode {
- return ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) starlarkNode {
+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}
})
}
@@ -1068,6 +1096,18 @@
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,
@@ -1113,8 +1153,6 @@
switch call.name {
case baseName + ".filter":
return ctx.parseCompareFilterFuncResult(directive, call, value, isEq)
- case baseName + ".expand_wildcard":
- return ctx.parseCompareWildcardFuncResult(directive, call, value, !isEq), true
case baseName + ".findstring":
return ctx.parseCheckFindstringFuncResult(directive, call, value, !isEq), true
case baseName + ".strip":
@@ -1159,22 +1197,6 @@
}
}
-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,
xCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
if isEmptyString(xValue) {
@@ -1259,7 +1281,34 @@
// 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
@@ -1309,9 +1358,8 @@
} else {
return ctx.newBadExpr(node, "cannot handle invoking %s", name)
}
- } else {
- return ctx.newBadExpr(node, "cannot handle %s", refDump)
}
+ return ctx.newBadExpr(node, "cannot handle %s", refDump)
}
type simpleCallParser struct {
@@ -1387,6 +1435,51 @@
return &stringLiteralExpr{literal: filepath.Dir(ctx.script.mkFile)}
}
+type andOrParser struct {
+ isAnd bool
+}
+
+func (p *andOrParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ if args.Empty() {
+ return ctx.newBadExpr(node, "and/or function must have at least 1 argument")
+ }
+ op := "or"
+ if p.isAnd {
+ op = "and"
+ }
+
+ argsParsed := make([]starlarkExpr, 0)
+
+ for _, arg := range args.Split(",") {
+ arg.TrimLeftSpaces()
+ arg.TrimRightSpaces()
+ x := ctx.parseMakeString(node, arg)
+ if xBad, ok := x.(*badExpr); ok {
+ return xBad
+ }
+ argsParsed = append(argsParsed, x)
+ }
+ typ := starlarkTypeUnknown
+ for _, arg := range argsParsed {
+ if typ != arg.typ() && arg.typ() != starlarkTypeUnknown && typ != starlarkTypeUnknown {
+ return ctx.newBadExpr(node, "Expected all arguments to $(or) or $(and) to have the same type, found %q and %q", typ.String(), arg.typ().String())
+ }
+ if arg.typ() != starlarkTypeUnknown {
+ typ = arg.typ()
+ }
+ }
+ result := argsParsed[0]
+ for _, arg := range argsParsed[1:] {
+ result = &binaryOpExpr{
+ left: result,
+ right: arg,
+ op: op,
+ returnType: typ,
+ }
+ }
+ return result
+}
+
type isProductInListCallParser struct{}
func (p *isProductInListCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
@@ -1558,11 +1651,21 @@
}
}
- 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) {
@@ -1587,6 +1690,16 @@
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
+ }
}
}
@@ -1637,9 +1750,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")
@@ -1647,35 +1762,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) {
@@ -1752,6 +1872,18 @@
if len(nodes) == 0 {
return []starlarkNode{}
} else if len(nodes) == 1 {
+ // Replace the nodeLocator with one that just returns the location of
+ // the $(eval) node. Otherwise, statements inside an $(eval) will show as
+ // being on line 1 of the file, because they're on line 1 of
+ // strings.NewReader(args.Dump())
+ oldNodeLocator := ctx.script.nodeLocator
+ ctx.script.nodeLocator = func(pos mkparser.Pos) int {
+ return oldNodeLocator(node.Pos())
+ }
+ defer func() {
+ ctx.script.nodeLocator = oldNodeLocator
+ }()
+
switch n := nodes[0].(type) {
case *mkparser.Assignment:
if n.Name.Const() {
@@ -1759,10 +1891,41 @@
}
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 and comments are supported")}
+ return []starlarkNode{ctx.newBadNode(node, "Eval expression too complex; only assignments, comments, includes, and inherit-products are supported")}
+}
+
+type lowerUpperParser struct {
+ isUpper bool
+}
+
+func (p *lowerUpperParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ fn := "lower"
+ if p.isUpper {
+ fn = "upper"
+ }
+ arg := ctx.parseMakeString(node, args)
+
+ return &callExpr{
+ object: arg,
+ name: fn,
+ returnType: starlarkTypeString,
+ }
}
func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr {
@@ -1822,7 +1985,7 @@
result = []starlarkNode{res}
}
case "include", "-include":
- result = ctx.handleInclude(node, ctx.parseMakeString(node, x.Args), x.Name[0] != '-')
+ result = ctx.handleInclude(x)
case "ifeq", "ifneq", "ifdef", "ifndef":
result = []starlarkNode{ctx.handleIfBlock(x)}
default:
diff --git a/mk2rbc/cmd/mk2rbc.go b/mk2rbc/mk2rbc/mk2rbc.go
similarity index 97%
rename from mk2rbc/cmd/mk2rbc.go
rename to mk2rbc/mk2rbc/mk2rbc.go
index e84eacd..cc83430 100644
--- a/mk2rbc/cmd/mk2rbc.go
+++ b/mk2rbc/mk2rbc/mk2rbc.go
@@ -173,7 +173,7 @@
}
ok := true
for _, mkFile := range files {
- ok = convertOne(mkFile) && ok
+ ok = convertOne(mkFile, []string{}) && ok
}
if *launcher != "" {
@@ -183,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"))
}
@@ -201,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(
@@ -310,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() {
@@ -356,6 +360,7 @@
return false
}
}
+ loadStack = append(loadStack, mkFile)
ok = true
if *recurse {
for _, sub := range ss.SubConfigFiles() {
@@ -363,7 +368,7 @@
if _, err := os.Stat(sub); os.IsNotExist(err) {
continue
}
- ok = convertOne(sub) && ok
+ ok = convertOne(sub, loadStack) && ok
}
}
converted[mkFile] = ss
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index de75129..7e68026 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -117,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")
`,
},
{
@@ -568,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
`,
},
@@ -808,6 +812,10 @@
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): $(dir foo/bar))
$(info $(firstword $(PRODUCT_COPY_FILES)))
@@ -830,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", "$(dir foo/bar): %s" % 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", 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")
@@ -975,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",
@@ -1142,6 +1154,13 @@
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
+$(call inherit-product,vendor/$(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")
@@ -1156,6 +1175,28 @@
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)
+ _entry = {
+ "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+ }.get("vendor/%s" % 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" % ("vendor/%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)
`,
},
{
@@ -1242,13 +1283,15 @@
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 $@ $*")
`,
},
{
@@ -1269,6 +1312,7 @@
in: `
ifeq (,$(call foobar))
endif
+my_sources := $(local-generated-sources-dir)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -1276,6 +1320,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")
`,
},
{
@@ -1327,6 +1372,8 @@
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),\
@@ -1346,6 +1393,8 @@
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"]:
@@ -1509,24 +1558,132 @@
$(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))) \
-)
+ $(if $(MY_OTHER_VAR),$(eval PRODUCT_COPY_FILES += $(MY_OTHER_VAR):foo/bar/$(x))))
+$(foreach x,$(MY_LIST_VAR), \
+ $(eval include foo/$(x).mk))
+
+# Check that we get as least close to correct line numbers for errors on statements inside evals
+$(eval $(call inherit-product,$(A_VAR)))
+`,
+ 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)
+ # Check that we get as least close to correct line numbers for errors on statements inside evals
+ rblf.mk2rbc_error("product.mk:17", "inherit-product/include argument is too complex")
+`,
+ },
+ {
+ 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"
- # This is a test of eval functions
- rblf.mk2rbc_error("product.mk:5", "Eval expression too complex; only assignments and comments are supported")
- 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()
+`,
+ },
+ {
+ 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")
+`,
+ },
+ {
+ desc: "Conditional functions",
+ mkname: "product.mk",
+ in: `
+B := foo
+X := $(or $(A))
+X := $(or $(A),$(B))
+X := $(or $(A),$(B),$(C))
+X := $(and $(A))
+X := $(and $(A),$(B))
+X := $(and $(A),$(B),$(C))
+X := $(or $(A),$(B)) Y
+
+D := $(wildcard *.mk)
+X := $(or $(B),$(D))
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["B"] = "foo"
+ g["X"] = g.get("A", "")
+ g["X"] = g.get("A", "") or g["B"]
+ g["X"] = g.get("A", "") or g["B"] or g.get("C", "")
+ g["X"] = g.get("A", "")
+ g["X"] = g.get("A", "") and g["B"]
+ g["X"] = g.get("A", "") and g["B"] and g.get("C", "")
+ g["X"] = "%s Y" % g.get("A", "") or g["B"]
+ g["D"] = rblf.expand_wildcard("*.mk")
+ g["X"] = rblf.mk2rbc_error("product.mk:12", "Expected all arguments to $(or) or $(and) to have the same type, found \"string\" and \"list\"")
+`,
+ },
+ {
+
+ desc: "is-lower/is-upper",
+ mkname: "product.mk",
+ in: `
+X := $(call to-lower,aBc)
+X := $(call to-upper,aBc)
+X := $(call to-lower,$(VAR))
+X := $(call to-upper,$(VAR))
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["X"] = ("aBc").lower()
+ g["X"] = ("aBc").upper()
+ g["X"] = (g.get("VAR", "")).lower()
+ g["X"] = (g.get("VAR", "")).upper()
`,
},
}
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index 7c39b9e..a01abd8 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -83,7 +83,7 @@
}
type inheritedDynamicModule struct {
- path interpolateExpr
+ path starlarkExpr
candidateModules []*moduleInfo
loadAlways bool
location ErrorLocation
@@ -120,7 +120,7 @@
}
func (i inheritedDynamicModule) pathExpr() starlarkExpr {
- return &i.path
+ return i.path
}
func (i inheritedDynamicModule) needsLoadCheck() bool {
diff --git a/mk2rbc/soong_variables.go b/mk2rbc/soong_variables.go
index a52ec4f..7a6aa5f 100644
--- a/mk2rbc/soong_variables.go
+++ b/mk2rbc/soong_variables.go
@@ -67,7 +67,11 @@
var valueType starlarkType
switch typeString {
case "bool":
- valueType = starlarkTypeBool
+ // TODO: We run into several issues later on if we type this as a bool:
+ // - We still assign bool-typed variables to strings
+ // - When emitting the final results as make code, some bool's false values have to
+ // be an empty string, and some have to be false in order to match the make variables.
+ valueType = starlarkTypeString
case "csv":
// Only PLATFORM_VERSION_ALL_CODENAMES, and it's a list
valueType = starlarkTypeList
diff --git a/mk2rbc/test/android_products.mk.test b/mk2rbc/test/android_products.mk.test
index a2220ed..400ec35 100644
--- a/mk2rbc/test/android_products.mk.test
+++ b/mk2rbc/test/android_products.mk.test
@@ -1,4 +1,3 @@
PRODUCT_MAKEFILES := \
- $(LOCAL_DIR)/aosp_tv_arm.mk \
$(LOCAL_DIR)/aosp_tv_arm64.mk \
aosp_cf_x86_tv:$(LOCAL_DIR)/vsoc_x86/tv/device.mk
\ No newline at end of file
diff --git a/mk2rbc/types.go b/mk2rbc/types.go
index 46c6aa9..ac32507 100644
--- a/mk2rbc/types.go
+++ b/mk2rbc/types.go
@@ -14,6 +14,8 @@
package mk2rbc
+import "fmt"
+
// Starlark expression types we use
type starlarkType int
@@ -31,6 +33,25 @@
starlarkTypeVoid starlarkType = iota
)
+func (t starlarkType) String() string {
+ switch t {
+ case starlarkTypeList:
+ return "list"
+ case starlarkTypeString:
+ return "string"
+ case starlarkTypeInt:
+ return "int"
+ case starlarkTypeBool:
+ return "bool"
+ case starlarkTypeVoid:
+ return "void"
+ case starlarkTypeUnknown:
+ return "unknown"
+ default:
+ panic(fmt.Sprintf("Unknown starlark type %d", t))
+ }
+}
+
type hiddenArgType int
const (
diff --git a/multitree/Android.bp b/multitree/Android.bp
new file mode 100644
index 0000000..78c4962
--- /dev/null
+++ b/multitree/Android.bp
@@ -0,0 +1,20 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-multitree",
+ pkgPath: "android/soong/multitree",
+ deps: [
+ "blueprint",
+ "soong-android",
+ ],
+ srcs: [
+ "api_imports.go",
+ "api_surface.go",
+ "export.go",
+ "metadata.go",
+ "import.go",
+ ],
+ pluginFor: ["soong_build"],
+}
diff --git a/multitree/api_imports.go b/multitree/api_imports.go
new file mode 100644
index 0000000..07ec7bc
--- /dev/null
+++ b/multitree/api_imports.go
@@ -0,0 +1,102 @@
+// 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"
+ "strings"
+
+ "github.com/google/blueprint"
+)
+
+var (
+ apiImportNameSuffix = ".apiimport"
+)
+
+func init() {
+ RegisterApiImportsModule(android.InitRegistrationContext)
+ android.RegisterMakeVarsProvider(pctx, makeVarsProvider)
+}
+
+func RegisterApiImportsModule(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("api_imports", apiImportsFactory)
+}
+
+type ApiImports struct {
+ android.ModuleBase
+ properties apiImportsProperties
+}
+
+type apiImportsProperties struct {
+ Shared_libs []string // List of C shared libraries from API surfaces
+ Header_libs []string // List of C header libraries from API surfaces
+ Apex_shared_libs []string // List of C shared libraries with APEX stubs
+}
+
+// 'api_imports' is a module which describes modules available from API surfaces.
+// This module is required to get the list of all imported API modules, because
+// it is discouraged to loop and fetch all modules from its type information. The
+// only module with name 'api_imports' will be used from the build.
+func apiImportsFactory() android.Module {
+ module := &ApiImports{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ return module
+}
+
+func (imports *ApiImports) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // ApiImport module does not generate any build actions
+}
+
+type ApiImportInfo struct {
+ SharedLibs, HeaderLibs, ApexSharedLibs map[string]string
+}
+
+var ApiImportsProvider = blueprint.NewMutatorProvider(ApiImportInfo{}, "deps")
+
+// Store module lists into ApiImportInfo and share it over mutator provider.
+func (imports *ApiImports) DepsMutator(ctx android.BottomUpMutatorContext) {
+ generateNameMapWithSuffix := func(names []string) map[string]string {
+ moduleNameMap := make(map[string]string)
+ for _, name := range names {
+ moduleNameMap[name] = name + apiImportNameSuffix
+ }
+
+ return moduleNameMap
+ }
+
+ sharedLibs := generateNameMapWithSuffix(imports.properties.Shared_libs)
+ headerLibs := generateNameMapWithSuffix(imports.properties.Header_libs)
+ apexSharedLibs := generateNameMapWithSuffix(imports.properties.Apex_shared_libs)
+
+ ctx.SetProvider(ApiImportsProvider, ApiImportInfo{
+ SharedLibs: sharedLibs,
+ HeaderLibs: headerLibs,
+ ApexSharedLibs: apexSharedLibs,
+ })
+}
+
+func GetApiImportSuffix() string {
+ return apiImportNameSuffix
+}
+
+func makeVarsProvider(ctx android.MakeVarsContext) {
+ ctx.VisitAllModules(func(m android.Module) {
+ if i, ok := m.(*ApiImports); ok {
+ ctx.Strict("API_IMPORTED_SHARED_LIBRARIES", strings.Join(i.properties.Shared_libs, " "))
+ ctx.Strict("API_IMPORTED_HEADER_LIBRARIES", strings.Join(i.properties.Header_libs, " "))
+ }
+ })
+}
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/phony/phony.go b/phony/phony.go
index a31d402..760b79b 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -49,7 +49,7 @@
func (p *phony) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
- fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # phony.phony")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name)
data.Entries.WriteLicenseVariables(w)
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
index e49f3d4..fbb6212 100644
--- a/provenance/provenance_singleton.go
+++ b/provenance/provenance_singleton.go
@@ -35,9 +35,10 @@
mergeProvenanceMetaData = pctx.AndroidStaticRule("mergeProvenanceMetaData",
blueprint.RuleParams{
- Command: `rm -rf $out $out.temp && ` +
- `echo -e "# proto-file: build/soong/provenance/proto/provenance_metadata.proto\n# proto-message: ProvenanceMetaDataList" > $out && ` +
- `touch $out.temp && cat $out.temp $in | grep -v "^#.*" >> $out && rm -rf $out.temp`,
+ Command: `rm -rf $out && ` +
+ `echo "# proto-file: build/soong/provenance/proto/provenance_metadata.proto" > $out && ` +
+ `echo "# proto-message: ProvenanceMetaDataList" >> $out && ` +
+ `for file in $in; do echo '' >> $out; echo 'metadata {' | cat - $$file | grep -Ev "^#.*|^$$" >> $out; echo '}' >> $out; done`,
})
)
diff --git a/provenance/tools/gen_provenance_metadata.py b/provenance/tools/gen_provenance_metadata.py
index b33f911..f3f4d1f 100644
--- a/provenance/tools/gen_provenance_metadata.py
+++ b/provenance/tools/gen_provenance_metadata.py
@@ -16,6 +16,7 @@
import argparse
import hashlib
+import os.path
import sys
import google.protobuf.text_format as text_format
@@ -51,6 +52,11 @@
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",
diff --git a/provenance/tools/gen_provenance_metadata_test.py b/provenance/tools/gen_provenance_metadata_test.py
index 2fc04bf..1f69b8f 100644
--- a/provenance/tools/gen_provenance_metadata_test.py
+++ b/provenance/tools/gen_provenance_metadata_test.py
@@ -100,6 +100,11 @@
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"])
@@ -117,9 +122,11 @@
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/Android.bp b/python/Android.bp
index e49fa6a..7578673 100644
--- a/python/Android.bp
+++ b/python/Android.bp
@@ -9,13 +9,13 @@
"blueprint",
"soong-android",
"soong-tradefed",
+ "soong-cc",
],
srcs: [
- "androidmk.go",
"binary.go",
+ "bp2build.go",
"builder.go",
"defaults.go",
- "installer.go",
"library.go",
"proto.go",
"python.go",
diff --git a/python/androidmk.go b/python/androidmk.go
deleted file mode 100644
index 233d867..0000000
--- a/python/androidmk.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package python
-
-import (
- "path/filepath"
- "strings"
-
- "android/soong/android"
-)
-
-type subAndroidMkProvider interface {
- AndroidMk(*Module, *android.AndroidMkEntries)
-}
-
-func (p *Module) subAndroidMk(entries *android.AndroidMkEntries, obj interface{}) {
- if p.subAndroidMkOnce == nil {
- p.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
- }
- if androidmk, ok := obj.(subAndroidMkProvider); ok {
- if !p.subAndroidMkOnce[androidmk] {
- p.subAndroidMkOnce[androidmk] = true
- androidmk.AndroidMk(p, entries)
- }
- }
-}
-
-func (p *Module) AndroidMkEntries() []android.AndroidMkEntries {
- entries := android.AndroidMkEntries{OutputFile: p.installSource}
-
- p.subAndroidMk(&entries, p.installer)
-
- return []android.AndroidMkEntries{entries}
-}
-
-func (p *binaryDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- entries.Class = "EXECUTABLES"
-
- entries.ExtraEntries = append(entries.ExtraEntries,
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
- })
- base.subAndroidMk(entries, p.pythonInstaller)
-}
-
-func (p *testDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- entries.Class = "NATIVE_TESTS"
-
- entries.ExtraEntries = append(entries.ExtraEntries,
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
- if p.testConfig != nil {
- entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
- }
-
- entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
-
- entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
-
- entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(p.testProperties.Test_options.Unit_test))
- })
- base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
-}
-
-func (installer *pythonInstaller) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- entries.Required = append(entries.Required, "libc++")
- entries.ExtraEntries = append(entries.ExtraEntries,
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- path, file := filepath.Split(installer.path.String())
- stem := strings.TrimSuffix(file, filepath.Ext(file))
-
- entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
- entries.SetString("LOCAL_MODULE_PATH", path)
- entries.SetString("LOCAL_MODULE_STEM", stem)
- entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
- entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
- })
-}
diff --git a/python/binary.go b/python/binary.go
index 99c6259..a5db2f6 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -18,11 +18,10 @@
import (
"fmt"
+ "path/filepath"
+ "strings"
"android/soong/android"
- "android/soong/bazel"
-
- "github.com/google/blueprint/proptools"
)
func init() {
@@ -33,67 +32,12 @@
ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
}
-type bazelPythonBinaryAttributes struct {
- Main *string
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- Python_version *string
-}
-
-func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
- var main *string
- for _, propIntf := range m.GetProperties() {
- if props, ok := propIntf.(*BinaryProperties); ok {
- // main is optional.
- if props.Main != nil {
- main = props.Main
- break
- }
- }
- }
-
- // 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
- // mutator. This is sufficient for very simple python_binary_host modules
- // under Bionic.
- py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
- py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
- var python_version *string
- if py3Enabled && py2Enabled {
- panic(fmt.Errorf(
- "error for '%s' module: bp2build's python_binary_host converter does not support "+
- "converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
- } else if py2Enabled {
- python_version = &pyVersion2
- } else {
- // do nothing, since python_version defaults to PY3.
- }
-
- baseAttrs := m.makeArchVariantBaseAttributes(ctx)
- attrs := &bazelPythonBinaryAttributes{
- Main: main,
- Srcs: baseAttrs.Srcs,
- Deps: baseAttrs.Deps,
- Python_version: python_version,
- }
-
- props := bazel.BazelTargetModuleProperties{
- // Use the native py_binary rule.
- Rule_class: "py_binary",
- }
-
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{
- Name: m.Name(),
- Data: baseAttrs.Data,
- }, attrs)
-}
-
type BinaryProperties struct {
// the name of the source file that is the main entry point of the program.
// this file must also be listed in srcs.
// If left unspecified, module name is used instead.
// If name doesn’t match any filename in srcs, main must be specified.
- Main *string `android:"arch_variant"`
+ Main *string
// set the name of the output binary.
Stem *string `android:"arch_variant"`
@@ -116,53 +60,58 @@
Auto_gen_config *bool
}
-type binaryDecorator struct {
+type PythonBinaryModule struct {
+ PythonLibraryModule
binaryProperties BinaryProperties
- *pythonInstaller
+ // (.intermediate) module output path as installation source.
+ installSource android.Path
+
+ // Final installation path.
+ installedDest android.Path
+
+ androidMkSharedLibs []string
}
+var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
+var _ android.Module = (*PythonBinaryModule)(nil)
+
type IntermPathProvider interface {
IntermPathForModuleOut() android.OptionalPath
}
-var (
- StubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
-)
-
-func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
- module := newModule(hod, android.MultilibFirst)
- decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
-
- module.bootstrapper = decorator
- module.installer = decorator
-
- return module, decorator
+func NewBinary(hod android.HostOrDeviceSupported) *PythonBinaryModule {
+ return &PythonBinaryModule{
+ PythonLibraryModule: *newModule(hod, android.MultilibFirst),
+ }
}
func PythonBinaryHostFactory() android.Module {
- module, _ := NewBinary(android.HostSupported)
-
- android.InitBazelModule(module)
-
- return module.init()
+ return NewBinary(android.HostSupported).init()
}
-func (binary *binaryDecorator) autorun() bool {
- return BoolDefault(binary.binaryProperties.Autorun, true)
+func (p *PythonBinaryModule) init() android.Module {
+ p.AddProperties(&p.properties, &p.protoProperties)
+ p.AddProperties(&p.binaryProperties)
+ android.InitAndroidArchModule(p, p.hod, p.multilib)
+ android.InitDefaultableModule(p)
+ android.InitBazelModule(p)
+ return p
}
-func (binary *binaryDecorator) bootstrapperProps() []interface{} {
- return []interface{}{&binary.binaryProperties}
+func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+ p.buildBinary(ctx)
+ p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
+ p.installSource.Base(), p.installSource)
}
-func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
- embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
- depsSrcsZips android.Paths) android.OptionalPath {
-
+func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
+ embeddedLauncher := p.isEmbeddedLauncherEnabled()
+ depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx, embeddedLauncher)
main := ""
- if binary.autorun() {
- main = binary.getPyMainFile(ctx, srcsPathMappings)
+ if p.autorun() {
+ main = p.getPyMainFile(ctx, p.srcsPathMappings)
}
var launcherPath android.OptionalPath
@@ -177,16 +126,88 @@
}
})
}
+ srcsZips := make(android.Paths, 0, len(depsSrcsZips)+1)
+ if embeddedLauncher {
+ srcsZips = append(srcsZips, p.precompiledSrcsZip)
+ } else {
+ srcsZips = append(srcsZips, p.srcsZip)
+ }
+ srcsZips = append(srcsZips, depsSrcsZips...)
+ p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
+ p.getHostInterpreterName(ctx, p.properties.Actual_version),
+ main, p.getStem(ctx), srcsZips)
- binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
- binary.getHostInterpreterName(ctx, actualVersion),
- main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
+ var sharedLibs []string
+ // if embedded launcher is enabled, we need to collect the shared library dependencies of the
+ // launcher
+ for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
+ sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+ }
+ p.androidMkSharedLibs = sharedLibs
+}
- return android.OptionalPathForPath(binFile)
+func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries {
+ entries := android.AndroidMkEntries{OutputFile: android.OptionalPathForPath(p.installSource)}
+
+ entries.Class = "EXECUTABLES"
+
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+ })
+
+ entries.Required = append(entries.Required, "libc++")
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ path, file := filepath.Split(p.installedDest.String())
+ stem := strings.TrimSuffix(file, filepath.Ext(file))
+
+ entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
+ entries.SetString("LOCAL_MODULE_PATH", path)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
+ entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
+ entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
+ })
+
+ return []android.AndroidMkEntries{entries}
+}
+
+func (p *PythonBinaryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ p.PythonLibraryModule.DepsMutator(ctx)
+
+ if p.isEmbeddedLauncherEnabled() {
+ p.AddDepsOnPythonLauncherAndStdlib(ctx, pythonLibTag, launcherTag, launcherSharedLibTag, p.autorun(), ctx.Target())
+ }
+}
+
+// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
+// fulfilling the android.HostToolProvider interface.
+func (p *PythonBinaryModule) HostToolPath() android.OptionalPath {
+ // TODO: This should only be set when building host binaries -- tests built for device would be
+ // setting this incorrectly.
+ return android.OptionalPathForPath(p.installedDest)
+}
+
+// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
+func (p *PythonBinaryModule) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{p.installSource}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+func (p *PythonBinaryModule) isEmbeddedLauncherEnabled() bool {
+ return Bool(p.properties.Embedded_launcher)
+}
+
+func (b *PythonBinaryModule) autorun() bool {
+ return BoolDefault(b.binaryProperties.Autorun, true)
}
// get host interpreter name.
-func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getHostInterpreterName(ctx android.ModuleContext,
actualVersion string) string {
var interp string
switch actualVersion {
@@ -203,13 +224,13 @@
}
// find main program path within runfiles tree.
-func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getPyMainFile(ctx android.ModuleContext,
srcsPathMappings []pathMapping) string {
var main string
- if String(binary.binaryProperties.Main) == "" {
+ if String(p.binaryProperties.Main) == "" {
main = ctx.ModuleName() + pyExt
} else {
- main = String(binary.binaryProperties.Main)
+ main = String(p.binaryProperties.Main)
}
for _, path := range srcsPathMappings {
@@ -222,11 +243,21 @@
return ""
}
-func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
+func (p *PythonBinaryModule) getStem(ctx android.ModuleContext) string {
stem := ctx.ModuleName()
- if String(binary.binaryProperties.Stem) != "" {
- stem = String(binary.binaryProperties.Stem)
+ if String(p.binaryProperties.Stem) != "" {
+ stem = String(p.binaryProperties.Stem)
}
- return stem + String(binary.binaryProperties.Suffix)
+ return stem + String(p.binaryProperties.Suffix)
+}
+
+func installDir(ctx android.ModuleContext, dir, dir64, relative string) android.InstallPath {
+ if ctx.Arch().ArchType.Multilib == "lib64" && dir64 != "" {
+ dir = dir64
+ }
+ if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
+ dir = filepath.Join(dir, ctx.Arch().ArchType.String())
+ }
+ return android.PathForModuleInstall(ctx, dir, relative)
}
diff --git a/python/bp2build.go b/python/bp2build.go
new file mode 100644
index 0000000..cd3f2a1
--- /dev/null
+++ b/python/bp2build.go
@@ -0,0 +1,233 @@
+// Copyright 2023 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 python
+
+import (
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/bazel"
+)
+
+type bazelPythonLibraryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Imports bazel.StringListAttribute
+ Srcs_version *string
+}
+
+type bazelPythonProtoLibraryAttributes struct {
+ Deps bazel.LabelListAttribute
+}
+
+type baseAttributes struct {
+ // TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
+ //Pkg_path bazel.StringAttribute
+ // TODO: Related to Pkg_bath and similarLy gated
+ //Is_internal bazel.BoolAttribute
+ // Combines Srcs and Exclude_srcs
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ // Combines Data and Java_data (invariant)
+ Data bazel.LabelListAttribute
+ Imports bazel.StringListAttribute
+}
+
+func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
+ var attrs baseAttributes
+ archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
+ for axis, configToProps := range archVariantBaseProps {
+ for config, props := range configToProps {
+ if baseProps, ok := props.(*BaseProperties); ok {
+ attrs.Srcs.SetSelectValue(axis, config,
+ android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
+ attrs.Deps.SetSelectValue(axis, config,
+ android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
+ data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
+ data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
+ attrs.Data.SetSelectValue(axis, config, data)
+ }
+ }
+ }
+
+ 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))
+ }
+
+ // 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)
+ }
+
+ 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())
+ }
+ numFolders := strings.Count(pkg_path, "/") + 1
+ dots := make([]string, numFolders)
+ for i := 0; i < numFolders; i++ {
+ dots[i] = ".."
+ }
+ imports = strings.Join(dots, "/")
+ }
+ attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
+
+ return attrs
+}
+
+func (m *PythonLibraryModule) bp2buildPythonVersion(ctx android.TopDownMutatorContext) *string {
+ py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
+ py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+ if py2Enabled && !py3Enabled {
+ return &pyVersion2
+ } else if !py2Enabled && py3Enabled {
+ return &pyVersion3
+ } else if !py2Enabled && !py3Enabled {
+ ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
+ return &pyVersion3
+ } else {
+ return &pyVersion2And3
+ }
+}
+
+type bazelPythonBinaryAttributes struct {
+ Main *bazel.Label
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Python_version *string
+ Imports bazel.StringListAttribute
+}
+
+func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ // 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
+ // mutator. This is sufficient for very simple python_library modules under
+ // Bionic.
+ baseAttrs := p.makeArchVariantBaseAttributes(ctx)
+ pyVersion := p.bp2buildPythonVersion(ctx)
+ if *pyVersion == pyVersion2And3 {
+ // Libraries default to python 2 and 3
+ pyVersion = nil
+ }
+
+ attrs := &bazelPythonLibraryAttributes{
+ Srcs: baseAttrs.Srcs,
+ Deps: baseAttrs.Deps,
+ Srcs_version: pyVersion,
+ Imports: baseAttrs.Imports,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ // Use the native py_library rule.
+ Rule_class: "py_library",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: p.Name(),
+ Data: baseAttrs.Data,
+ }, attrs)
+}
+
+func (p *PythonBinaryModule) bp2buildBinaryProperties(ctx android.TopDownMutatorContext) (*bazelPythonBinaryAttributes, bazel.LabelListAttribute) {
+ // 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
+ // mutator. This is sufficient for very simple python_binary_host modules
+ // under Bionic.
+
+ baseAttrs := p.makeArchVariantBaseAttributes(ctx)
+ pyVersion := p.bp2buildPythonVersion(ctx)
+ if *pyVersion == pyVersion3 {
+ // Binaries default to python 3
+ pyVersion = nil
+ } else if *pyVersion == pyVersion2And3 {
+ ctx.ModuleErrorf("error for '%s' module: bp2build's python_binary_host converter "+
+ "does not support converting a module that is enabled for both Python 2 and 3 at the "+
+ "same time.", p.Name())
+ }
+
+ attrs := &bazelPythonBinaryAttributes{
+ Main: nil,
+ Srcs: baseAttrs.Srcs,
+ Deps: baseAttrs.Deps,
+ Python_version: pyVersion,
+ Imports: baseAttrs.Imports,
+ }
+
+ // main is optional.
+ if p.binaryProperties.Main != nil {
+ main := android.BazelLabelForModuleSrcSingle(ctx, *p.binaryProperties.Main)
+ attrs.Main = &main
+ }
+ return attrs, baseAttrs.Data
+}
+
+func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ attrs, data := p.bp2buildBinaryProperties(ctx)
+
+ props := bazel.BazelTargetModuleProperties{
+ // Use the native py_binary rule.
+ Rule_class: "py_binary",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: p.Name(),
+ Data: data,
+ }, attrs)
+}
+
+func (p *PythonTestModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ // Python tests are currently exactly the same as binaries, but with a different module type
+ attrs, data := p.bp2buildBinaryProperties(ctx)
+
+ props := bazel.BazelTargetModuleProperties{
+ // Use the native py_binary rule.
+ Rule_class: "py_test",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: p.Name(),
+ Data: data,
+ }, attrs)
+}
diff --git a/python/builder.go b/python/builder.go
index 7d7239c..1066493 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -20,7 +20,6 @@
"strings"
"android/soong/android"
-
"github.com/google/blueprint"
_ "github.com/google/blueprint/bootstrap"
)
@@ -44,13 +43,15 @@
hostPar = pctx.AndroidStaticRule("hostPar",
blueprint.RuleParams{
- Command: `sed -e 's/%interpreter%/$interp/g' -e 's/%main%/$main/g' $template > $stub && ` +
+ Command: `sed -e 's/%interpreter%/$interp/g' -e 's/%main%/__soong_entrypoint_redirector__.py/g' build/soong/python/scripts/stub_template_host.txt > $out.main && ` +
+ "sed -e 's/ENTRY_POINT/$main/g' build/soong/python/scripts/main_non_embedded.py >`dirname $out`/__soong_entrypoint_redirector__.py && " +
+ "$parCmd -o $out.entrypoint_zip -C `dirname $out` -f `dirname $out`/__soong_entrypoint_redirector__.py && " +
`echo "#!/usr/bin/env $interp" >${out}.prefix &&` +
- `$mergeParCmd -p --prefix ${out}.prefix -pm $stub $out $srcsZips && ` +
- `chmod +x $out && (rm -f $stub; rm -f ${out}.prefix)`,
- CommandDeps: []string{"$mergeParCmd"},
+ `$mergeParCmd -p --prefix ${out}.prefix -pm $out.main $out $srcsZips $out.entrypoint_zip && ` +
+ "chmod +x $out && (rm -f $out.main; rm -f ${out}.prefix; rm -f $out.entrypoint_zip; rm -f `dirname $out`/__soong_entrypoint_redirector__.py)",
+ CommandDeps: []string{"$mergeParCmd", "$parCmd", "build/soong/python/scripts/stub_template_host.txt", "build/soong/python/scripts/main_non_embedded.py"},
},
- "interp", "main", "template", "stub", "srcsZips")
+ "interp", "main", "srcsZips")
embeddedPar = pctx.AndroidStaticRule("embeddedPar",
blueprint.RuleParams{
@@ -58,7 +59,7 @@
`sed 's/ENTRY_POINT/$main/' build/soong/python/scripts/main.py >$out.main &&` +
`$mergeParCmd -p -pm $out.main --prefix $launcher $out $srcsZips && ` +
`chmod +x $out && rm -rf $out.main`,
- CommandDeps: []string{"$mergeParCmd", "$parCmd", "build/soong/python/scripts/main.py"},
+ CommandDeps: []string{"$mergeParCmd", "build/soong/python/scripts/main.py"},
},
"main", "srcsZips", "launcher")
@@ -69,6 +70,17 @@
CommandDeps: []string{"$mergeParCmd"},
},
"srcsZips", "launcher")
+
+ precompile = pctx.AndroidStaticRule("precompilePython", blueprint.RuleParams{
+ Command: `LD_LIBRARY_PATH="$ldLibraryPath" ` +
+ `PYTHONPATH=$stdlibZip/internal/stdlib ` +
+ `$launcher build/soong/python/scripts/precompile_python.py $in $out`,
+ CommandDeps: []string{
+ "$stdlibZip",
+ "$launcher",
+ "build/soong/python/scripts/precompile_python.py",
+ },
+ }, "stdlibZip", "launcher", "ldLibraryPath")
)
func init() {
@@ -90,13 +102,6 @@
implicits := srcsZips
if !embeddedLauncher {
- // the path of stub_template_host.txt from source tree.
- template := android.PathForSource(ctx, StubTemplateHost)
- implicits = append(implicits, template)
-
- // intermediate output path for __main__.py
- stub := android.PathForModuleOut(ctx, mainFileName).String()
-
ctx.Build(pctx, android.BuildParams{
Rule: hostPar,
Description: "host python archive",
@@ -104,9 +109,7 @@
Implicits: implicits,
Args: map[string]string{
"interp": strings.Replace(interpreter, "/", `\/`, -1),
- "main": strings.Replace(main, "/", `\/`, -1),
- "template": template.String(),
- "stub": stub,
+ "main": strings.Replace(strings.TrimSuffix(main, pyExt), "/", ".", -1),
"srcsZips": strings.Join(srcsZips.Strings(), " "),
},
})
diff --git a/python/defaults.go b/python/defaults.go
index dba23a7..3dc5bc4 100644
--- a/python/defaults.go
+++ b/python/defaults.go
@@ -19,7 +19,7 @@
)
func init() {
- android.RegisterModuleType("python_defaults", defaultsFactory)
+ android.RegisterModuleType("python_defaults", DefaultsFactory)
}
type Defaults struct {
@@ -30,16 +30,13 @@
func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
-func defaultsFactory() android.Module {
- return DefaultsFactory()
-}
-
-func DefaultsFactory(props ...interface{}) android.Module {
+func DefaultsFactory() android.Module {
module := &Defaults{}
- module.AddProperties(props...)
module.AddProperties(
&BaseProperties{},
+ &android.ProtoProperties{},
+ &BinaryProperties{},
)
android.InitDefaultsModule(module)
diff --git a/python/installer.go b/python/installer.go
deleted file mode 100644
index 396f036..0000000
--- a/python/installer.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package python
-
-import (
- "path/filepath"
-
- "android/soong/android"
-)
-
-// This file handles installing python executables into their final location
-
-type installLocation int
-
-const (
- InstallInData installLocation = iota
-)
-
-type pythonInstaller struct {
- dir string
- dir64 string
- relative string
-
- path android.InstallPath
-
- androidMkSharedLibs []string
-}
-
-func NewPythonInstaller(dir, dir64 string) *pythonInstaller {
- return &pythonInstaller{
- dir: dir,
- dir64: dir64,
- }
-}
-
-var _ installer = (*pythonInstaller)(nil)
-
-func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.InstallPath {
- dir := installer.dir
- if ctx.Arch().ArchType.Multilib == "lib64" && installer.dir64 != "" {
- dir = installer.dir64
- }
- if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
- dir = filepath.Join(dir, ctx.Arch().ArchType.String())
- }
- return android.PathForModuleInstall(ctx, dir, installer.relative)
-}
-
-func (installer *pythonInstaller) install(ctx android.ModuleContext, file android.Path) {
- installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file)
-}
-
-func (installer *pythonInstaller) setAndroidMkSharedLibs(sharedLibs []string) {
- installer.androidMkSharedLibs = sharedLibs
-}
diff --git a/python/library.go b/python/library.go
index d026c13..7cdb80b 100644
--- a/python/library.go
+++ b/python/library.go
@@ -18,9 +18,6 @@
import (
"android/soong/android"
- "android/soong/bazel"
-
- "github.com/google/blueprint/proptools"
)
func init() {
@@ -33,59 +30,9 @@
}
func PythonLibraryHostFactory() android.Module {
- module := newModule(android.HostSupported, android.MultilibFirst)
-
- android.InitBazelModule(module)
-
- return module.init()
-}
-
-type bazelPythonLibraryAttributes struct {
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- Srcs_version *string
-}
-
-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
- // mutator. This is sufficient for very simple python_library modules under
- // Bionic.
- py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
- py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
- var python_version *string
- if py2Enabled && !py3Enabled {
- python_version = &pyVersion2
- } else if !py2Enabled && py3Enabled {
- python_version = &pyVersion3
- } else if !py2Enabled && !py3Enabled {
- ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
- } else {
- // do nothing, since python_version defaults to PY2ANDPY3
- }
-
- baseAttrs := m.makeArchVariantBaseAttributes(ctx)
- attrs := &bazelPythonLibraryAttributes{
- Srcs: baseAttrs.Srcs,
- Deps: baseAttrs.Deps,
- Srcs_version: python_version,
- }
-
- props := bazel.BazelTargetModuleProperties{
- Rule_class: "py_library",
- Bzl_load_location: "//build/bazel/rules/python:library.bzl",
- }
-
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{
- Name: m.Name(),
- Data: baseAttrs.Data,
- }, attrs)
+ return newModule(android.HostSupported, android.MultilibFirst).init()
}
func PythonLibraryFactory() android.Module {
- module := newModule(android.HostAndDeviceSupported, android.MultilibBoth)
-
- android.InitBazelModule(module)
-
- return module.init()
+ return newModule(android.HostAndDeviceSupported, android.MultilibBoth).init()
}
diff --git a/python/proto.go b/python/proto.go
index 53ebb58..400e72c 100644
--- a/python/proto.go
+++ b/python/proto.go
@@ -18,7 +18,7 @@
"android/soong/android"
)
-func genProto(ctx android.ModuleContext, protoFile android.Path, flags android.ProtoFlags, pkgPath string) android.Path {
+func genProto(ctx android.ModuleContext, protoFile android.Path, flags android.ProtoFlags) android.Path {
srcsZipFile := android.PathForModuleGen(ctx, protoFile.Base()+".srcszip")
outDir := srcsZipFile.ReplaceExtension(ctx, "tmp")
@@ -36,9 +36,6 @@
zipCmd := rule.Command().
BuiltTool("soong_zip").
FlagWithOutput("-o ", srcsZipFile)
- if pkgPath != "" {
- zipCmd.FlagWithArg("-P ", pkgPath)
- }
zipCmd.FlagWithArg("-C ", outDir.String()).
FlagWithArg("-D ", outDir.String())
diff --git a/python/python.go b/python/python.go
index 5485108..1a12973 100644
--- a/python/python.go
+++ b/python/python.go
@@ -22,8 +22,6 @@
"regexp"
"strings"
- "android/soong/bazel"
-
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -122,25 +120,13 @@
Embedded_launcher *bool `blueprint:"mutated"`
}
-type baseAttributes struct {
- // TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
- //Pkg_path bazel.StringAttribute
- // TODO: Related to Pkg_bath and similarLy gated
- //Is_internal bazel.BoolAttribute
- // Combines Srcs and Exclude_srcs
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- // Combines Data and Java_data (invariant)
- Data bazel.LabelListAttribute
-}
-
// Used to store files of current module after expanding dependencies
type pathMapping struct {
dest string
src android.Path
}
-type Module struct {
+type PythonLibraryModule struct {
android.ModuleBase
android.DefaultableModuleBase
android.BazelModuleBase
@@ -152,16 +138,6 @@
hod android.HostOrDeviceSupported
multilib android.Multilib
- // interface used to bootstrap .par executable when embedded_launcher is true
- // this should be set by Python modules which are runnable, e.g. binaries and tests
- // bootstrapper might be nil (e.g. Python library module).
- bootstrapper bootstrapper
-
- // interface that implements functions required for installation
- // this should be set by Python modules which are runnable, e.g. binaries and tests
- // installer might be nil (e.g. Python library module).
- installer installer
-
// the Python files of current module after expanding source dependencies.
// pathMapping: <dest: runfile_path, src: source_path>
srcsPathMappings []pathMapping
@@ -170,102 +146,62 @@
// pathMapping: <dest: runfile_path, src: source_path>
dataPathMappings []pathMapping
- // the zip filepath for zipping current module source/data files.
+ // The zip file containing the current module's source/data files.
srcsZip android.Path
- // dependency modules' zip filepath for zipping current module source/data files.
- depsSrcsZips android.Paths
-
- // (.intermediate) module output path as installation source.
- installSource android.OptionalPath
-
- // Map to ensure sub-part of the AndroidMk for this module is only added once
- subAndroidMkOnce map[subAndroidMkProvider]bool
+ // The zip file containing the current module's source/data files, with the
+ // source files precompiled.
+ precompiledSrcsZip android.Path
}
// newModule generates new Python base module
-func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
- return &Module{
+func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *PythonLibraryModule {
+ return &PythonLibraryModule{
hod: hod,
multilib: multilib,
}
}
-func (m *Module) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
- var attrs baseAttributes
- archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
- for axis, configToProps := range archVariantBaseProps {
- for config, props := range configToProps {
- if baseProps, ok := props.(*BaseProperties); ok {
- attrs.Srcs.SetSelectValue(axis, config,
- android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
- attrs.Deps.SetSelectValue(axis, config,
- android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
- data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
- data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
- attrs.Data.SetSelectValue(axis, config, data)
- }
- }
- }
- return attrs
-}
-
-// bootstrapper interface should be implemented for runnable modules, e.g. binary and test
-type bootstrapper interface {
- bootstrapperProps() []interface{}
- bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
- srcsPathMappings []pathMapping, srcsZip android.Path,
- depsSrcsZips android.Paths) android.OptionalPath
-
- autorun() bool
-}
-
-// installer interface should be implemented for installable modules, e.g. binary and test
-type installer interface {
- install(ctx android.ModuleContext, path android.Path)
- setAndroidMkSharedLibs(sharedLibs []string)
-}
-
// interface implemented by Python modules to provide source and data mappings and zip to python
// modules that depend on it
type pythonDependency interface {
getSrcsPathMappings() []pathMapping
getDataPathMappings() []pathMapping
getSrcsZip() android.Path
+ getPrecompiledSrcsZip() android.Path
}
// getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination
-func (p *Module) getSrcsPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getSrcsPathMappings() []pathMapping {
return p.srcsPathMappings
}
// getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination
-func (p *Module) getDataPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getDataPathMappings() []pathMapping {
return p.dataPathMappings
}
// getSrcsZip returns the filepath where the current module's source/data files are zipped.
-func (p *Module) getSrcsZip() android.Path {
+func (p *PythonLibraryModule) getSrcsZip() android.Path {
return p.srcsZip
}
-var _ pythonDependency = (*Module)(nil)
+// getSrcsZip returns the filepath where the current module's source/data files are zipped.
+func (p *PythonLibraryModule) getPrecompiledSrcsZip() android.Path {
+ return p.precompiledSrcsZip
+}
-var _ android.AndroidMkEntriesProvider = (*Module)(nil)
+func (p *PythonLibraryModule) getBaseProperties() *BaseProperties {
+ return &p.properties
+}
-func (p *Module) init(additionalProps ...interface{}) android.Module {
+var _ pythonDependency = (*PythonLibraryModule)(nil)
+
+func (p *PythonLibraryModule) init() android.Module {
p.AddProperties(&p.properties, &p.protoProperties)
-
- // Add additional properties for bootstrapping/installation
- // This is currently tied to the bootstrapper interface;
- // however, these are a combination of properties for the installation and bootstrapping of a module
- if p.bootstrapper != nil {
- p.AddProperties(p.bootstrapper.bootstrapperProps()...)
- }
-
android.InitAndroidArchModule(p, p.hod, p.multilib)
android.InitDefaultableModule(p)
-
+ android.InitBazelModule(p)
return p
}
@@ -287,40 +223,55 @@
}
var (
- pythonLibTag = dependencyTag{name: "pythonLib"}
- javaDataTag = dependencyTag{name: "javaData"}
+ pythonLibTag = dependencyTag{name: "pythonLib"}
+ javaDataTag = dependencyTag{name: "javaData"}
+ // The python interpreter, with soong module name "py3-launcher" or "py3-launcher-autorun".
launcherTag = dependencyTag{name: "launcher"}
launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"}
- pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
- pyExt = ".py"
- protoExt = ".proto"
- pyVersion2 = "PY2"
- pyVersion3 = "PY3"
- initFileName = "__init__.py"
- mainFileName = "__main__.py"
- entryPointFile = "entry_point.txt"
- parFileExt = ".zip"
- internalPath = "internal"
+ // The python interpreter built for host so that we can precompile python sources.
+ // This only works because the precompiled sources don't vary by architecture.
+ // The soong module name is "py3-launcher".
+ hostLauncherTag = dependencyTag{name: "hostLauncher"}
+ hostlauncherSharedLibTag = dependencyTag{name: "hostlauncherSharedLib"}
+ hostStdLibTag = dependencyTag{name: "hostStdLib"}
+ pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
+ pyExt = ".py"
+ protoExt = ".proto"
+ pyVersion2 = "PY2"
+ pyVersion3 = "PY3"
+ pyVersion2And3 = "PY2ANDPY3"
+ internalPath = "internal"
)
+type basePropertiesProvider interface {
+ getBaseProperties() *BaseProperties
+}
+
// versionSplitMutator creates version variants for modules and appends the version-specific
// properties for a given variant to the properties in the variant module
func versionSplitMutator() func(android.BottomUpMutatorContext) {
return func(mctx android.BottomUpMutatorContext) {
- if base, ok := mctx.Module().(*Module); ok {
- versionNames := []string{}
+ if base, ok := mctx.Module().(basePropertiesProvider); ok {
+ props := base.getBaseProperties()
+ var versionNames []string
// collect version specific properties, so that we can merge version-specific properties
// into the module's overall properties
- versionProps := []VersionProperties{}
+ var versionProps []VersionProperties
// PY3 is first so that we alias the PY3 variant rather than PY2 if both
// are available
- if proptools.BoolDefault(base.properties.Version.Py3.Enabled, true) {
+ if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
versionNames = append(versionNames, pyVersion3)
- versionProps = append(versionProps, base.properties.Version.Py3)
+ versionProps = append(versionProps, props.Version.Py3)
}
- if proptools.BoolDefault(base.properties.Version.Py2.Enabled, false) {
+ if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
+ if !mctx.DeviceConfig().BuildBrokenUsesSoongPython2Modules() &&
+ mctx.ModuleName() != "par_test" &&
+ mctx.ModuleName() != "py2-cmd" &&
+ mctx.ModuleName() != "py2-stdlib" {
+ mctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3. This error can be temporarily overridden by setting BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES := true in the product configuration")
+ }
versionNames = append(versionNames, pyVersion2)
- versionProps = append(versionProps, base.properties.Version.Py2)
+ versionProps = append(versionProps, props.Version.Py2)
}
modules := mctx.CreateLocalVariations(versionNames...)
// Alias module to the first variant
@@ -329,9 +280,10 @@
}
for i, v := range versionNames {
// set the actual version for Python module.
- modules[i].(*Module).properties.Actual_version = v
+ newProps := modules[i].(basePropertiesProvider).getBaseProperties()
+ newProps.Actual_version = v
// append versioned properties for the Python module to the overall properties
- err := proptools.AppendMatchingProperties([]interface{}{&modules[i].(*Module).properties}, &versionProps[i], nil)
+ err := proptools.AppendMatchingProperties([]interface{}{newProps}, &versionProps[i], nil)
if err != nil {
panic(err)
}
@@ -340,38 +292,6 @@
}
}
-// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
-// fulfilling HostToolProvider interface.
-func (p *Module) HostToolPath() android.OptionalPath {
- if p.installer != nil {
- if bin, ok := p.installer.(*binaryDecorator); ok {
- // TODO: This should only be set when building host binaries -- tests built for device would be
- // setting this incorrectly.
- return android.OptionalPathForPath(bin.path)
- }
- }
-
- return android.OptionalPath{}
-
-}
-
-// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
-func (p *Module) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- if outputFile := p.installSource; outputFile.Valid() {
- return android.Paths{outputFile.Path()}, nil
- }
- return android.Paths{}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (p *Module) isEmbeddedLauncherEnabled() bool {
- return p.installer != nil && Bool(p.properties.Embedded_launcher)
-}
-
func anyHasExt(paths []string, ext string) bool {
for _, p := range paths {
if filepath.Ext(p) == ext {
@@ -382,7 +302,7 @@
return false
}
-func (p *Module) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
+func (p *PythonLibraryModule) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
return anyHasExt(p.properties.Srcs, ext)
}
@@ -390,7 +310,7 @@
// - handles proto dependencies,
// - if required, specifies launcher and adds launcher dependencies,
// - applies python version mutations to Python dependencies
-func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (p *PythonLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
android.ProtoDeps(ctx, &p.protoProperties)
versionVariation := []blueprint.Variation{
@@ -405,111 +325,85 @@
// Add python library dependencies for this python version variation
ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
- // If this module will be installed and has an embedded launcher, we need to add dependencies for:
- // * standard library
- // * launcher
- // * shared dependencies of the launcher
- if p.installer != nil && p.isEmbeddedLauncherEnabled() {
- var stdLib string
- var launcherModule string
- // Add launcher shared lib dependencies. Ideally, these should be
- // derived from the `shared_libs` property of the launcher. However, we
- // cannot read the property at this stage and it will be too late to add
- // dependencies later.
- launcherSharedLibDeps := []string{
- "libsqlite",
- }
- // Add launcher-specific dependencies for bionic
- 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:
- stdLib = "py2-stdlib"
-
- launcherModule = "py2-launcher"
- if p.bootstrapper.autorun() {
- launcherModule = "py2-launcher-autorun"
- }
-
- launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
-
- case pyVersion3:
- stdLib = "py3-stdlib"
-
- launcherModule = "py3-launcher"
- 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")
- }
- default:
- panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
- p.properties.Actual_version, ctx.ModuleName()))
- }
- ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib)
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...)
- }
-
// Emulate the data property for java_data but with the arch variation overridden to "common"
// so that it can point to java modules.
javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}}
ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...)
+
+ p.AddDepsOnPythonLauncherAndStdlib(ctx, hostStdLibTag, hostLauncherTag, hostlauncherSharedLibTag, false, ctx.Config().BuildOSTarget)
}
-func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.generatePythonBuildActions(ctx)
-
- // Only Python binary and test modules have non-empty bootstrapper.
- if p.bootstrapper != nil {
- // if the module is being installed, we need to collect all transitive dependencies to embed in
- // the final par
- p.collectPathsFromTransitiveDeps(ctx)
- // bootstrap the module, including resolving main file, getting launcher path, and
- // registering actions to build the par file
- // bootstrap returns the binary output path
- p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
- p.isEmbeddedLauncherEnabled(), p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
+// AddDepsOnPythonLauncherAndStdlib will make the current module depend on the python stdlib,
+// launcher (interpreter), and the launcher's shared libraries. If autorun is true, it will use
+// the autorun launcher instead of the regular one. This function acceps a targetForDeps argument
+// as the target to use for these dependencies. For embedded launcher python binaries, the launcher
+// that will be embedded will be under the same target as the python module itself. But when
+// precompiling python code, we need to get the python launcher built for host, even if we're
+// compiling the python module for device, so we pass a different target to this function.
+func (p *PythonLibraryModule) AddDepsOnPythonLauncherAndStdlib(ctx android.BottomUpMutatorContext,
+ stdLibTag, launcherTag, launcherSharedLibTag blueprint.DependencyTag,
+ autorun bool, targetForDeps android.Target) {
+ var stdLib string
+ var launcherModule string
+ // Add launcher shared lib dependencies. Ideally, these should be
+ // derived from the `shared_libs` property of the launcher. TODO: read these from
+ // the python launcher itself using ctx.OtherModuleProvider() or similar on the result
+ // of ctx.AddFarVariationDependencies()
+ launcherSharedLibDeps := []string{
+ "libsqlite",
+ }
+ // Add launcher-specific dependencies for bionic
+ if targetForDeps.Os.Bionic() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
+ }
+ if targetForDeps.Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
}
- // Only Python binary and test modules have non-empty installer.
- if p.installer != nil {
- var sharedLibs []string
- // if embedded launcher is enabled, we need to collect the shared library depenendencies of the
- // launcher
- for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
- sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+ switch p.properties.Actual_version {
+ case pyVersion2:
+ stdLib = "py2-stdlib"
+
+ launcherModule = "py2-launcher"
+ if autorun {
+ launcherModule = "py2-launcher-autorun"
}
- p.installer.setAndroidMkSharedLibs(sharedLibs)
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
+ case pyVersion3:
+ stdLib = "py3-stdlib"
- // Install the par file from installSource
- if p.installSource.Valid() {
- p.installer.install(ctx, p.installSource.Path())
+ launcherModule = "py3-launcher"
+ if autorun {
+ launcherModule = "py3-launcher-autorun"
}
+ if ctx.Config().HostStaticBinaries() && targetForDeps.Os == android.LinuxMusl {
+ launcherModule += "-static"
+ }
+ if ctx.Device() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
+ }
+ default:
+ panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
+ p.properties.Actual_version, ctx.ModuleName()))
}
+ targetVariations := targetForDeps.Variations()
+ if ctx.ModuleName() != stdLib {
+ stdLibVariations := make([]blueprint.Variation, 0, len(targetVariations)+1)
+ stdLibVariations = append(stdLibVariations, blueprint.Variation{Mutator: "python_version", Variation: p.properties.Actual_version})
+ stdLibVariations = append(stdLibVariations, targetVariations...)
+ // Using AddFarVariationDependencies for all of these because they can be for a different
+ // platform, like if the python module itself was being compiled for device, we may want
+ // the python interpreter built for host so that we can precompile python sources.
+ ctx.AddFarVariationDependencies(stdLibVariations, stdLibTag, stdLib)
+ }
+ ctx.AddFarVariationDependencies(targetVariations, launcherTag, launcherModule)
+ ctx.AddFarVariationDependencies(targetVariations, launcherSharedLibTag, launcherSharedLibDeps...)
}
-// generatePythonBuildActions performs build actions common to all Python modules
-func (p *Module) generatePythonBuildActions(ctx android.ModuleContext) {
+// GenerateAndroidBuildActions performs build actions common to all Python modules
+func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
- requiresSrcs := true
- if p.bootstrapper != nil && !p.bootstrapper.autorun() {
- requiresSrcs = false
- }
- if len(expandedSrcs) == 0 && requiresSrcs {
- ctx.ModuleErrorf("doesn't have any source files!")
- }
// expand data files from "data" property.
expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
@@ -542,6 +436,7 @@
// generate the zipfile of all source and data files
p.srcsZip = p.createSrcsZip(ctx, pkgPath)
+ p.precompiledSrcsZip = p.precompileSrcs(ctx)
}
func isValidPythonPath(path string) error {
@@ -560,7 +455,7 @@
// For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path>
// for python/data files expanded from properties.
-func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
+func (p *PythonLibraryModule) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
expandedSrcs, expandedData android.Paths) {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
// check current module duplicates.
@@ -595,43 +490,59 @@
}
// createSrcsZip registers build actions to zip current module's sources and data.
-func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
+func (p *PythonLibraryModule) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
relativeRootMap := make(map[string]android.Paths)
- pathMappings := append(p.srcsPathMappings, p.dataPathMappings...)
-
var protoSrcs android.Paths
- // "srcs" or "data" properties may contain filegroup so it might happen that
- // the root directory for each source path is different.
- for _, path := range pathMappings {
+ addPathMapping := func(path pathMapping) {
// handle proto sources separately
if path.src.Ext() == protoExt {
protoSrcs = append(protoSrcs, path.src)
} else {
- var relativeRoot string
- relativeRoot = strings.TrimSuffix(path.src.String(), path.src.Rel())
- if v, found := relativeRootMap[relativeRoot]; found {
- relativeRootMap[relativeRoot] = append(v, path.src)
- } else {
- relativeRootMap[relativeRoot] = android.Paths{path.src}
- }
+ relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
+ relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
}
}
+
+ // "srcs" or "data" properties may contain filegroups so it might happen that
+ // the root directory for each source path is different.
+ for _, path := range p.srcsPathMappings {
+ addPathMapping(path)
+ }
+ for _, path := range p.dataPathMappings {
+ addPathMapping(path)
+ }
+
var zips android.Paths
if len(protoSrcs) > 0 {
protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
protoFlags.OutTypeFlag = "--python_out"
+ if pkgPath != "" {
+ pkgPathStagingDir := android.PathForModuleGen(ctx, "protos_staged_for_pkg_path")
+ rule := android.NewRuleBuilder(pctx, ctx)
+ var stagedProtoSrcs android.Paths
+ for _, srcFile := range protoSrcs {
+ stagedProtoSrc := pkgPathStagingDir.Join(ctx, pkgPath, srcFile.Rel())
+ rule.Command().Text("mkdir -p").Flag(filepath.Base(stagedProtoSrc.String()))
+ rule.Command().Text("cp -f").Input(srcFile).Output(stagedProtoSrc)
+ stagedProtoSrcs = append(stagedProtoSrcs, stagedProtoSrc)
+ }
+ rule.Build("stage_protos_for_pkg_path", "Stage protos for pkg_path")
+ protoSrcs = stagedProtoSrcs
+ }
+
for _, srcFile := range protoSrcs {
- zip := genProto(ctx, srcFile, protoFlags, pkgPath)
+ zip := genProto(ctx, srcFile, protoFlags)
zips = append(zips, zip)
}
}
if len(relativeRootMap) > 0 {
// in order to keep stable order of soong_zip params, we sort the keys here.
- roots := android.SortedStringKeys(relativeRootMap)
+ roots := android.SortedKeys(relativeRootMap)
- parArgs := []string{}
+ // Use -symlinks=false so that the symlinks in the bazel output directory are followed
+ parArgs := []string{"-symlinks=false"}
if pkgPath != "" {
// use package path as path prefix
parArgs = append(parArgs, `-P `+pkgPath)
@@ -674,30 +585,79 @@
}
}
-// isPythonLibModule returns whether the given module is a Python library Module or not
+func (p *PythonLibraryModule) precompileSrcs(ctx android.ModuleContext) android.Path {
+ // To precompile the python sources, we need a python interpreter and stdlib built
+ // for host. We then use those to compile the python sources, which may be used on either
+ // host of device. Python bytecode is architecture agnostic, so we're essentially
+ // "cross compiling" for device here purely by virtue of host and device python bytecode
+ // being the same.
+ var stdLib android.Path
+ var launcher android.Path
+ if ctx.ModuleName() == "py3-stdlib" || ctx.ModuleName() == "py2-stdlib" {
+ stdLib = p.srcsZip
+ } else {
+ ctx.VisitDirectDepsWithTag(hostStdLibTag, func(module android.Module) {
+ if dep, ok := module.(pythonDependency); ok {
+ stdLib = dep.getPrecompiledSrcsZip()
+ }
+ })
+ }
+ ctx.VisitDirectDepsWithTag(hostLauncherTag, func(module android.Module) {
+ if dep, ok := module.(IntermPathProvider); ok {
+ optionalLauncher := dep.IntermPathForModuleOut()
+ if optionalLauncher.Valid() {
+ launcher = optionalLauncher.Path()
+ }
+ }
+ })
+ var launcherSharedLibs android.Paths
+ var ldLibraryPath []string
+ ctx.VisitDirectDepsWithTag(hostlauncherSharedLibTag, func(module android.Module) {
+ if dep, ok := module.(IntermPathProvider); ok {
+ optionalPath := dep.IntermPathForModuleOut()
+ if optionalPath.Valid() {
+ launcherSharedLibs = append(launcherSharedLibs, optionalPath.Path())
+ ldLibraryPath = append(ldLibraryPath, filepath.Dir(optionalPath.Path().String()))
+ }
+ }
+ })
+
+ out := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszipprecompiled")
+ if stdLib == nil || launcher == nil {
+ // This shouldn't happen in a real build because we'll error out when adding dependencies
+ // on the stdlib and launcher if they don't exist. But some tests set
+ // AllowMissingDependencies.
+ return out
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: precompile,
+ Input: p.srcsZip,
+ Output: out,
+ Implicits: launcherSharedLibs,
+ Description: "Precompile the python sources of " + ctx.ModuleName(),
+ Args: map[string]string{
+ "stdlibZip": stdLib.String(),
+ "launcher": launcher.String(),
+ "ldLibraryPath": strings.Join(ldLibraryPath, ":"),
+ },
+ })
+ return out
+}
+
+// isPythonLibModule returns whether the given module is a Python library PythonLibraryModule or not
func isPythonLibModule(module blueprint.Module) bool {
- if m, ok := module.(*Module); ok {
- return m.isLibrary()
+ if _, ok := module.(*PythonLibraryModule); ok {
+ if _, ok := module.(*PythonBinaryModule); !ok {
+ return true
+ }
}
return false
}
-// This is distinguished by the fact that Python libraries are not installable, while other Python
-// modules are.
-func (p *Module) isLibrary() bool {
- // Python library has no bootstrapper or installer
- return p.bootstrapper == nil && p.installer == nil
-}
-
-func (p *Module) isBinary() bool {
- _, ok := p.bootstrapper.(*binaryDecorator)
- return ok
-}
-
// collectPathsFromTransitiveDeps checks for source/data files for duplicate paths
// for module and its transitive dependencies and collects list of data/source file
// zips for transitive dependencies.
-func (p *Module) collectPathsFromTransitiveDeps(ctx android.ModuleContext) {
+func (p *PythonLibraryModule) collectPathsFromTransitiveDeps(ctx android.ModuleContext, precompiled bool) android.Paths {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
// check duplicates.
destToPySrcs := make(map[string]string)
@@ -711,6 +671,8 @@
seen := make(map[android.Module]bool)
+ var result android.Paths
+
// visit all its dependencies in depth first.
ctx.WalkDeps(func(child, parent android.Module) bool {
// we only collect dependencies tagged as python library deps
@@ -739,10 +701,15 @@
checkForDuplicateOutputPath(ctx, destToPyData,
path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
}
- p.depsSrcsZips = append(p.depsSrcsZips, dep.getSrcsZip())
+ if precompiled {
+ result = append(result, dep.getPrecompiledSrcsZip())
+ } else {
+ result = append(result, dep.getSrcsZip())
+ }
}
return true
})
+ return result
}
// chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which
@@ -763,18 +730,10 @@
}
// InstallInData returns true as Python is not supported in the system partition
-func (p *Module) InstallInData() bool {
+func (p *PythonLibraryModule) InstallInData() bool {
return true
}
-func (p *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- if p.isLibrary() {
- pythonLibBp2Build(ctx, p)
- } else if p.isBinary() {
- pythonBinaryBp2Build(ctx, p)
- }
-}
-
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var String = proptools.String
diff --git a/python/python_test.go b/python/python_test.go
index f57f504..75a6a89 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -18,10 +18,10 @@
"fmt"
"os"
"path/filepath"
- "regexp"
"testing"
"android/soong/android"
+ "android/soong/cc"
)
type pyModule struct {
@@ -33,8 +33,10 @@
}
var (
- buildNamePrefix = "soong_python_test"
- moduleVariantErrTemplate = "%s: module %q variant %q: "
+ buildNamePrefix = "soong_python_test"
+ // We allow maching almost anything before the actual variant so that the os/arch variant
+ // is matched.
+ moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*%s": `
pkgPathErrTemplate = moduleVariantErrTemplate +
"pkg_path: %q must be a relative path contained in par file."
badIdentifierErrTemplate = moduleVariantErrTemplate +
@@ -300,8 +302,6 @@
filepath.Join("dir", "file2.py"): nil,
filepath.Join("dir", "bin.py"): nil,
filepath.Join("dir", "file4.py"): nil,
- StubTemplateHost: []byte(`PYTHON_BINARY = '%interpreter%'
- MAIN_FILE = '%main%'`),
},
expectedBinaries: []pyModule{
{
@@ -314,10 +314,6 @@
"e/file4.py",
},
srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip",
- depsSrcsZips: []string{
- "out/soong/.intermediates/dir/lib5/PY3/lib5.py.srcszip",
- "out/soong/.intermediates/dir/lib6/PY3/lib6.py.srcszip",
- },
},
},
},
@@ -329,17 +325,26 @@
if d.desc != "module with duplicate runfile path" {
continue
}
- errorPatterns := make([]string, len(d.errors))
- for i, s := range d.errors {
- errorPatterns[i] = regexp.QuoteMeta(s)
- }
+ d.mockFiles[filepath.Join("common", bpFile)] = []byte(`
+python_library {
+ name: "py3-stdlib",
+ host_supported: true,
+}
+cc_binary {
+ name: "py3-launcher",
+ host_supported: true,
+}
+`)
t.Run(d.desc, func(t *testing.T) {
result := android.GroupFixturePreparers(
android.PrepareForTestWithDefaults,
+ android.PrepareForTestWithArchMutator,
+ android.PrepareForTestWithAllowMissingDependencies,
+ cc.PrepareForTestWithCcDefaultModules,
PrepareForTestWithPythonBuildComponents,
d.mockFiles.AddToFixture(),
- ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)).
RunTest(t)
if len(result.Errs) > 0 {
@@ -348,17 +353,17 @@
for _, e := range d.expectedBinaries {
t.Run(e.name, func(t *testing.T) {
- expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles, e.depsSrcsZips)
+ expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles)
})
}
})
}
}
-func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles, expectedDepsSrcsZips []string) {
+func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
module := ctx.ModuleForTests(name, variant)
- base, baseOk := module.Module().(*Module)
+ base, baseOk := module.Module().(*PythonLibraryModule)
if !baseOk {
t.Fatalf("%s is not Python module!", name)
}
@@ -371,8 +376,6 @@
android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
-
- android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips)
}
func TestMain(m *testing.M) {
diff --git a/python/scripts/main_non_embedded.py b/python/scripts/main_non_embedded.py
new file mode 100644
index 0000000..ffbaaa8
--- /dev/null
+++ b/python/scripts/main_non_embedded.py
@@ -0,0 +1,6 @@
+import runpy
+
+# The purpose of this file is to implement python 3.11+'s
+# PYTHON_SAFE_PATH / -P option on older python versions.
+
+runpy._run_module_as_main("ENTRY_POINT", alter_argv=False)
diff --git a/python/scripts/precompile_python.py b/python/scripts/precompile_python.py
new file mode 100644
index 0000000..e12e7d2
--- /dev/null
+++ b/python/scripts/precompile_python.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# Copyright 2023 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.
+
+import argparse
+import py_compile
+import os
+import shutil
+import tempfile
+import zipfile
+
+# This file needs to support both python 2 and 3.
+
+
+def process_one_file(name, inf, outzip):
+ if not name.endswith('.py'):
+ outzip.writestr(name, inf.read())
+ return
+
+ # Unfortunately py_compile requires the input/output files to be written
+ # out to disk.
+ with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+ shutil.copyfileobj(inf, tmp)
+ in_name = tmp.name
+ with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+ out_name = tmp.name
+ try:
+ py_compile.compile(in_name, out_name, name, doraise=True)
+ with open(out_name, 'rb') as f:
+ outzip.writestr(name + 'c', f.read())
+ finally:
+ os.remove(in_name)
+ os.remove(out_name)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('src_zip')
+ parser.add_argument('dst_zip')
+ args = parser.parse_args()
+
+ with open(args.dst_zip, 'wb') as outf, open(args.src_zip, 'rb') as inf:
+ with zipfile.ZipFile(outf, mode='w') as outzip, zipfile.ZipFile(inf, mode='r') as inzip:
+ for name in inzip.namelist():
+ with inzip.open(name, mode='r') as inzipf:
+ process_one_file(name, inzipf, outzip)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt
index 138404b..2d1bd4a 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -1,9 +1,9 @@
#!/usr/bin/env '%interpreter%'
import os
-import re
import tempfile
import shutil
+import signal
import sys
import subprocess
import zipfile
@@ -15,56 +15,22 @@
# Don't imply 'import site' on initialization
PYTHON_ARG = '-S'
-def SearchPathEnv(name):
- search_path = os.getenv('PATH', os.defpath).split(os.pathsep)
- for directory in search_path:
- if directory == '': continue
- path = os.path.join(directory, name)
- # Check if path is actual executable file.
- if os.path.isfile(path) and os.access(path, os.X_OK):
- return path
- return None
-
-def FindPythonBinary():
- if PYTHON_BINARY.startswith('/'):
- # Case 1: Python interpreter is directly provided with absolute path.
- return PYTHON_BINARY
- else:
- # Case 2: Find Python interpreter through environment variable: PATH.
- return SearchPathEnv(PYTHON_BINARY)
-
-# Create the runfiles tree by extracting the zip file
-def ExtractRunfiles():
- temp_dir = tempfile.mkdtemp("", "Soong.python_")
- zf = zipfile.ZipFile(os.path.dirname(__file__))
- zf.extractall(temp_dir)
- return temp_dir
-
def Main():
args = sys.argv[1:]
- new_env = {}
- runfiles_path = None
-
+ runfiles_path = tempfile.mkdtemp(prefix="Soong.python_")
try:
- runfiles_path = ExtractRunfiles()
+ zf = zipfile.ZipFile(os.path.dirname(__file__))
+ zf.extractall(runfiles_path)
+ zf.close()
- # Add runfiles path to PYTHONPATH.
- python_path_entries = [runfiles_path]
-
- # Add top dirs within runfiles path to PYTHONPATH.
- top_entries = [os.path.join(runfiles_path, i) for i in os.listdir(runfiles_path)]
- top_pkg_dirs = [i for i in top_entries if os.path.isdir(i)]
- python_path_entries += top_pkg_dirs
-
+ new_python_path = runfiles_path
old_python_path = os.environ.get(PYTHON_PATH)
- separator = ':'
- new_python_path = separator.join(python_path_entries)
- # Copy old PYTHONPATH.
if old_python_path:
- new_python_path += separator + old_python_path
- new_env[PYTHON_PATH] = new_python_path
+ os.environ.update({PYTHON_PATH: new_python_path + ":" + old_python_path})
+ else:
+ os.environ.update({PYTHON_PATH: new_python_path})
# Now look for main python source file.
main_filepath = os.path.join(runfiles_path, MAIN_FILE)
@@ -73,21 +39,25 @@
assert os.access(main_filepath, os.R_OK), \
'Cannot exec() %r: file not readable.' % main_filepath
- python_program = FindPythonBinary()
- if python_program is None:
- raise AssertionError('Could not find python binary: ' + PYTHON_BINARY)
- args = [python_program, PYTHON_ARG, main_filepath] + args
-
- os.environ.update(new_env)
+ args = [PYTHON_BINARY, PYTHON_ARG, main_filepath] + args
sys.stdout.flush()
- retCode = subprocess.call(args)
- sys.exit(retCode)
- except:
- raise
+ # close_fds=False so that you can run binaries with files provided on the command line:
+ # my_python_app --file <(echo foo)
+ p = subprocess.Popen(args, close_fds=False)
+
+ def handler(sig, frame):
+ p.send_signal(sig)
+
+ # Redirect SIGINT and SIGTERM to subprocess
+ signal.signal(signal.SIGINT, handler)
+ signal.signal(signal.SIGTERM, handler)
+
+ p.wait()
+
+ sys.exit(p.returncode)
finally:
- if runfiles_path is not None:
- shutil.rmtree(runfiles_path, True)
+ shutil.rmtree(runfiles_path, ignore_errors=True)
if __name__ == '__main__':
Main()
diff --git a/python/test.go b/python/test.go
index 7413782..31da17e 100644
--- a/python/test.go
+++ b/python/test.go
@@ -15,6 +15,8 @@
package python
import (
+ "fmt"
+
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -32,10 +34,18 @@
ctx.RegisterModuleType("python_test", PythonTestFactory)
}
-// Test option struct.
-type TestOptions struct {
- // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
- Unit_test *bool
+func NewTest(hod android.HostOrDeviceSupported) *PythonTestModule {
+ return &PythonTestModule{PythonBinaryModule: *NewBinary(hod)}
+}
+
+func PythonTestHostFactory() android.Module {
+ return NewTest(android.HostSupportedNoCross).init()
+}
+
+func PythonTestFactory() android.Module {
+ module := NewTest(android.HostAndDeviceSupported)
+ module.multilib = android.MultilibBoth
+ return module.init()
}
type TestProperties struct {
@@ -58,71 +68,121 @@
Test_options TestOptions
}
-type testDecorator struct {
- *binaryDecorator
+type TestOptions struct {
+ android.CommonTestOptions
+
+ // Runner for the test. Supports "tradefed" and "mobly" (for multi-device tests). Default is "tradefed".
+ Runner *string
+
+ // Metadata to describe the test configuration.
+ Metadata []Metadata
+}
+
+type Metadata struct {
+ Name string
+ Value string
+}
+
+type PythonTestModule struct {
+ PythonBinaryModule
testProperties TestProperties
-
- testConfig android.Path
-
- data []android.DataPath
+ testConfig android.Path
+ data []android.DataPath
}
-func (test *testDecorator) bootstrapperProps() []interface{} {
- return append(test.binaryDecorator.bootstrapperProps(), &test.testProperties)
+func (p *PythonTestModule) init() android.Module {
+ p.AddProperties(&p.properties, &p.protoProperties)
+ p.AddProperties(&p.binaryProperties)
+ p.AddProperties(&p.testProperties)
+ android.InitAndroidArchModule(p, p.hod, p.multilib)
+ android.InitDefaultableModule(p)
+ android.InitBazelModule(p)
+ if p.hod == android.HostSupportedNoCross && p.testProperties.Test_options.Unit_test == nil {
+ p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+ }
+ return p
}
-func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
- test.testConfig = tradefed.AutoGenPythonBinaryHostTestConfig(ctx, test.testProperties.Test_config,
- test.testProperties.Test_config_template, test.binaryDecorator.binaryProperties.Test_suites,
- test.binaryDecorator.binaryProperties.Auto_gen_config)
+func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // We inherit from only the library's GenerateAndroidBuildActions, and then
+ // just use buildBinary() so that the binary is not installed into the location
+ // it would be for regular binaries.
+ p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+ p.buildBinary(ctx)
- test.binaryDecorator.pythonInstaller.dir = "nativetest"
- test.binaryDecorator.pythonInstaller.dir64 = "nativetest64"
+ var configs []tradefed.Option
+ for _, metadata := range p.testProperties.Test_options.Metadata {
+ configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: metadata.Name, Value: metadata.Value})
+ }
- test.binaryDecorator.pythonInstaller.relative = ctx.ModuleName()
+ runner := proptools.StringDefault(p.testProperties.Test_options.Runner, "tradefed")
+ if runner == "tradefed" {
+ p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: p.testProperties.Test_config,
+ TestConfigTemplateProp: p.testProperties.Test_config_template,
+ TestSuites: p.binaryProperties.Test_suites,
+ OptionsForAutogenerated: configs,
+ AutoGenConfig: p.binaryProperties.Auto_gen_config,
+ DeviceTemplate: "${PythonBinaryHostTestConfigTemplate}",
+ HostTemplate: "${PythonBinaryHostTestConfigTemplate}",
+ })
+ } else if runner == "mobly" {
+ if p.testProperties.Test_config != nil || p.testProperties.Test_config_template != nil || p.binaryProperties.Auto_gen_config != nil {
+ panic(fmt.Errorf("cannot set test_config, test_config_template or auto_gen_config for mobly test"))
+ }
- test.binaryDecorator.pythonInstaller.install(ctx, file)
+ for _, testSuite := range p.binaryProperties.Test_suites {
+ if testSuite == "cts" {
+ configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: "cts"})
+ break
+ }
+ }
+ p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ OptionsForAutogenerated: configs,
+ DeviceTemplate: "${PythonBinaryHostMoblyTestConfigTemplate}",
+ HostTemplate: "${PythonBinaryHostMoblyTestConfigTemplate}",
+ })
+ } else {
+ panic(fmt.Errorf("unknown python test runner '%s', should be 'tradefed' or 'mobly'", runner))
+ }
- dataSrcPaths := android.PathsForModuleSrc(ctx, test.testProperties.Data)
+ p.installedDest = ctx.InstallFile(installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName()), p.installSource.Base(), p.installSource)
- for _, dataSrcPath := range dataSrcPaths {
- test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
+ for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) {
+ p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
}
// Emulate the data property for java_data dependencies.
for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
- test.data = append(test.data, android.DataPath{SrcPath: javaDataSrcPath})
+ p.data = append(p.data, android.DataPath{SrcPath: javaDataSrcPath})
}
}
}
-func NewTest(hod android.HostOrDeviceSupported) *Module {
- module, binary := NewBinary(hod)
-
- binary.pythonInstaller = NewPythonInstaller("nativetest", "nativetest64")
-
- test := &testDecorator{binaryDecorator: binary}
- if hod == android.HostSupportedNoCross && test.testProperties.Test_options.Unit_test == nil {
- test.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries {
+ entriesList := p.PythonBinaryModule.AndroidMkEntries()
+ if len(entriesList) != 1 {
+ panic("Expected 1 entry")
}
+ entries := &entriesList[0]
- module.bootstrapper = test
- module.installer = test
+ entries.Class = "NATIVE_TESTS"
- return module
-}
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ //entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+ if p.testConfig != nil {
+ entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
+ }
-func PythonTestHostFactory() android.Module {
- module := NewTest(android.HostSupportedNoCross)
+ entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
- return module.init()
-}
+ entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
-func PythonTestFactory() android.Module {
- module := NewTest(android.HostAndDeviceSupported)
- module.multilib = android.MultilibBoth
+ p.testProperties.Test_options.SetAndroidMkEntries(entries)
+ })
- return module.init()
+ return entriesList
}
diff --git a/python/tests/dont_import_folder_of_entrypoint/Android.bp b/python/tests/dont_import_folder_of_entrypoint/Android.bp
new file mode 100644
index 0000000..e54e9b2
--- /dev/null
+++ b/python/tests/dont_import_folder_of_entrypoint/Android.bp
@@ -0,0 +1,26 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_test_host {
+ name: "py_dont_import_folder_of_entrypoint_test",
+ main: "mypkg/main.py",
+ srcs: [
+ "mypkg/main.py",
+ "mypkg/mymodule.py",
+ ],
+}
+
+python_test_host {
+ name: "py_dont_import_folder_of_entrypoint_test_embedded_launcher",
+ main: "mypkg/main.py",
+ srcs: [
+ "mypkg/main.py",
+ "mypkg/mymodule.py",
+ ],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+}
diff --git a/python/tests/dont_import_folder_of_entrypoint/mypkg/main.py b/python/tests/dont_import_folder_of_entrypoint/mypkg/main.py
new file mode 100644
index 0000000..c6a36ed
--- /dev/null
+++ b/python/tests/dont_import_folder_of_entrypoint/mypkg/main.py
@@ -0,0 +1,15 @@
+import unittest
+import sys
+
+class TestProtoWithPkgPath(unittest.TestCase):
+
+ def test_cant_import_mymodule_directly(self):
+ with self.assertRaises(ImportError):
+ import mymodule
+
+ def test_can_import_mymodule_by_parent_package(self):
+ import mypkg.mymodule
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/python/tests/dont_import_folder_of_entrypoint/mypkg/mymodule.py b/python/tests/dont_import_folder_of_entrypoint/mypkg/mymodule.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/tests/dont_import_folder_of_entrypoint/mypkg/mymodule.py
diff --git a/python/tests/par_test.py b/python/tests/par_test.py
index 56a5063..1e03f16 100644
--- a/python/tests/par_test.py
+++ b/python/tests/par_test.py
@@ -27,7 +27,10 @@
failed = True
assert_equal("__name__", __name__, "__main__")
-assert_equal("os.path.basename(__file__)", os.path.basename(__file__), "par_test.py")
+fileName = os.path.basename(__file__)
+if fileName.endswith('.pyc'):
+ fileName = fileName[:-1]
+assert_equal("os.path.basename(__file__)", fileName, "par_test.py")
archive = os.path.dirname(__file__)
diff --git a/python/tests/proto_pkg_path/Android.bp b/python/tests/proto_pkg_path/Android.bp
new file mode 100644
index 0000000..a6bfd3f
--- /dev/null
+++ b/python/tests/proto_pkg_path/Android.bp
@@ -0,0 +1,16 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_test_host {
+ name: "py_proto_pkg_path_test",
+ main: "main.py",
+ srcs: [
+ "main.py",
+ "proto/*.proto",
+ ],
+ pkg_path: "mylib/subpackage",
+ proto: {
+ canonical_path_from_root: false,
+ },
+}
diff --git a/python/tests/proto_pkg_path/main.py b/python/tests/proto_pkg_path/main.py
new file mode 100644
index 0000000..c4acdde
--- /dev/null
+++ b/python/tests/proto_pkg_path/main.py
@@ -0,0 +1,18 @@
+import sys
+
+import unittest
+import mylib.subpackage.proto.test_pb2 as test_pb2
+import mylib.subpackage.proto.common_pb2 as common_pb2
+
+print(sys.path)
+
+class TestProtoWithPkgPath(unittest.TestCase):
+
+ def test_main(self):
+ x = test_pb2.MyMessage(name="foo",
+ common = common_pb2.MyCommonMessage(common="common"))
+ self.assertEqual(x.name, "foo")
+ self.assertEqual(x.common.common, "common")
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/python/tests/proto_pkg_path/proto/common.proto b/python/tests/proto_pkg_path/proto/common.proto
new file mode 100644
index 0000000..b24b8ea
--- /dev/null
+++ b/python/tests/proto_pkg_path/proto/common.proto
@@ -0,0 +1,5 @@
+syntax = "proto3";
+
+message MyCommonMessage {
+ string common = 1;
+}
diff --git a/python/tests/proto_pkg_path/proto/test.proto b/python/tests/proto_pkg_path/proto/test.proto
new file mode 100644
index 0000000..55f3b17
--- /dev/null
+++ b/python/tests/proto_pkg_path/proto/test.proto
@@ -0,0 +1,8 @@
+syntax = "proto3";
+
+import "mylib/subpackage/proto/common.proto";
+
+message MyMessage {
+ string name = 1;
+ MyCommonMessage common = 2;
+}
diff --git a/python/tests/testpkg/par_test.py b/python/tests/testpkg/par_test.py
index ffad430..b513409 100644
--- a/python/tests/testpkg/par_test.py
+++ b/python/tests/testpkg/par_test.py
@@ -28,7 +28,10 @@
archive = sys.modules["__main__"].__loader__.archive
assert_equal("__name__", __name__, "testpkg.par_test")
-assert_equal("__file__", __file__, os.path.join(archive, "testpkg/par_test.py"))
+fileName = __file__
+if fileName.endswith('.pyc'):
+ fileName = fileName[:-1]
+assert_equal("__file__", fileName, os.path.join(archive, "testpkg/par_test.py"))
# Python3 is returning None here for me, and I haven't found any problems caused by this.
if sys.version_info[0] == 2:
diff --git a/python/tests/top_level_dirs/Android.bp b/python/tests/top_level_dirs/Android.bp
new file mode 100644
index 0000000..574350a
--- /dev/null
+++ b/python/tests/top_level_dirs/Android.bp
@@ -0,0 +1,12 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_test_host {
+ name: "py_dont_add_top_level_dirs_test",
+ main: "main.py",
+ srcs: [
+ "main.py",
+ "mypkg/mymodule.py",
+ ],
+}
diff --git a/python/tests/top_level_dirs/main.py b/python/tests/top_level_dirs/main.py
new file mode 100644
index 0000000..9f30bfa
--- /dev/null
+++ b/python/tests/top_level_dirs/main.py
@@ -0,0 +1,17 @@
+import unittest
+import sys
+
+print(sys.path, file=sys.stderr)
+
+class TestProtoWithPkgPath(unittest.TestCase):
+
+ def test_cant_import_mymodule_directly(self):
+ with self.assertRaises(ImportError):
+ import mymodule
+
+ def test_can_import_mymodule_by_parent_package(self):
+ import mypkg.mymodule
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/python/tests/top_level_dirs/mypkg/mymodule.py b/python/tests/top_level_dirs/mypkg/mymodule.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/tests/top_level_dirs/mypkg/mymodule.py
diff --git a/rust/Android.bp b/rust/Android.bp
index 3fd68e5..b01a94a 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -42,6 +42,7 @@
"toolchain_library.go",
],
testSrcs: [
+ "afdo_test.go",
"benchmark_test.go",
"binary_test.go",
"bindgen_test.go",
diff --git a/rust/OWNERS b/rust/OWNERS
index ddaebc5..b595511 100644
--- a/rust/OWNERS
+++ b/rust/OWNERS
@@ -1,5 +1,2 @@
# Additional owner/reviewers for rust rules, including parent directory owners.
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 = chiw@google.com, chriswailes@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
diff --git a/rust/afdo.go b/rust/afdo.go
index 996fd7e..3534ee6 100644
--- a/rust/afdo.go
+++ b/rust/afdo.go
@@ -17,7 +17,10 @@
import (
"fmt"
+ "android/soong/android"
"android/soong/cc"
+
+ "github.com/google/blueprint"
)
const afdoFlagFormat = "-Zprofile-sample-use=%s"
@@ -30,19 +33,49 @@
return []interface{}{&afdo.Properties}
}
-func (afdo *afdo) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
+func (afdo *afdo) addDep(ctx BaseModuleContext, actx android.BottomUpMutatorContext) {
+ // afdo is not supported outside of Android
+ if ctx.Host() {
+ return
+ }
+
+ if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
+ fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName())
+ if err != nil {
+ ctx.ModuleErrorf("%s", err.Error())
+ }
+ if fdoProfileName != nil {
+ actx.AddFarVariationDependencies(
+ []blueprint.Variation{
+ {Mutator: "arch", Variation: actx.Target().ArchVariation()},
+ {Mutator: "os", Variation: "android"},
+ },
+ cc.FdoProfileTag,
+ []string{*fdoProfileName}...,
+ )
+ }
+ }
+}
+
+func (afdo *afdo) flags(ctx android.ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
if ctx.Host() {
return flags, deps
}
- if afdo != nil && afdo.Properties.Afdo {
- if profileFile := afdo.Properties.GetAfdoProfileFile(ctx, ctx.ModuleName()); profileFile.Valid() {
- profileUseFlag := fmt.Sprintf(afdoFlagFormat, profileFile)
+ if !afdo.Properties.Afdo {
+ return flags, deps
+ }
+
+ ctx.VisitDirectDepsWithTag(cc.FdoProfileTag, func(m android.Module) {
+ if ctx.OtherModuleHasProvider(m, cc.FdoProfileProvider) {
+ info := ctx.OtherModuleProvider(m, cc.FdoProfileProvider).(cc.FdoProfileInfo)
+ path := info.Path
+ profileUseFlag := fmt.Sprintf(afdoFlagFormat, path.String())
flags.RustFlags = append(flags.RustFlags, profileUseFlag)
- profileFilePath := profileFile.Path()
- deps.AfdoProfiles = append(deps.AfdoProfiles, profileFilePath)
+ deps.AfdoProfiles = append(deps.AfdoProfiles, path)
}
- }
+ })
+
return flags, deps
}
diff --git a/rust/afdo_test.go b/rust/afdo_test.go
new file mode 100644
index 0000000..0cdf704
--- /dev/null
+++ b/rust/afdo_test.go
@@ -0,0 +1,112 @@
+// Copyright 2023 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 rust
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+func TestAfdoEnabled(t *testing.T) {
+ bp := `
+ rust_binary {
+ name: "foo",
+ srcs: ["foo.rs"],
+ afdo: true,
+ }
+`
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ cc.PrepareForTestWithFdoProfile,
+ android.FixtureAddTextFile("afdo_profiles_package/foo.afdo", ""),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AfdoProfiles = []string{
+ "foo://afdo_profiles_package:foo_afdo",
+ }
+ }),
+ android.MockFS{
+ "afdo_profiles_package/Android.bp": []byte(`
+ fdo_profile {
+ name: "foo_afdo",
+ profile: "foo.afdo",
+ }
+ `),
+ }.AddToFixture(),
+ rustMockedFiles.AddToFixture(),
+ ).RunTestWithBp(t, bp)
+
+ foo := result.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+
+ expectedCFlag := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo.afdo")
+
+ if !strings.Contains(foo.Args["rustcFlags"], expectedCFlag) {
+ t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", expectedCFlag, foo.Args["rustcFlags"])
+ }
+}
+
+func TestAfdoEnabledWithMultiArchs(t *testing.T) {
+ bp := `
+ rust_binary {
+ name: "foo",
+ srcs: ["foo.rs"],
+ afdo: true,
+ compile_multilib: "both",
+ }
+`
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ cc.PrepareForTestWithFdoProfile,
+ android.FixtureAddTextFile("afdo_profiles_package/foo_arm.afdo", ""),
+ android.FixtureAddTextFile("afdo_profiles_package/foo_arm64.afdo", ""),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AfdoProfiles = []string{
+ "foo://afdo_profiles_package:foo_afdo",
+ }
+ }),
+ android.MockFS{
+ "afdo_profiles_package/Android.bp": []byte(`
+ fdo_profile {
+ name: "foo_afdo",
+ arch: {
+ arm: {
+ profile: "foo_arm.afdo",
+ },
+ arm64: {
+ profile: "foo_arm64.afdo",
+ }
+ }
+ }
+ `),
+ }.AddToFixture(),
+ rustMockedFiles.AddToFixture(),
+ ).RunTestWithBp(t, bp)
+
+ fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon").Rule("rustc")
+ fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+
+ expectedCFlagArm := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo_arm.afdo")
+ expectedCFlagArm64 := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo_arm64.afdo")
+
+ if !strings.Contains(fooArm.Args["rustcFlags"], expectedCFlagArm) {
+ t.Errorf("Expected 'fooArm' to enable afdo, but did not find %q in cflags %q", expectedCFlagArm, fooArm.Args["rustcFlags"])
+ }
+
+ if !strings.Contains(fooArm64.Args["rustcFlags"], expectedCFlagArm64) {
+ t.Errorf("Expected 'fooArm64' to enable afdo, but did not find %q in cflags %q", expectedCFlagArm64, fooArm64.Args["rustcFlags"])
+ }
+}
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 2361e03..5e680b0 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -43,6 +43,10 @@
}
}
+func (mod *Module) AndroidMkSuffix() string {
+ return mod.Properties.RustSubName + mod.Properties.SubName
+}
+
func (mod *Module) AndroidMkEntries() []android.AndroidMkEntries {
if mod.Properties.HideFromMake || mod.hideApexVariantFromMake {
@@ -79,8 +83,7 @@
mod.SubAndroidMk(&ret, mod.sanitize)
}
- ret.SubName += mod.Properties.RustSubName
- ret.SubName += mod.Properties.SubName
+ ret.SubName += mod.AndroidMkSuffix()
return []android.AndroidMkEntries{ret}
}
@@ -105,10 +108,11 @@
entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
}
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...)
}
+
+ test.Properties.Test_options.SetAndroidMkEntries(entries)
})
cc.AndroidMkWriteTestData(test.data, ret)
@@ -151,6 +155,11 @@
})
}
+func (library *snapshotLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
+ ctx.SubAndroidMk(ret, library.libraryDecorator)
+ ret.SubName = library.SnapshotAndroidMkSuffix()
+}
+
func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
ctx.SubAndroidMk(ret, procMacro.baseCompiler)
@@ -204,8 +213,8 @@
})
}
-func (fuzz *fuzzDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
- ctx.SubAndroidMk(entries, fuzz.binaryDecorator)
+func (fuzz *fuzzDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
+ ctx.SubAndroidMk(ret, fuzz.binaryDecorator)
var fuzzFiles []string
for _, d := range fuzz.fuzzPackagedModule.Corpus {
@@ -228,11 +237,14 @@
filepath.Dir(fuzz.fuzzPackagedModule.Config.String())+":config.json")
}
- entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext,
+ ret.ExtraEntries = append(ret.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext,
entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
if len(fuzzFiles) > 0 {
entries.AddStrings("LOCAL_TEST_DATA", fuzzFiles...)
}
+ if fuzz.installedSharedDeps != nil {
+ entries.AddStrings("LOCAL_FUZZ_INSTALLED_SHARED_DEPS", fuzz.installedSharedDeps...)
+ }
})
}
diff --git a/rust/benchmark.go b/rust/benchmark.go
index 0e84243..c0f1e24 100644
--- a/rust/benchmark.go
+++ b/rust/benchmark.go
@@ -112,12 +112,14 @@
}
func (benchmark *benchmarkDecorator) install(ctx ModuleContext) {
- benchmark.testConfig = tradefed.AutoGenRustBenchmarkConfig(ctx,
- benchmark.Properties.Test_config,
- benchmark.Properties.Test_config_template,
- benchmark.Properties.Test_suites,
- nil,
- benchmark.Properties.Auto_gen_config)
+ benchmark.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: benchmark.Properties.Test_config,
+ TestConfigTemplateProp: benchmark.Properties.Test_config_template,
+ TestSuites: benchmark.Properties.Test_suites,
+ AutoGenConfig: benchmark.Properties.Auto_gen_config,
+ DeviceTemplate: "${RustDeviceBenchmarkConfigTemplate}",
+ HostTemplate: "${RustHostBenchmarkConfigTemplate}",
+ })
// default relative install path is module name
if !Bool(benchmark.Properties.No_named_install_directory) {
diff --git a/rust/binary.go b/rust/binary.go
index 0dc320e..2de92c1 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -72,11 +72,14 @@
func (binary *binaryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
flags = binary.baseCompiler.compilerFlags(ctx, flags)
+ if ctx.Os().Linux() {
+ flags.LinkFlags = append(flags.LinkFlags, "-Wl,--gc-sections")
+ }
+
if ctx.toolchain().Bionic() {
// no-undefined-version breaks dylib compilation since __rust_*alloc* functions aren't defined,
// but we can apply this to binaries.
flags.LinkFlags = append(flags.LinkFlags,
- "-Wl,--gc-sections",
"-Wl,-z,nocopyreloc",
"-Wl,--no-undefined-version")
@@ -106,7 +109,7 @@
if static {
deps.CrtBegin = []string{"libc_musl_crtbegin_static"}
} else {
- deps.CrtBegin = []string{"libc_musl_crtbegin_dynamic", "musl_linker_script"}
+ deps.CrtBegin = []string{"libc_musl_crtbegin_dynamic"}
}
deps.CrtEnd = []string{"libc_musl_crtend"}
}
@@ -128,15 +131,15 @@
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...)
- flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
+ flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects.Strings()...)
if binary.stripper.NeedsStrip(ctx) {
strippedOutputFile := outputFile
@@ -147,8 +150,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/binary_test.go b/rust/binary_test.go
index 7dac249..dd4f993 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -123,7 +123,7 @@
bootstrap: true,
}`)
- foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+ foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustLink")
flag := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker64"
if !strings.Contains(foo.Args["linkFlags"], flag) {
@@ -140,10 +140,11 @@
}`)
fizzOut := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc")
+ fizzOutLink := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustLink")
fizzMod := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)
flags := fizzOut.Args["rustcFlags"]
- linkFlags := fizzOut.Args["linkFlags"]
+ linkFlags := fizzOutLink.Args["linkFlags"]
if !strings.Contains(flags, "-C relocation-model=static") {
t.Errorf("static binary missing '-C relocation-model=static' in rustcFlags, found: %#v", flags)
}
@@ -173,7 +174,7 @@
name: "libfoo",
}`)
- fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Rule("rustc")
+ fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Rule("rustLink")
linkFlags := fizzBuzz.Args["linkFlags"]
if !strings.Contains(linkFlags, "/libfoo.so") {
t.Errorf("missing shared dependency 'libfoo.so' in linkFlags: %#v", linkFlags)
diff --git a/rust/bindgen.go b/rust/bindgen.go
index b4626a0..96645b0 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -15,7 +15,6 @@
package rust
import (
- "fmt"
"strings"
"github.com/google/blueprint"
@@ -30,7 +29,7 @@
defaultBindgenFlags = []string{""}
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
- bindgenClangVersion = "clang-r450784d"
+ bindgenClangVersion = "clang-r487747c"
_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -41,10 +40,25 @@
//TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen.
_ = pctx.HostBinToolVariable("bindgenCmd", "bindgen")
+ _ = pctx.VariableFunc("bindgenHostPrebuiltTag", func(ctx android.PackageVarContext) string {
+ if ctx.Config().UseHostMusl() {
+ // This is a hack to use the glibc bindgen binary until we have a musl version checked in.
+ return "linux-x86"
+ } else {
+ return "${config.HostPrebuiltTag}"
+ }
+ })
+ _ = pctx.VariableFunc("bindgenClangLibdir", func(ctx android.PackageVarContext) string {
+ if ctx.Config().UseHostMusl() {
+ return "musl/lib/"
+ } else {
+ return "lib/"
+ }
+ })
_ = pctx.SourcePathVariable("bindgenClang",
- "${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/bin/clang")
+ "${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/bin/clang")
_ = pctx.SourcePathVariable("bindgenLibClang",
- "${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/lib64/")
+ "${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/${bindgenClangLibdir}")
//TODO(ivanlozano) Switch this to RuleBuilder
bindgen = pctx.AndroidStaticRule("bindgen",
@@ -163,10 +177,6 @@
if mctx, ok := ctx.(*moduleContext); ok && mctx.apexVariationName() != "" {
cflags = append(cflags, "-D__ANDROID_APEX__")
- if ctx.Device() {
- cflags = append(cflags, fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
- ctx.RustModule().apexSdkVersion.FinalOrFutureInt()))
- }
}
if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -224,6 +234,15 @@
cflags = append(cflags, "-x c")
}
+ // clang-r468909b complains about the -x c in the flags in clang-sys parse_search_paths:
+ // clang: error: '-x c' after last input file has no effect [-Werror,-Wunused-command-line-argument]
+ cflags = append(cflags, "-Wno-unused-command-line-argument")
+
+ // LLVM_NEXT may contain flags that bindgen doesn't recognise. Turn off unknown flags warning.
+ if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+ cflags = append(cflags, "-Wno-unknown-warning-option")
+ }
+
outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
var cmd, cmdDesc string
@@ -279,14 +298,22 @@
ClangProperties: cc.RustBindgenClangProperties{},
}
- module := NewSourceProviderModule(hod, bindgen, false)
+ module := NewSourceProviderModule(hod, bindgen, false, true)
+
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ type stub_props struct {
+ Visibility []string
+ }
+ props := &stub_props{[]string{":__subpackages__"}}
+ ctx.PrependProperties(props)
+ })
return module, bindgen
}
func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
- if ctx.toolchain().Bionic() {
+ if ctx.toolchain().Bionic() && !ctx.RustModule().compiler.noStdlibs() {
deps = bionicDeps(ctx, deps, false)
} else if ctx.Os() == android.LinuxMusl {
deps = muslDeps(ctx, deps, false)
diff --git a/rust/builder.go b/rust/builder.go
index 20ca5db..0aa2225 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -26,14 +26,14 @@
var (
_ = pctx.SourcePathVariable("rustcCmd", "${config.RustBin}/rustc")
+ _ = pctx.SourcePathVariable("mkcraterspCmd", "build/soong/scripts/mkcratersp.py")
rustc = pctx.AndroidStaticRule("rustc",
blueprint.RuleParams{
Command: "$envVars $rustcCmd " +
- "-C linker=${config.RustLinker} " +
- "-C link-args=\"${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}\" " +
+ "-C linker=$mkcraterspCmd " +
"--emit link -o $out --emit dep-info=$out.d.raw $in ${libFlags} $rustcFlags" +
" && grep \"^$out:\" $out.d.raw > $out.d",
- CommandDeps: []string{"$rustcCmd"},
+ CommandDeps: []string{"$rustcCmd", "$mkcraterspCmd"},
// Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633
// Rustc emits unneeded dependency lines for the .d and input .rs files.
// Those extra lines cause ninja warning:
@@ -42,7 +42,12 @@
Deps: blueprint.DepsGCC,
Depfile: "$out.d",
},
- "rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
+ "rustcFlags", "libFlags", "envVars")
+ rustLink = pctx.AndroidStaticRule("rustLink",
+ blueprint.RuleParams{
+ Command: "${config.RustLinker} -o $out ${crtBegin} ${config.RustLinkerArgs} @$in ${linkFlags} ${crtEnd}",
+ },
+ "linkFlags", "crtBegin", "crtEnd")
_ = pctx.SourcePathVariable("rustdocCmd", "${config.RustBin}/rustdoc")
rustdoc = pctx.AndroidStaticRule("rustdoc",
@@ -83,10 +88,36 @@
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=true ` +
+ `$in ${libFlags} $rustcFlags`,
+ CommandDeps: []string{"$rustExtractor", "$kytheVnames"},
+ Rspfile: "${out}.rsp",
+ RspfileContent: "$in",
+ },
+ "rustcFlags", "libFlags", "envVars")
)
type buildOutput struct {
outputFile android.Path
+ kytheFile android.Path
}
func init() {
@@ -107,6 +138,8 @@
func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
+ flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib")
}
@@ -179,6 +212,9 @@
outDirPrefix = ""
}
envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String()))
+ } else {
+ // TODO(pcc): Change this to "OUT_DIR=" after fixing crates to not rely on this value.
+ envVars = append(envVars, "OUT_DIR=out")
}
return envVars
@@ -188,10 +224,9 @@
outputFile android.WritablePath, crateType string) buildOutput {
var inputs android.Paths
- var implicits android.Paths
+ var implicits, linkImplicits, linkOrderOnly android.Paths
var output buildOutput
var rustcFlags, linkFlags []string
- var implicitOutputs android.WritablePaths
output.outputFile = outputFile
crateName := ctx.RustModule().CrateName()
@@ -220,7 +255,13 @@
if ctx.Config().IsEnvTrue("SOONG_RUSTC_INCREMENTAL") {
incrementalPath := android.PathForOutput(ctx, "rustc").String()
- rustcFlags = append(rustcFlags, "-C incremental="+incrementalPath)
+ rustcFlags = append(rustcFlags, "-Cincremental="+incrementalPath)
+ }
+
+ // Disallow experimental features
+ modulePath := android.PathForModuleSrc(ctx).String()
+ if !(android.IsThirdPartyPath(modulePath) || strings.HasPrefix(modulePath, "prebuilts")) {
+ rustcFlags = append(rustcFlags, "-Zallow-features=\"custom_inner_attributes,mixed_integer_ops\"")
}
// Collect linker flags
@@ -242,13 +283,15 @@
implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
- implicits = append(implicits, deps.StaticLibs...)
- implicits = append(implicits, deps.SharedLibDeps...)
- implicits = append(implicits, deps.srcProviderFiles...)
implicits = append(implicits, deps.AfdoProfiles...)
+ implicits = append(implicits, deps.srcProviderFiles...)
+ implicits = append(implicits, deps.WholeStaticLibs...)
- implicits = append(implicits, deps.CrtBegin...)
- implicits = append(implicits, deps.CrtEnd...)
+ linkImplicits = append(linkImplicits, deps.LibDeps...)
+ linkImplicits = append(linkImplicits, deps.CrtBegin...)
+ linkImplicits = append(linkImplicits, deps.CrtEnd...)
+
+ linkOrderOnly = append(linkOrderOnly, deps.linkObjects...)
if len(deps.SrcDeps) > 0 {
moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
@@ -287,15 +330,16 @@
}
}
+ envVars = append(envVars, "AR=${cc_config.ClangBin}/llvm-ar")
+
if flags.Clippy {
clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
ctx.Build(pctx, android.BuildParams{
- Rule: clippyDriver,
- Description: "clippy " + main.Rel(),
- Output: clippyFile,
- ImplicitOutputs: nil,
- Inputs: inputs,
- Implicits: implicits,
+ Rule: clippyDriver,
+ Description: "clippy " + main.Rel(),
+ Output: clippyFile,
+ Inputs: inputs,
+ Implicits: implicits,
Args: map[string]string{
"rustcFlags": strings.Join(rustcFlags, " "),
"libFlags": strings.Join(libFlags, " "),
@@ -307,23 +351,57 @@
implicits = append(implicits, clippyFile)
}
+ rustcOutputFile := outputFile
+ usesLinker := crateType == "bin" || crateType == "dylib" || crateType == "cdylib" || crateType == "proc-macro"
+ if usesLinker {
+ rustcOutputFile = android.PathForModuleOut(ctx, outputFile.Base()+".rsp")
+ }
+
ctx.Build(pctx, android.BuildParams{
- Rule: rustc,
- Description: "rustc " + main.Rel(),
- Output: outputFile,
- ImplicitOutputs: implicitOutputs,
- Inputs: inputs,
- Implicits: implicits,
+ Rule: rustc,
+ Description: "rustc " + main.Rel(),
+ Output: rustcOutputFile,
+ 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, " "),
},
})
+ if usesLinker {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: rustLink,
+ Description: "rustLink " + main.Rel(),
+ Output: outputFile,
+ Inputs: android.Paths{rustcOutputFile},
+ Implicits: linkImplicits,
+ OrderOnly: linkOrderOnly,
+ Args: map[string]string{
+ "linkFlags": strings.Join(linkFlags, " "),
+ "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "),
+ "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "),
+ },
+ })
+ }
+
+ 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, " "),
+ "libFlags": strings.Join(libFlags, " "),
+ "envVars": strings.Join(envVars, " "),
+ },
+ })
+ output.kytheFile = kytheFile
+ }
return output
}
@@ -353,7 +431,7 @@
// Silence warnings about renamed lints for third-party crates
modulePath := android.PathForModuleSrc(ctx).String()
if android.IsThirdPartyPath(modulePath) {
- rustdocFlags = append(rustdocFlags, " -A renamed_and_removed_lints")
+ rustdocFlags = append(rustdocFlags, " -A warnings")
}
// Yes, the same out directory is used simultaneously by all rustdoc builds.
diff --git a/rust/compiler.go b/rust/compiler.go
index 19499fa..06ae12f 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -15,6 +15,7 @@
package rust
import (
+ "android/soong/cc"
"fmt"
"path/filepath"
"strings"
@@ -161,7 +162,7 @@
// This is primarily meant for rust_binary and rust_ffi modules where the default
// linkage of libstd might need to be overridden in some use cases. This should
// generally be avoided with other module types since it may cause collisions at
- // linkage if all dependencies of the root binary module do not link against libstd\
+ // linkage if all dependencies of the root binary module do not link against libstd
// the same way.
Prefer_rlib *bool `android:"arch_variant"`
@@ -207,6 +208,10 @@
panic("baseCompiler does not implement SetDisabled()")
}
+func (compiler *baseCompiler) noStdlibs() bool {
+ return Bool(compiler.Properties.No_stdlibs)
+}
+
func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath {
panic("baseCompiler does not implement coverageOutputZipPath()")
}
@@ -264,6 +269,11 @@
func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags {
if ctx.RustModule().UseVndk() {
compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vndk")
+ if ctx.RustModule().InVendor() {
+ compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vendor")
+ } else if ctx.RustModule().InProduct() {
+ compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_product")
+ }
}
flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...)
@@ -304,27 +314,16 @@
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/`
- if ctx.Darwin() {
- rpathPrefix = "@loader_path/"
- }
-
- var rpath string
- if ctx.toolchain().Is64Bit() {
- rpath = "lib64"
- } else {
- rpath = "lib"
- }
- flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
- flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+"../"+rpath)
+ flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...)
}
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!"))
}
@@ -370,8 +369,9 @@
if !Bool(compiler.Properties.No_stdlibs) {
for _, stdlib := range config.Stdlibs {
- // If we're building for the build host, use the prebuilt stdlibs
- if ctx.Target().Os == android.Linux || ctx.Target().Os == android.Darwin {
+ // If we're building for the build host, use the prebuilt stdlibs, unless the host
+ // is linux_bionic which doesn't have prebuilts.
+ if ctx.Host() && !ctx.Target().HostCross && ctx.Target().Os != android.LinuxBionic {
stdlib = "prebuilt_" + stdlib
}
deps.Stdlibs = append(deps.Stdlibs, stdlib)
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index e07d39b..79ea7a1 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -11,12 +11,12 @@
],
srcs: [
"arm_device.go",
+ "arm_linux_host.go",
"arm64_device.go",
"global.go",
"lints.go",
"riscv64_device.go",
"toolchain.go",
- "allowed_list.go",
"darwin_host.go",
"x86_linux_bionic_host.go",
"x86_linux_host.go",
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
deleted file mode 100644
index 802e1da..0000000
--- a/rust/config/allowed_list.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package config
-
-var (
- // When adding a new path below, add a rustfmt.toml file at the root of
- // the repository and enable the rustfmt repo hook. See aosp/1458238
- // for an example.
- // TODO(b/160223496): enable rustfmt globally.
- RustAllowedPaths = []string{
- "device/google/cuttlefish",
- "external/adhd",
- "external/crosvm",
- "external/libchromeos-rs",
- "external/minijail",
- "external/open-dice",
- "external/rust",
- "external/selinux/libselinux",
- "external/uwb",
- "external/vm_tools/p9",
- "frameworks/native/libs/binder/rust",
- "frameworks/proto_logging/stats",
- "hardware/interfaces/security",
- "hardware/interfaces/uwb",
- "packages/modules/Bluetooth",
- "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/extras/profcollectd",
- "system/extras/simpleperf",
- "system/hardware/interfaces/keystore2",
- "system/librustutils",
- "system/logging/liblog",
- "system/logging/rust",
- "system/nfc",
- "system/security",
- "system/tools/aidl",
- "tools/security/fuzzing/example_rust_fuzzer",
- "tools/security/fuzzing/orphans",
- "tools/security/remote_provisioning/cert_validator",
- "tools/vendor",
- "vendor/",
- }
-
- DownstreamRustAllowedPaths = []string{
- // Add downstream allowed Rust paths here.
- }
-
- RustModuleTypes = []string{
- // Don't add rust_bindgen or rust_protobuf as these are code generation modules
- // and can be expected to be in paths without Rust code.
- "rust_benchmark",
- "rust_benchmark_host",
- "rust_binary",
- "rust_binary_host",
- "rust_library",
- "rust_library_dylib",
- "rust_library_rlib",
- "rust_ffi",
- "rust_ffi_shared",
- "rust_ffi_static",
- "rust_fuzz",
- "rust_library_host",
- "rust_library_host_dylib",
- "rust_library_host_rlib",
- "rust_ffi_host",
- "rust_ffi_host_shared",
- "rust_ffi_host_static",
- "rust_proc_macro",
- "rust_test",
- "rust_test_host",
- }
-)
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 186e571..ae783e8 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -30,6 +30,7 @@
"armv8-a-branchprot": []string{},
"armv8-2a": []string{},
"armv8-2a-dotprod": []string{},
+ "armv9-a": []string{},
}
)
diff --git a/rust/config/arm_linux_host.go b/rust/config/arm_linux_host.go
new file mode 100644
index 0000000..22bdaee
--- /dev/null
+++ b/rust/config/arm_linux_host.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 config
+
+import (
+ "strings"
+
+ "android/soong/android"
+)
+
+var (
+ linuxArmRustflags = []string{}
+ linuxArmLinkflags = []string{}
+ linuxArm64Rustflags = []string{}
+ linuxArm64Linkflags = []string{}
+)
+
+func init() {
+ registerToolchainFactory(android.LinuxMusl, android.Arm64, linuxMuslArm64ToolchainFactory)
+ registerToolchainFactory(android.LinuxMusl, android.Arm, linuxMuslArmToolchainFactory)
+
+ pctx.StaticVariable("LinuxToolchainArmRustFlags", strings.Join(linuxArmRustflags, " "))
+ pctx.StaticVariable("LinuxToolchainArmLinkFlags", strings.Join(linuxArmLinkflags, " "))
+ pctx.StaticVariable("LinuxToolchainArm64RustFlags", strings.Join(linuxArm64Rustflags, " "))
+ pctx.StaticVariable("LinuxToolchainArm64LinkFlags", strings.Join(linuxArm64Linkflags, " "))
+}
+
+// Base 64-bit linux rust toolchain
+type toolchainLinuxArm64 struct {
+ toolchain64Bit
+}
+
+func (toolchainLinuxArm64) Supported() bool {
+ return true
+}
+
+func (toolchainLinuxArm64) Bionic() bool {
+ return false
+}
+
+func (t *toolchainLinuxArm64) Name() string {
+ return "arm64"
+}
+
+func (t *toolchainLinuxArm64) ToolchainLinkFlags() string {
+ // Prepend the lld flags from cc_config so we stay in sync with cc
+ return "${cc_config.LinuxLldflags} ${cc_config.LinuxArm64Lldflags} " +
+ "${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainArm64LinkFlags}"
+}
+
+func (t *toolchainLinuxArm64) ToolchainRustFlags() string {
+ return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainArm64RustFlags}"
+}
+
+// Specialization of the 64-bit linux rust toolchain for musl. Adds the musl rust triple and
+// linker flags to avoid using the host sysroot.
+type toolchainLinuxMuslArm64 struct {
+ toolchainLinuxArm64
+}
+
+func (t *toolchainLinuxMuslArm64) RustTriple() string {
+ return "aarch64-unknown-linux-musl"
+}
+
+func (t *toolchainLinuxMuslArm64) ToolchainLinkFlags() string {
+ return t.toolchainLinuxArm64.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslArm64) ToolchainRustFlags() string {
+ return t.toolchainLinuxArm64.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslArm64ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxMuslArm64Singleton
+}
+
+// Base 32-bit linux rust toolchain
+type toolchainLinuxArm struct {
+ toolchain32Bit
+}
+
+func (toolchainLinuxArm) Supported() bool {
+ return true
+}
+
+func (toolchainLinuxArm) Bionic() bool {
+ return false
+}
+
+func (t *toolchainLinuxArm) Name() string {
+ return "arm"
+}
+
+func (toolchainLinuxArm) LibclangRuntimeLibraryArch() string {
+ return "arm"
+}
+
+func (toolchainLinuxArm64) LibclangRuntimeLibraryArch() string {
+ return "arm64"
+}
+
+func (t *toolchainLinuxArm) ToolchainLinkFlags() string {
+ // Prepend the lld flags from cc_config so we stay in sync with cc
+ return "${cc_config.LinuxLldflags} ${cc_config.LinuxArmLldflags} " +
+ "${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainArmLinkFlags}"
+}
+
+func (t *toolchainLinuxArm) ToolchainRustFlags() string {
+ return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainArmRustFlags}"
+}
+
+// Specialization of the 32-bit linux rust toolchain for musl. Adds the musl rust triple and
+// linker flags to avoid using the host sysroot.
+type toolchainLinuxMuslArm struct {
+ toolchainLinuxArm
+}
+
+func (t *toolchainLinuxMuslArm) RustTriple() string {
+ return "arm-unknown-linux-musleabihf"
+}
+
+func (t *toolchainLinuxMuslArm) ToolchainLinkFlags() string {
+ return t.toolchainLinuxArm.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslArm) ToolchainRustFlags() string {
+ return t.toolchainLinuxArm.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslArmToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxMuslArmSingleton
+}
+
+var toolchainLinuxMuslArm64Singleton Toolchain = &toolchainLinuxMuslArm64{}
+var toolchainLinuxMuslArmSingleton Toolchain = &toolchainLinuxMuslArm{}
diff --git a/rust/config/global.go b/rust/config/global.go
index 2d5fa99..2d1f0c1 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.59.0"
+ RustDefaultVersion = "1.68.0"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2021"
Stdlibs = []string{
@@ -50,6 +50,11 @@
"-C force-unwind-tables=yes",
// Use v0 mangling to distinguish from C++ symbols
"-C symbol-mangling-version=v0",
+ "--color always",
+ // TODO (b/267698452): Temporary workaround until the "no unstable
+ // features" policy is enforced.
+ "-A stable-features",
+ "-Zdylib-lto",
}
deviceGlobalRustFlags = []string{
@@ -77,7 +82,13 @@
func init() {
pctx.SourcePathVariable("RustDefaultBase", RustDefaultBase)
- pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+ pctx.VariableConfigMethod("HostPrebuiltTag", func(config android.Config) string {
+ if config.UseHostMusl() {
+ return "linux-musl-x86"
+ } else {
+ return config.PrebuiltOS()
+ }
+ })
pctx.VariableFunc("RustBase", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("RUST_PREBUILTS_BASE"); override != "" {
@@ -93,7 +104,7 @@
pctx.ImportAs("cc_config", "android/soong/cc/config")
pctx.StaticVariable("RustLinker", "${cc_config.ClangBin}/clang++")
- pctx.StaticVariable("RustLinkerArgs", "")
+ pctx.StaticVariable("RustLinkerArgs", "-Wl,--as-needed")
pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go
index 94b719f..3458ec9 100644
--- a/rust/config/x86_64_device.go
+++ b/rust/config/x86_64_device.go
@@ -26,15 +26,18 @@
x86_64LinkFlags = []string{}
x86_64ArchVariantRustFlags = map[string][]string{
- "": []string{},
- "broadwell": []string{"-C target-cpu=broadwell"},
- "haswell": []string{"-C target-cpu=haswell"},
- "ivybridge": []string{"-C target-cpu=ivybridge"},
- "sandybridge": []string{"-C target-cpu=sandybridge"},
- "silvermont": []string{"-C target-cpu=silvermont"},
- "skylake": []string{"-C target-cpu=skylake"},
+ "": []string{},
+ "broadwell": []string{"-C target-cpu=broadwell"},
+ "goldmont": []string{"-C target-cpu=goldmont"},
+ "goldmont-plus": []string{"-C target-cpu=goldmont-plus"},
+ "haswell": []string{"-C target-cpu=haswell"},
+ "ivybridge": []string{"-C target-cpu=ivybridge"},
+ "sandybridge": []string{"-C target-cpu=sandybridge"},
+ "silvermont": []string{"-C target-cpu=silvermont"},
+ "skylake": []string{"-C target-cpu=skylake"},
//TODO: Add target-cpu=stoneyridge when rustc supports it.
"stoneyridge": []string{""},
+ "tremont": []string{"-C target-cpu=tremont"},
}
)
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
index 5ae30e7..43f7340 100644
--- a/rust/config/x86_device.go
+++ b/rust/config/x86_device.go
@@ -26,16 +26,19 @@
x86LinkFlags = []string{}
x86ArchVariantRustFlags = map[string][]string{
- "": []string{},
- "atom": []string{"-C target-cpu=atom"},
- "broadwell": []string{"-C target-cpu=broadwell"},
- "haswell": []string{"-C target-cpu=haswell"},
- "ivybridge": []string{"-C target-cpu=ivybridge"},
- "sandybridge": []string{"-C target-cpu=sandybridge"},
- "silvermont": []string{"-C target-cpu=silvermont"},
- "skylake": []string{"-C target-cpu=skylake"},
+ "": []string{},
+ "atom": []string{"-C target-cpu=atom"},
+ "broadwell": []string{"-C target-cpu=broadwell"},
+ "goldmont": []string{"-C target-cpu=goldmont"},
+ "goldmont-plus": []string{"-C target-cpu=goldmont-plus"},
+ "haswell": []string{"-C target-cpu=haswell"},
+ "ivybridge": []string{"-C target-cpu=ivybridge"},
+ "sandybridge": []string{"-C target-cpu=sandybridge"},
+ "silvermont": []string{"-C target-cpu=silvermont"},
+ "skylake": []string{"-C target-cpu=skylake"},
//TODO: Add target-cpu=stoneyridge when rustc supports it.
"stoneyridge": []string{""},
+ "tremont": []string{"-C target-cpu=tremont"},
// use prescott for x86_64, like cc/config/x86_device.go
"x86_64": []string{"-C target-cpu=prescott"},
}
diff --git a/rust/config/x86_linux_bionic_host.go b/rust/config/x86_linux_bionic_host.go
index b1a2c17..79c40ce 100644
--- a/rust/config/x86_linux_bionic_host.go
+++ b/rust/config/x86_linux_bionic_host.go
@@ -21,7 +21,9 @@
)
var (
- LinuxBionicRustFlags = []string{}
+ LinuxBionicRustFlags = []string{
+ "-C panic=abort",
+ }
LinuxBionicRustLinkFlags = []string{
"-B${cc_config.ClangBin}",
"-fuse-ld=lld",
diff --git a/rust/coverage.go b/rust/coverage.go
index 651ce6e..5216d60 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -21,6 +21,7 @@
)
var CovLibraryName = "libprofile-clang-extras"
+var ProfilerBuiltins = "libprofiler_builtins.rust_sysroot"
// Add '%c' to default specifier after we resolve http://b/210012154
const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
@@ -41,6 +42,11 @@
ctx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
}, cc.CoverageDepTag, CovLibraryName)
+
+ // no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
+ if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
+ ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, ProfilerBuiltins)
+ }
}
return deps
@@ -56,10 +62,17 @@
flags.Coverage = true
coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
flags.RustFlags = append(flags.RustFlags,
- "-Z instrument-coverage", "-g")
+ "-C instrument-coverage", "-g")
flags.LinkFlags = append(flags.LinkFlags,
profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
- deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+ deps.LibDeps = append(deps.LibDeps, coverage.OutputFile().Path())
+
+ // no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
+ if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
+ profiler_builtins := ctx.GetDirectDepWithTag(ProfilerBuiltins, rlibDepTag).(*Module)
+ deps.RLibs = append(deps.RLibs, RustLibrary{Path: profiler_builtins.OutputFile().Path(), CrateName: profiler_builtins.CrateName()})
+ }
+
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")
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index f3cd375..64077cf 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -55,8 +55,12 @@
libbarNoCov := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustc")
fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
+ libfooCovLink := ctx.ModuleForTests("libfoo_cov", "android_arm64_armv8-a_dylib_cov").Rule("rustLink")
+ libbarNoCovLink := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustLink")
+ fizzCovLink := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustLink")
+ buzzNoCovLink := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustLink")
- rustcCoverageFlags := []string{"-Z instrument-coverage", " -g "}
+ rustcCoverageFlags := []string{"-C instrument-coverage", " -g "}
for _, flag := range rustcCoverageFlags {
missingErrorStr := "missing rustc flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
containsErrorStr := "contains rustc flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
@@ -80,17 +84,17 @@
missingErrorStr := "missing rust linker flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
containsErrorStr := "contains rust linker flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
- if !strings.Contains(fizzCov.Args["linkFlags"], flag) {
- t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["linkFlags"])
+ if !strings.Contains(fizzCovLink.Args["linkFlags"], flag) {
+ t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCovLink.Args["linkFlags"])
}
- if !strings.Contains(libfooCov.Args["linkFlags"], flag) {
- t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["linkFlags"])
+ if !strings.Contains(libfooCovLink.Args["linkFlags"], flag) {
+ t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCovLink.Args["linkFlags"])
}
- if strings.Contains(buzzNoCov.Args["linkFlags"], flag) {
- t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["linkFlags"])
+ if strings.Contains(buzzNoCovLink.Args["linkFlags"], flag) {
+ t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCovLink.Args["linkFlags"])
}
- if strings.Contains(libbarNoCov.Args["linkFlags"], flag) {
- t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["linkFlags"])
+ if strings.Contains(libbarNoCovLink.Args["linkFlags"], flag) {
+ t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCovLink.Args["linkFlags"])
}
}
@@ -103,7 +107,7 @@
srcs: ["foo.rs"],
}`)
- fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustc")
+ fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustLink")
if !strings.Contains(fizz.Args["linkFlags"], "libprofile-clang-extras.a") {
t.Fatalf("missing expected coverage 'libprofile-clang-extras' dependency in linkFlags: %#v", fizz.Args["linkFlags"])
}
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 55921ba..d7e7ddf 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -16,8 +16,6 @@
import (
"path/filepath"
- "sort"
- "strings"
"android/soong/android"
"android/soong/cc"
@@ -27,13 +25,14 @@
func init() {
android.RegisterModuleType("rust_fuzz", RustFuzzFactory)
- android.RegisterSingletonType("rust_fuzz_packaging", rustFuzzPackagingFactory)
}
type fuzzDecorator struct {
*binaryDecorator
- fuzzPackagedModule fuzz.FuzzPackagedModule
+ fuzzPackagedModule fuzz.FuzzPackagedModule
+ sharedLibraries android.Paths
+ installedSharedDeps []string
}
var _ compiler = (*fuzzDecorator)(nil)
@@ -63,9 +62,14 @@
flags = fuzzer.binaryDecorator.compilerFlags(ctx, flags)
// `../lib` for installed fuzz targets (both host and device), and `./lib` for fuzz target packages.
- flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+ if ctx.InstallInVendor() {
+ flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../../lib`)
+ } else {
+ flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+
+ }
return flags
}
@@ -86,6 +90,13 @@
&fuzzer.fuzzPackagedModule.FuzzProperties)
}
+func (fuzzer *fuzzDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
+
+ out := fuzzer.binaryDecorator.compile(ctx, flags, deps)
+
+ return out
+}
+
func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage {
return RlibLinkage
}
@@ -94,86 +105,6 @@
return rlibAutoDep
}
-// Responsible for generating GNU Make rules that package fuzz targets into
-// their architecture & target/host specific zip file.
-type rustFuzzPackager struct {
- fuzz.FuzzPackager
-}
-
-func rustFuzzPackagingFactory() android.Singleton {
- return &rustFuzzPackager{}
-}
-
-func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
- // Map between each architecture + host/device combination.
- archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
-
- // List of individual fuzz targets.
- s.FuzzTargets = make(map[string]bool)
-
- // Map tracking whether each shared library has an install rule to avoid duplicate install rules from
- // multiple fuzzers that depend on the same shared library.
- sharedLibraryInstalled := make(map[string]bool)
-
- ctx.VisitAllModules(func(module android.Module) {
- // Discard non-fuzz targets.
- rustModule, ok := module.(*Module)
- if !ok {
- return
- }
-
- if ok := fuzz.IsValid(rustModule.FuzzModule); !ok || rustModule.Properties.PreventInstall {
- return
- }
-
- fuzzModule, ok := rustModule.compiler.(*fuzzDecorator)
- if !ok {
- return
- }
-
- hostOrTargetString := "target"
- if rustModule.Host() {
- hostOrTargetString = "host"
- }
-
- archString := rustModule.Arch().ArchType.String()
- archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
- archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
-
- var files []fuzz.FileToZip
- builder := android.NewRuleBuilder(pctx, ctx)
-
- // Package the artifacts (data, corpus, config and dictionary into a zipfile.
- files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
-
- // The executable.
- files = append(files, fuzz.FileToZip{rustModule.UnstrippedOutputFile(), ""})
-
- // Grab the list of required shared libraries.
- sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, cc.UnstrippedOutputFile, cc.IsValidSharedDependency)
-
- // Package shared libraries
- files = append(files, cc.GetSharedLibsToZip(sharedLibraries, rustModule, &s.FuzzPackager, archString, &sharedLibraryInstalled)...)
-
- archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
- if !ok {
- return
- }
-
- })
- s.CreateFuzzPackage(ctx, archDirs, fuzz.Rust, pctx)
-}
-
-func (s *rustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
- packages := s.Packages.Strings()
- sort.Strings(packages)
-
- ctx.Strict("SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
-
- // Preallocate the slice of fuzz targets to minimize memory allocations.
- s.PreallocateSlice(ctx, "ALL_RUST_FUZZ_TARGETS")
-}
-
func (fuzz *fuzzDecorator) install(ctx ModuleContext) {
fuzz.binaryDecorator.baseCompiler.dir = filepath.Join(
"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
@@ -181,13 +112,22 @@
"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
fuzz.binaryDecorator.baseCompiler.install(ctx)
- if fuzz.fuzzPackagedModule.FuzzProperties.Corpus != nil {
- fuzz.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Corpus)
- }
- if fuzz.fuzzPackagedModule.FuzzProperties.Data != nil {
- fuzz.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Data)
- }
- if fuzz.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
- fuzz.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzz.fuzzPackagedModule.FuzzProperties.Dictionary)
+ fuzz.fuzzPackagedModule = cc.PackageFuzzModule(ctx, fuzz.fuzzPackagedModule, pctx)
+
+ installBase := "fuzz"
+
+ // Grab the list of required shared libraries.
+ fuzz.sharedLibraries, _ = cc.CollectAllSharedDependencies(ctx)
+
+ for _, lib := range fuzz.sharedLibraries {
+ fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+ cc.SharedLibraryInstallLocation(
+ lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+
+ // Also add the dependency on the shared library symbols dir.
+ if !ctx.Host() {
+ fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+ cc.SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+ }
}
}
diff --git a/rust/image.go b/rust/image.go
index dfc7f74..50bf02a 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -129,6 +129,10 @@
return ctx.ModuleContext.DeviceSpecific() || (ctx.RustModule().InVendor() && ctx.RustModule().VendorVariantToOdm())
}
+func (ctx *moduleContext) SystemExtSpecific() bool {
+ return ctx.ModuleContext.SystemExtSpecific()
+}
+
// Returns true when this module creates a vendor variant and wants to install the vendor variant
// to the odm partition.
func (c *Module) VendorVariantToOdm() bool {
@@ -158,22 +162,15 @@
}
func (mod *Module) OnlyInRamdisk() bool {
- // TODO(b/165791368)
- return false
+ return mod.ModuleBase.InstallInRamdisk()
}
func (mod *Module) OnlyInRecovery() bool {
- // TODO(b/165791368)
- return false
+ return mod.ModuleBase.InstallInRecovery()
}
func (mod *Module) OnlyInVendorRamdisk() bool {
- return false
-}
-
-func (mod *Module) OnlyInProduct() bool {
- //TODO(b/165791368)
- return false
+ return mod.ModuleBase.InstallInVendorRamdisk()
}
// Returns true when this module is configured to have core and vendor variants.
@@ -226,10 +223,7 @@
// Rust does not support installing to the product image yet.
vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
- if mctx.ProductSpecific() {
- mctx.PropertyErrorf("product_specific",
- "Rust modules do not yet support installing to the product image.")
- } else if Bool(mod.VendorProperties.Double_loadable) {
+ if Bool(mod.VendorProperties.Double_loadable) {
mctx.PropertyErrorf("double_loadable",
"Rust modules do not yet support double loading")
}
@@ -243,6 +237,11 @@
mctx.PropertyErrorf("vendor", "Vendor-only dylibs are not yet supported, use rust_library_rlib.")
}
}
+ if mctx.ProductSpecific() {
+ if lib, ok := mod.compiler.(libraryInterface); ok && lib.buildDylib() {
+ mctx.PropertyErrorf("product", "Product-only dylibs are not yet supported, use rust_library_rlib.")
+ }
+ }
cc.MutateImage(mctx, mod)
diff --git a/rust/image_test.go b/rust/image_test.go
index 95e788f..fb4d9c1 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -53,6 +53,7 @@
crate_name: "foo",
srcs: ["foo.rs"],
vendor_available: true,
+ product_available: true,
}
`)
@@ -61,6 +62,35 @@
if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") {
t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
}
+ if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vendor'") {
+ t.Errorf("missing \"--cfg 'android_vendor'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
+ }
+ if strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_product'") {
+ t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
+ }
+
+ product := ctx.ModuleForTests("libfoo", "android_product.29_arm64_armv8-a_static").Rule("rustc")
+ if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vndk'") {
+ t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
+ }
+ if strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vendor'") {
+ t.Errorf("unexpected \"--cfg 'android_vendor'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
+ }
+ if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_product'") {
+ t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
+ }
+
+ system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Rule("rustc")
+ if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vndk'") {
+ t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
+ }
+ if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vendor'") {
+ t.Errorf("unexpected \"--cfg 'android_vendor'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
+ }
+ if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_product'") {
+ t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo system variant, rustcFlags: %#v", product.Args["rustcFlags"])
+ }
+
}
// Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries.
@@ -103,3 +133,93 @@
}
`)
}
+
+func checkInstallPartition(t *testing.T, ctx *android.TestContext, name, variant, expected string) {
+ mod := ctx.ModuleForTests(name, variant).Module().(*Module)
+ partitionDefined := false
+ checkPartition := func(specific bool, partition string) {
+ if specific {
+ if expected != partition && !partitionDefined {
+ // The variant is installed to the 'partition'
+ t.Errorf("%s variant of %q must not be installed to %s partition", variant, name, partition)
+ }
+ partitionDefined = true
+ } else {
+ // The variant is not installed to the 'partition'
+ if expected == partition {
+ t.Errorf("%s variant of %q must be installed to %s partition", variant, name, partition)
+ }
+ }
+ }
+ socSpecific := func(m *Module) bool {
+ return m.SocSpecific()
+ }
+ deviceSpecific := func(m *Module) bool {
+ return m.DeviceSpecific()
+ }
+ productSpecific := func(m *Module) bool {
+ return m.ProductSpecific() || m.productSpecificModuleContext()
+ }
+ systemExtSpecific := func(m *Module) bool {
+ return m.SystemExtSpecific()
+ }
+ checkPartition(socSpecific(mod), "vendor")
+ checkPartition(deviceSpecific(mod), "odm")
+ checkPartition(productSpecific(mod), "product")
+ checkPartition(systemExtSpecific(mod), "system_ext")
+ if !partitionDefined && expected != "system" {
+ t.Errorf("%s variant of %q is expected to be installed to %s partition,"+
+ " but installed to system partition", variant, name, expected)
+ }
+}
+
+func TestInstallPartition(t *testing.T) {
+ t.Parallel()
+ t.Helper()
+ ctx := testRust(t, `
+ rust_binary {
+ name: "sample_system",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ }
+ rust_binary {
+ name: "sample_system_ext",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ system_ext_specific: true,
+ }
+ rust_binary {
+ name: "sample_product",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ product_specific: true,
+ }
+ rust_binary {
+ name: "sample_vendor",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ vendor: true,
+ }
+ rust_binary {
+ name: "sample_odm",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ device_specific: true,
+ }
+ rust_binary {
+ name: "sample_all_available",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ vendor_available: true,
+ product_available: true,
+ }
+ `)
+
+ checkInstallPartition(t, ctx, "sample_system", binaryCoreVariant, "system")
+ checkInstallPartition(t, ctx, "sample_system_ext", binaryCoreVariant, "system_ext")
+ checkInstallPartition(t, ctx, "sample_product", binaryProductVariant, "product")
+ checkInstallPartition(t, ctx, "sample_vendor", binaryVendorVariant, "vendor")
+ checkInstallPartition(t, ctx, "sample_odm", binaryVendorVariant, "odm")
+
+ checkInstallPartition(t, ctx, "sample_all_available", binaryCoreVariant, "system")
+}
diff --git a/rust/library.go b/rust/library.go
index 62eaefd..a3a5672 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()))
}
@@ -271,84 +267,94 @@
var _ libraryInterface = (*libraryDecorator)(nil)
var _ exportedFlagsProducer = (*libraryDecorator)(nil)
-// rust_library produces all rust variants.
+// rust_library produces all Rust variants (rust_library_dylib and
+// rust_library_rlib).
func RustLibraryFactory() android.Module {
module, library := NewRustLibrary(android.HostAndDeviceSupported)
library.BuildOnlyRust()
return module.Init()
}
-// rust_ffi produces all ffi variants.
+// rust_ffi produces all FFI variants (rust_ffi_shared and
+// rust_ffi_static).
func RustFFIFactory() android.Module {
module, library := NewRustLibrary(android.HostAndDeviceSupported)
library.BuildOnlyFFI()
return module.Init()
}
-// rust_library_dylib produces a dylib.
+// rust_library_dylib produces a Rust dylib (Rust crate type "dylib").
func RustLibraryDylibFactory() android.Module {
module, library := NewRustLibrary(android.HostAndDeviceSupported)
library.BuildOnlyDylib()
return module.Init()
}
-// rust_library_rlib produces an rlib.
+// rust_library_rlib produces an rlib (Rust crate type "rlib").
func RustLibraryRlibFactory() android.Module {
module, library := NewRustLibrary(android.HostAndDeviceSupported)
library.BuildOnlyRlib()
return module.Init()
}
-// rust_ffi_shared produces a shared library.
+// rust_ffi_shared produces a shared library (Rust crate type
+// "cdylib").
func RustFFISharedFactory() android.Module {
module, library := NewRustLibrary(android.HostAndDeviceSupported)
library.BuildOnlyShared()
return module.Init()
}
-// rust_ffi_static produces a static library.
+// rust_ffi_static produces a static library (Rust crate type
+// "staticlib").
func RustFFIStaticFactory() android.Module {
module, library := NewRustLibrary(android.HostAndDeviceSupported)
library.BuildOnlyStatic()
return module.Init()
}
-// rust_library_host produces all rust variants.
+// rust_library_host produces all Rust variants for the host
+// (rust_library_dylib_host and rust_library_rlib_host).
func RustLibraryHostFactory() android.Module {
module, library := NewRustLibrary(android.HostSupported)
library.BuildOnlyRust()
return module.Init()
}
-// rust_ffi_host produces all FFI variants.
+// rust_ffi_host produces all FFI variants for the host
+// (rust_ffi_static_host and rust_ffi_shared_host).
func RustFFIHostFactory() android.Module {
module, library := NewRustLibrary(android.HostSupported)
library.BuildOnlyFFI()
return module.Init()
}
-// rust_library_dylib_host produces a dylib.
+// rust_library_dylib_host produces a dylib for the host (Rust crate
+// type "dylib").
func RustLibraryDylibHostFactory() android.Module {
module, library := NewRustLibrary(android.HostSupported)
library.BuildOnlyDylib()
return module.Init()
}
-// rust_library_rlib_host produces an rlib.
+// rust_library_rlib_host produces an rlib for the host (Rust crate
+// type "rlib").
func RustLibraryRlibHostFactory() android.Module {
module, library := NewRustLibrary(android.HostSupported)
library.BuildOnlyRlib()
return module.Init()
}
-// rust_ffi_static_host produces a static library.
+// rust_ffi_static_host produces a static library for the host (Rust
+// crate type "staticlib").
func RustFFIStaticHostFactory() android.Module {
module, library := NewRustLibrary(android.HostSupported)
library.BuildOnlyStatic()
return module.Init()
}
-// rust_ffi_shared_host produces an shared library.
+// rust_ffi_shared_host produces an shared library for the host (Rust
+// crate type "cdylib").
func RustFFISharedHostFactory() android.Module {
module, library := NewRustLibrary(android.HostSupported)
library.BuildOnlyShared()
@@ -474,8 +480,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 +494,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) {
@@ -513,7 +520,7 @@
flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
- flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
+ flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects.Strings()...)
if library.dylib() {
// We need prefer-dynamic for now to avoid linking in the static stdlib. See:
@@ -524,18 +531,19 @@
// 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() {
library.flagExporter.exportLinkDirs(deps.linkDirs...)
library.flagExporter.exportLinkObjects(deps.linkObjects...)
+ library.flagExporter.exportLibDeps(deps.LibDeps...)
}
if library.static() || library.shared() {
@@ -572,7 +580,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]
diff --git a/rust/library_test.go b/rust/library_test.go
index 4633cc7..d4b525f 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -30,11 +30,11 @@
srcs: ["foo.rs"],
crate_name: "foo",
}
- rust_ffi_host {
- name: "libfoo.ffi",
- srcs: ["foo.rs"],
- crate_name: "foo"
- }`)
+ rust_ffi_host {
+ name: "libfoo.ffi",
+ srcs: ["foo.rs"],
+ crate_name: "foo"
+ }`)
// Test all variants are being built.
libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
@@ -45,7 +45,7 @@
rlibCrateType := "rlib"
dylibCrateType := "dylib"
sharedCrateType := "cdylib"
- staticCrateType := "static"
+ staticCrateType := "staticlib"
// Test crate type for rlib is correct.
if !strings.Contains(libfooRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) {
@@ -148,7 +148,7 @@
libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared")
- libfooOutput := libfoo.Rule("rustc")
+ libfooOutput := libfoo.Rule("rustLink")
if !strings.Contains(libfooOutput.Args["linkFlags"], "-Wl,-soname=libfoo.so") {
t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v",
libfooOutput.Args["linkFlags"])
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 6cdd07d..fe9d0b5 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -145,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)
@@ -154,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,
@@ -202,7 +202,7 @@
&prebuilt.Properties)
}
-func (prebuilt *prebuiltProcMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+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)
@@ -211,7 +211,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 *prebuiltProcMacroDecorator) rustdoc(ctx ModuleContext, flags Flags,
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index f8a4bbd..832b62c 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -70,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 {
diff --git a/rust/protobuf.go b/rust/protobuf.go
index 9fe27c4c..e30f25d 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -73,7 +73,7 @@
outDir := android.PathForModuleOut(ctx)
protoFiles := android.PathsForModuleSrc(ctx, proto.Properties.Protos)
grpcFiles := android.PathsForModuleSrc(ctx, proto.Properties.Grpc_protos)
- protoPluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
+ protoPluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust-deprecated")
commonProtoFlags = append(commonProtoFlags, defaultProtobufFlags...)
commonProtoFlags = append(commonProtoFlags, proto.Properties.Proto_flags...)
@@ -206,7 +206,7 @@
func (proto *protobufDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
deps = proto.BaseSourceProvider.SourceProviderDeps(ctx, deps)
- deps.Rustlibs = append(deps.Rustlibs, "libprotobuf")
+ deps.Rustlibs = append(deps.Rustlibs, "libprotobuf_deprecated")
deps.HeaderLibs = append(deps.SharedLibs, proto.Properties.Header_libs...)
if len(proto.Properties.Grpc_protos) > 0 {
@@ -238,7 +238,7 @@
Properties: ProtobufProperties{},
}
- module := NewSourceProviderModule(hod, protobuf, false)
+ module := NewSourceProviderModule(hod, protobuf, false, false)
return module, protobuf
}
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index f0f5ec0..0aa4549 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -42,14 +42,14 @@
`)
// Check that libprotobuf is added as a dependency.
librust_proto := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_dylib").Module().(*Module)
- if !android.InList("libprotobuf", librust_proto.Properties.AndroidMkDylibs) {
- t.Errorf("libprotobuf dependency missing for rust_protobuf (dependency missing from AndroidMkDylibs)")
+ if !android.InList("libprotobuf_deprecated", librust_proto.Properties.AndroidMkDylibs) {
+ t.Errorf("libprotobuf_deprecated dependency missing for rust_protobuf (dependency missing from AndroidMkDylibs)")
}
// Make sure the correct plugin is being used.
librust_proto_out := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").Output("buf.rs")
cmd := librust_proto_out.RuleParams.Command
- if w := "protoc-gen-rust"; !strings.Contains(cmd, w) {
+ if w := "protoc-gen-rust-deprecated"; !strings.Contains(cmd, w) {
t.Errorf("expected %q in %q", w, cmd)
}
diff --git a/rust/rust.go b/rust/rust.go
index 7c644dd..7b520cd 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -15,6 +15,7 @@
package rust
import (
+ "android/soong/bloaty"
"fmt"
"strings"
@@ -22,10 +23,10 @@
"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/multitree"
"android/soong/rust/config"
"android/soong/snapshot"
)
@@ -33,25 +34,18 @@
var pctx = android.NewPackageContext("android/soong/rust")
func init() {
- // Only allow rust modules to be defined for certain projects
-
- android.AddNeverAllowRules(
- android.NeverAllow().
- NotIn(append(config.RustAllowedPaths, config.DownstreamRustAllowedPaths...)...).
- ModuleType(config.RustModuleTypes...))
-
android.RegisterModuleType("rust_defaults", defaultsFactory)
android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
ctx.BottomUp("rust_begin", BeginMutator).Parallel()
-
})
android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
})
pctx.Import("android/soong/rust/config")
pctx.ImportAs("cc_config", "android/soong/cc/config")
+ android.InitRegistrationContext.RegisterSingletonType("kythe_rust_extract", kytheExtractRustFactory)
}
type Flags struct {
@@ -64,14 +58,15 @@
Toolchain config.Toolchain
Coverage bool
Clippy bool
+ EmitXrefs bool // If true, emit rules to aid cross-referencing
}
type BaseProperties struct {
- AndroidMkRlibs []string
- AndroidMkDylibs []string
- AndroidMkProcMacroLibs []string
- AndroidMkSharedLibs []string
- AndroidMkStaticLibs []string
+ AndroidMkRlibs []string `blueprint:"mutated"`
+ AndroidMkDylibs []string `blueprint:"mutated"`
+ AndroidMkProcMacroLibs []string `blueprint:"mutated"`
+ AndroidMkSharedLibs []string `blueprint:"mutated"`
+ AndroidMkStaticLibs []string `blueprint:"mutated"`
ImageVariationPrefix string `blueprint:"mutated"`
VndkVersion string `blueprint:"mutated"`
@@ -161,6 +156,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
@@ -210,6 +208,11 @@
}
return android.Paths{}, nil
}
+ case "unstripped":
+ if mod.compiler != nil {
+ return android.PathsIfNonNil(mod.compiler.unstrippedOutputFilePath()), nil
+ }
+ return nil, nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
@@ -394,6 +397,10 @@
return false
}
+func (mod *Module) XrefRustFiles() android.Paths {
+ return mod.kytheFiles
+}
+
type Deps struct {
Dylibs []string
Rlibs []string
@@ -413,13 +420,12 @@
}
type PathDeps struct {
- DyLibs RustLibraries
- RLibs RustLibraries
- SharedLibs android.Paths
- SharedLibDeps android.Paths
- StaticLibs android.Paths
- ProcMacros RustLibraries
- AfdoProfiles android.Paths
+ DyLibs RustLibraries
+ RLibs RustLibraries
+ LibDeps android.Paths
+ WholeStaticLibs android.Paths
+ ProcMacros RustLibraries
+ AfdoProfiles android.Paths
// depFlags and depLinkFlags are rustc and linker (clang) flags.
depFlags []string
@@ -428,7 +434,7 @@
// linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker.
// Both of these are exported and propagate to dependencies.
linkDirs []string
- linkObjects []string
+ linkObjects android.Paths
// Used by bindgen modules which call clang
depClangFlags []string
@@ -457,7 +463,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
@@ -483,6 +489,7 @@
SetDisabled()
stdLinkage(ctx *depsContext) RustLinkage
+ noStdlibs() bool
unstrippedOutputFilePath() android.Path
strippedOutputFilePath() android.OptionalPath
@@ -490,26 +497,36 @@
type exportedFlagsProducer interface {
exportLinkDirs(...string)
- exportLinkObjects(...string)
+ exportLinkObjects(...android.Path)
+}
+
+type xref interface {
+ XrefRustFiles() android.Paths
}
type flagExporter struct {
linkDirs []string
- linkObjects []string
+ linkObjects android.Paths
+ libDeps android.Paths
}
func (flagExporter *flagExporter) exportLinkDirs(dirs ...string) {
flagExporter.linkDirs = android.FirstUniqueStrings(append(flagExporter.linkDirs, dirs...))
}
-func (flagExporter *flagExporter) exportLinkObjects(flags ...string) {
- flagExporter.linkObjects = android.FirstUniqueStrings(append(flagExporter.linkObjects, flags...))
+func (flagExporter *flagExporter) exportLinkObjects(flags ...android.Path) {
+ flagExporter.linkObjects = android.FirstUniquePaths(append(flagExporter.linkObjects, flags...))
+}
+
+func (flagExporter *flagExporter) exportLibDeps(paths ...android.Path) {
+ flagExporter.libDeps = android.FirstUniquePaths(append(flagExporter.libDeps, paths...))
}
func (flagExporter *flagExporter) setProvider(ctx ModuleContext) {
ctx.SetProvider(FlagExporterInfoProvider, FlagExporterInfo{
LinkDirs: flagExporter.linkDirs,
LinkObjects: flagExporter.linkObjects,
+ LibDeps: flagExporter.libDeps,
})
}
@@ -522,7 +539,8 @@
type FlagExporterInfo struct {
Flags []string
LinkDirs []string // TODO: this should be android.Paths
- LinkObjects []string // TODO: this should be android.Paths
+ LinkObjects android.Paths
+ LibDeps android.Paths
}
var FlagExporterInfoProvider = blueprint.NewProvider(FlagExporterInfo{})
@@ -613,6 +631,31 @@
return false
}
+func (mod *Module) IsFuzzModule() bool {
+ if _, ok := mod.compiler.(*fuzzDecorator); ok {
+ return true
+ }
+ return false
+}
+
+func (mod *Module) FuzzModuleStruct() fuzz.FuzzModule {
+ return mod.FuzzModule
+}
+
+func (mod *Module) FuzzPackagedModule() fuzz.FuzzPackagedModule {
+ if fuzzer, ok := mod.compiler.(*fuzzDecorator); ok {
+ return fuzzer.fuzzPackagedModule
+ }
+ panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) FuzzSharedLibraries() android.Paths {
+ if fuzzer, ok := mod.compiler.(*fuzzDecorator); ok {
+ return fuzzer.sharedLibraries
+ }
+ panic(fmt.Errorf("FuzzSharedLibraries called on non-fuzz module: %q", mod.BaseModuleName()))
+}
+
func (mod *Module) UnstrippedOutputFile() android.Path {
if mod.compiler != nil {
return mod.compiler.unstrippedOutputFilePath()
@@ -882,7 +925,7 @@
// Calculate rustc flags
if mod.afdo != nil {
- flags, deps = mod.afdo.flags(ctx, flags, deps)
+ flags, deps = mod.afdo.flags(actx, flags, deps)
}
if mod.compiler != nil {
flags = mod.compiler.compilerFlags(ctx, flags)
@@ -917,11 +960,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)
@@ -1071,6 +1117,17 @@
return nil
}
+func rustMakeLibName(ctx android.ModuleContext, c cc.LinkableInterface, dep cc.LinkableInterface, depName string) string {
+ if rustDep, ok := dep.(*Module); ok {
+ // Use base module name for snapshots when exporting to Makefile.
+ if snapshotPrebuilt, ok := rustDep.compiler.(cc.SnapshotInterface); ok {
+ baseName := rustDep.BaseModuleName()
+ return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix() + rustDep.AndroidMkSuffix()
+ }
+ }
+ return cc.MakeLibName(ctx, c, dep, depName)
+}
+
func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
var depPaths PathDeps
@@ -1096,13 +1153,59 @@
mod.apexSdkVersion = android.FutureApiLevel
}
+ skipModuleList := map[string]bool{}
+
+ var apiImportInfo multitree.ApiImportInfo
+ hasApiImportInfo := false
+
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ if dep.Name() == "api_imports" {
+ apiImportInfo = ctx.OtherModuleProvider(dep, multitree.ApiImportsProvider).(multitree.ApiImportInfo)
+ hasApiImportInfo = true
+ }
+ })
+
+ if hasApiImportInfo {
+ targetStubModuleList := map[string]string{}
+ targetOrigModuleList := map[string]string{}
+
+ // Search for dependency which both original module and API imported library with APEX stub exists
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok {
+ targetStubModuleList[apiLibrary] = depName
+ }
+ })
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ if origLibrary, ok := targetStubModuleList[depName]; ok {
+ targetOrigModuleList[origLibrary] = depName
+ }
+ })
+
+ // Decide which library should be used between original and API imported library
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ if apiLibrary, ok := targetOrigModuleList[depName]; ok {
+ if cc.ShouldUseStubForApex(ctx, dep) {
+ skipModuleList[depName] = true
+ } else {
+ skipModuleList[apiLibrary] = true
+ }
+ }
+ })
+ }
+
ctx.VisitDirectDeps(func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
depTag := ctx.OtherModuleDependencyTag(dep)
+ if _, exists := skipModuleList[depName]; exists {
+ return
+ }
if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
//Handle Rust Modules
- makeLibName := cc.MakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
+ makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
switch depTag {
case dylibDepTag:
@@ -1153,6 +1256,7 @@
depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...)
depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...)
+ depPaths.LibDeps = append(depPaths.LibDeps, exportedInfo.LibDeps...)
}
if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
@@ -1196,6 +1300,7 @@
depPaths.depLinkFlags = append(depPaths.depLinkFlags, []string{"-Wl,--whole-archive", linkObject.Path().String(), "-Wl,--no-whole-archive"}...)
} else if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
+ depPaths.WholeStaticLibs = append(depPaths.WholeStaticLibs, linkObject.Path())
} else {
ctx.ModuleErrorf("'%q' cannot be listed as a whole_static_library in Rust modules unless the output is prefixed by 'lib'", depName, ctx.ModuleName())
}
@@ -1203,7 +1308,7 @@
// Add this to linkObjects to pass the library directly to the linker as well. This propagates
// to dependencies to avoid having to redeclare static libraries for dependents of the dylib variant.
- depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
+ depPaths.linkObjects = append(depPaths.linkObjects, linkObject.AsPaths()...)
depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
@@ -1229,7 +1334,7 @@
linkPath = linkPathFromFilePath(linkObject.Path())
depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
- depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
+ depPaths.linkObjects = append(depPaths.linkObjects, linkObject.AsPaths()...)
depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
@@ -1255,7 +1360,9 @@
// Make sure these dependencies are propagated
if lib, ok := mod.compiler.(exportedFlagsProducer); ok && exportDep {
lib.exportLinkDirs(linkPath)
- lib.exportLinkObjects(linkObject.String())
+ if linkObject.Valid() {
+ lib.exportLinkObjects(linkObject.Path())
+ }
}
} else {
switch {
@@ -1287,19 +1394,16 @@
procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
}
- var staticLibDepFiles android.Paths
+ var libDepFiles android.Paths
for _, dep := range directStaticLibDeps {
- staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
+ libDepFiles = append(libDepFiles, dep.OutputFile().Path())
}
- var sharedLibFiles android.Paths
- var sharedLibDepFiles android.Paths
for _, dep := range directSharedLibDeps {
- sharedLibFiles = append(sharedLibFiles, dep.SharedLibrary)
if dep.TableOfContents.Valid() {
- sharedLibDepFiles = append(sharedLibDepFiles, dep.TableOfContents.Path())
+ libDepFiles = append(libDepFiles, dep.TableOfContents.Path())
} else {
- sharedLibDepFiles = append(sharedLibDepFiles, dep.SharedLibrary)
+ libDepFiles = append(libDepFiles, dep.SharedLibrary)
}
}
@@ -1315,15 +1419,13 @@
depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
- depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibDepFiles...)
- depPaths.SharedLibDeps = append(depPaths.SharedLibDeps, sharedLibDepFiles...)
- depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
+ depPaths.LibDeps = append(depPaths.LibDeps, libDepFiles...)
depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
depPaths.SrcDeps = append(depPaths.SrcDeps, srcProviderDepFiles...)
// Dedup exported flags from dependencies
depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
- depPaths.linkObjects = android.FirstUniqueStrings(depPaths.linkObjects)
+ depPaths.linkObjects = android.FirstUniquePaths(depPaths.linkObjects)
depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
@@ -1355,6 +1457,16 @@
return strings.Split(filepath.String(), filepath.Base())[0]
}
+// usePublicApi returns true if the rust variant should link against NDK (publicapi)
+func (r *Module) usePublicApi() bool {
+ return r.Device() && r.UseSdk()
+}
+
+// useVendorApi returns true if the rust variant should link against LLNDK (vendorapi)
+func (r *Module) useVendorApi() bool {
+ return r.Device() && (r.InVendor() || r.InProduct())
+}
+
func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
ctx := &depsContext{
BottomUpMutatorContext: actx,
@@ -1364,6 +1476,13 @@
var commonDepVariations []blueprint.Variation
var snapshotInfo *cc.SnapshotInfo
+ apiImportInfo := cc.GetApiImports(mod, actx)
+ if mod.usePublicApi() || mod.useVendorApi() {
+ for idx, lib := range deps.SharedLibs {
+ deps.SharedLibs[idx] = cc.GetReplaceModuleName(lib, apiImportInfo.SharedLibs)
+ }
+ }
+
if ctx.Os() == android.Android {
deps.SharedLibs, _ = cc.RewriteLibs(mod, &snapshotInfo, actx, ctx.Config(), deps.SharedLibs)
}
@@ -1384,7 +1503,7 @@
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)
+ lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
actx.AddVariationDependencies(rlibDepVariations, depTag, lib)
}
@@ -1401,7 +1520,7 @@
for _, lib := range deps.Rustlibs {
if autoDep.depTag == rlibDepTag {
// Handle the rlib deptag case
- addRlibDependency(actx, lib, mod, snapshotInfo, rlibDepVariations)
+ 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,
@@ -1412,7 +1531,7 @@
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)
+ addRlibDependency(actx, lib, mod, &snapshotInfo, rlibDepVariations)
}
}
}
@@ -1422,7 +1541,7 @@
if mod.compiler.stdLinkage(ctx) == RlibLinkage {
for _, lib := range deps.Stdlibs {
depTag := rlibDepTag
- lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+ lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
depTag, lib)
@@ -1441,12 +1560,20 @@
variations := []blueprint.Variation{
{Mutator: "link", Variation: "shared"},
}
- cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
+ // For core variant, add a dep on the implementation (if it exists) and its .apiimport (if it exists)
+ // GenerateAndroidBuildActions will pick the correct impl/stub based on the api_domain boundary
+ if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) {
+ cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
+ }
+
+ if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok {
+ cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, apiLibraryName, version, false)
+ }
}
for _, lib := range deps.WholeStaticLibs {
depTag := cc.StaticDepTag(true)
- lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
+ lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -1455,7 +1582,7 @@
for _, lib := range deps.StaticLibs {
depTag := cc.StaticDepTag(false)
- lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
+ lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -1467,11 +1594,11 @@
crtVariations := cc.GetCrtVariations(ctx, mod)
for _, crt := range deps.CrtBegin {
actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag,
- cc.RewriteSnapshotLib(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+ cc.GetReplaceModuleName(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
}
for _, crt := range deps.CrtEnd {
actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag,
- cc.RewriteSnapshotLib(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+ cc.GetReplaceModuleName(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
}
if mod.sourceProvider != nil {
@@ -1490,11 +1617,13 @@
// proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy.
actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
+
+ mod.afdo.addDep(ctx, actx)
}
// 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)
+func addRlibDependency(actx android.BottomUpMutatorContext, lib string, mod *Module, snapshotInfo **cc.SnapshotInfo, variations []blueprint.Variation) {
+ lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, snapshotInfo, actx).Rlibs)
actx.AddVariationDependencies(variations, rlibDepTag, lib)
}
@@ -1601,7 +1730,7 @@
}
}
- if depTag == procMacroDepTag {
+ if depTag == procMacroDepTag || depTag == customBindgenDepTag {
return false
}
@@ -1631,6 +1760,29 @@
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...)
+ }
+}
+
+func (c *Module) Partition() string {
+ return ""
+}
+
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var String = proptools.String
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 97bd541..2a38b89 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -83,6 +83,10 @@
rlibVendorVariant = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
sharedRecoveryVariant = "android_recovery_arm64_armv8-a_shared"
rlibRecoveryVariant = "android_recovery_arm64_armv8-a_rlib_rlib-std"
+ binaryCoreVariant = "android_arm64_armv8-a"
+ binaryVendorVariant = "android_vendor.29_arm64_armv8-a"
+ binaryProductVariant = "android_product.29_arm64_armv8-a"
+ binaryRecoveryVariant = "android_recovery_arm64_armv8-a"
)
func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
@@ -205,6 +209,10 @@
// Test to make sure dependencies are being picked up correctly.
func TestDepsTracking(t *testing.T) {
ctx := testRust(t, `
+ cc_library {
+ host_supported: true,
+ name: "cc_stubs_dep",
+ }
rust_ffi_host_static {
name: "libstatic",
srcs: ["foo.rs"],
@@ -231,6 +239,7 @@
crate_name: "rlib",
static_libs: ["libstatic"],
whole_static_libs: ["libwholestatic"],
+ shared_libs: ["cc_stubs_dep"],
}
rust_proc_macro {
name: "libpm",
@@ -249,6 +258,7 @@
`)
module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
rustc := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
+ rustLink := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Rule("rustLink")
// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
if !android.InList("libdylib", module.Properties.AndroidMkDylibs) {
@@ -275,6 +285,17 @@
t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"])
}
+ if !strings.Contains(rustLink.Args["linkFlags"], "cc_stubs_dep.so") {
+ t.Errorf("shared cc_library not being passed to rustc linkFlags %#v", rustLink.Args["linkFlags"])
+ }
+
+ if !android.SuffixInList(rustLink.OrderOnly.Strings(), "cc_stubs_dep.so") {
+ t.Errorf("shared cc dep not being passed as order-only to rustc %#v", rustLink.OrderOnly.Strings())
+ }
+
+ if !android.SuffixInList(rustLink.Implicits.Strings(), "cc_stubs_dep.so.toc") {
+ t.Errorf("shared cc dep TOC not being passed as implicit to rustc %#v", rustLink.Implicits.Strings())
+ }
}
func TestSourceProviderDeps(t *testing.T) {
@@ -327,7 +348,7 @@
source_stem: "bindings",
host_supported: true,
wrapper_src: "src/any.h",
- }
+ }
`)
libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
@@ -367,7 +388,6 @@
if !android.InList("libbindings.rlib-std", libprocmacroMod.Properties.AndroidMkRlibs) {
t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
}
-
}
func TestSourceProviderTargetMismatch(t *testing.T) {
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 39aaf33..c68137e 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -50,7 +50,6 @@
}
}
SanitizerEnabled bool `blueprint:"mutated"`
- SanitizeDep bool `blueprint:"mutated"`
// Used when we need to place libraries in their own directory, such as ASAN.
InSanitizerDir bool `blueprint:"mutated"`
@@ -175,7 +174,7 @@
}
// Enable Memtag for all components in the include paths (for Aarch64 only)
- if ctx.Arch().ArchType == android.Arm64 {
+ if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() {
if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
if s.Memtag_heap == nil {
s.Memtag_heap = proptools.BoolPtr(true)
@@ -190,18 +189,8 @@
}
}
- // TODO:(b/178369775)
- // For now sanitizing is only supported on devices
- if ctx.Os() == android.Android && Bool(s.Fuzzer) {
- sanitize.Properties.SanitizerEnabled = true
- }
-
- if ctx.Os() == android.Android && Bool(s.Address) {
- sanitize.Properties.SanitizerEnabled = true
- }
-
// HWASan requires AArch64 hardware feature (top-byte-ignore).
- if ctx.Arch().ArchType != android.Arm64 {
+ if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
s.Hwaddress = nil
}
@@ -216,11 +205,13 @@
}
// Memtag_heap is only implemented on AArch64.
- if ctx.Arch().ArchType != android.Arm64 {
+ if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
s.Memtag_heap = nil
}
- if ctx.Os() == android.Android && (Bool(s.Hwaddress) || Bool(s.Address) || Bool(s.Memtag_heap)) {
+ // TODO:(b/178369775)
+ // For now sanitizing is only supported on devices
+ if ctx.Os() == android.Android && (Bool(s.Hwaddress) || Bool(s.Address) || Bool(s.Memtag_heap) || Bool(s.Fuzzer)) {
sanitize.Properties.SanitizerEnabled = true
}
}
@@ -235,7 +226,7 @@
}
if Bool(sanitize.Properties.Sanitize.Fuzzer) {
flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
- if ctx.Arch().ArchType == android.Arm64 {
+ if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() {
flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
} else {
flags.RustFlags = append(flags.RustFlags, asanFlags...)
@@ -283,13 +274,13 @@
var deps []string
if mod.IsSanitizerEnabled(cc.Asan) ||
- (mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType != android.Arm64) {
+ (mod.IsSanitizerEnabled(cc.Fuzzer) && (mctx.Arch().ArchType != android.Arm64 || !mctx.Os().Bionic())) {
variations = append(variations,
blueprint.Variation{Mutator: "link", Variation: "shared"})
depTag = cc.SharedDepTag()
deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
} else if mod.IsSanitizerEnabled(cc.Hwasan) ||
- (mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64) {
+ (mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64 && mctx.Os().Bionic()) {
// TODO(b/204776996): HWASan for static Rust binaries isn't supported yet.
if binary, ok := mod.compiler.(binaryInterface); ok {
if binary.staticallyLinked() {
@@ -444,20 +435,12 @@
return mod.sanitize.isSanitizerExplicitlyDisabled(t)
}
-func (mod *Module) SanitizeDep() bool {
- return mod.sanitize.Properties.SanitizeDep
-}
-
func (mod *Module) SetSanitizer(t cc.SanitizerType, b bool) {
if !Bool(mod.sanitize.Properties.Sanitize.Never) {
mod.sanitize.SetSanitizer(t, b)
}
}
-func (mod *Module) SetSanitizeDep(b bool) {
- mod.sanitize.Properties.SanitizeDep = b
-}
-
func (mod *Module) StaticallyLinked() bool {
if lib, ok := mod.compiler.(libraryInterface); ok {
return lib.rlib() || lib.static()
diff --git a/rust/sanitize_test.go b/rust/sanitize_test.go
index d6a14b2..43e95f4 100644
--- a/rust/sanitize_test.go
+++ b/rust/sanitize_test.go
@@ -35,7 +35,7 @@
note_sync := "note_memtag_heap_sync"
found := None
- implicits := m.Rule("rustc").Implicits
+ implicits := m.Rule("rustLink").Implicits
for _, lib := range implicits {
if strings.Contains(lib.Rel(), note_async) {
found = Async
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/source_provider.go b/rust/source_provider.go
index 7719611..3236bce 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -31,9 +31,8 @@
Properties SourceProviderProperties
// The first file in OutputFiles must be the library entry point.
- OutputFiles android.Paths
- subAndroidMkOnce map[SubAndroidMkProvider]bool
- subName string
+ OutputFiles android.Paths
+ subName string
}
var _ SourceProvider = (*BaseSourceProvider)(nil)
@@ -65,9 +64,12 @@
}
}
-func NewSourceProviderModule(hod android.HostOrDeviceSupported, sourceProvider SourceProvider, enableLints bool) *Module {
+func NewSourceProviderModule(hod android.HostOrDeviceSupported, sourceProvider SourceProvider, enableLints bool, rlibOnly bool) *Module {
_, library := NewRustLibrary(hod)
library.BuildOnlyRust()
+ if rlibOnly {
+ library.BuildOnlyRlib()
+ }
library.sourceProvider = sourceProvider
module := newModule(hod, android.MultilibBoth)
diff --git a/rust/test.go b/rust/test.go
index 250b765..4b5296e 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"
@@ -22,12 +24,6 @@
"android/soong/tradefed"
)
-// Test option struct.
-type TestOptions struct {
- // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
- Unit_test *bool
-}
-
type TestProperties struct {
// Disables the creation of a test-specific directory when used with
// relative_install_path. Useful if several tests need to be in the same
@@ -65,7 +61,7 @@
Test_harness *bool
// Test options.
- Test_options TestOptions
+ Test_options android.CommonTestOptions
// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
// with root permission.
@@ -134,13 +130,16 @@
configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
}
- test.testConfig = tradefed.AutoGenRustTestConfig(ctx,
- test.Properties.Test_config,
- test.Properties.Test_config_template,
- test.Properties.Test_suites,
- configs,
- test.Properties.Auto_gen_config,
- testInstallBase)
+ test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: test.Properties.Test_config,
+ TestConfigTemplateProp: test.Properties.Test_config_template,
+ TestSuites: test.Properties.Test_suites,
+ Config: configs,
+ AutoGenConfig: test.Properties.Auto_gen_config,
+ TestInstallBase: testInstallBase,
+ DeviceTemplate: "${RustDeviceTestConfigTemplate}",
+ HostTemplate: "${RustHostTestConfigTemplate}",
+ })
dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
@@ -151,9 +150,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())})
}
})
@@ -195,6 +200,7 @@
if ctx.Device() {
flags.RustFlags = append(flags.RustFlags, "-Z panic_abort_tests")
}
+
return flags
}
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 cb98bed..a33d948 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -104,6 +104,7 @@
crate_name: "std",
srcs: ["foo.rs"],
no_stdlibs: true,
+ product_available: true,
host_supported: true,
vendor_available: true,
vendor_ramdisk_available: true,
@@ -126,7 +127,7 @@
min_sdk_version: "29",
}
rust_library {
- name: "libprotobuf",
+ name: "libprotobuf_deprecated",
crate_name: "protobuf",
srcs: ["foo.rs"],
host_supported: true,
@@ -194,6 +195,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 7be0042..2e7a330 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -424,6 +424,14 @@
compile_multilib: "32",
srcs: ["bin.rs"],
}
+
+ rust_library {
+ name: "librust_vendor_available",
+ crate_name: "rust_vendor",
+ vendor_available: true,
+ srcs: ["client.rs"],
+ }
+
`
vndkBp := `
@@ -499,13 +507,6 @@
system_shared_libs: [],
}
- rust_library {
- name: "librust_vendor_available",
- crate_name: "rust_vendor",
- vendor_available: true,
- srcs: ["client.rs"],
- }
-
rust_ffi_shared {
name: "libclient",
crate_name: "client",
@@ -940,7 +941,7 @@
ctx := testRustVndkFsVersions(t, "", mockFS, "30", "current", "31")
// libclient uses libvndk.vndk.30.arm64, libvendor.vendor_static.30.arm64, libvendor_without_snapshot
- libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustc").Args["linkFlags"]
+ libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustLink").Args["linkFlags"]
for _, input := range [][]string{
[]string{sharedVariant, "libvndk.vndk.30.arm64"},
[]string{staticVariant, "libvendor.vendor_static.30.arm64"},
@@ -963,7 +964,7 @@
}
libclientAndroidMkRlibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkRlibs
- if g, w := libclientAndroidMkRlibs, []string{"librust_vendor_available.vendor_rlib.30.arm64.rlib-std", "libstd.vendor_rlib.30.arm64"}; !reflect.DeepEqual(g, w) {
+ if g, w := libclientAndroidMkRlibs, []string{"librust_vendor_available.vendor.rlib-std", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
t.Errorf("wanted libclient libclientAndroidMkRlibs %q, got %q", w, g)
}
@@ -978,11 +979,25 @@
}
libclientRustAndroidMkRlibs := ctx.ModuleForTests("libclient_rust", rlibVariant).Module().(*Module).Properties.AndroidMkRlibs
- if g, w := libclientRustAndroidMkRlibs, []string{"librust_vendor_available.vendor_rlib.30.arm64.rlib-std", "libstd.vendor_rlib.30.arm64"}; !reflect.DeepEqual(g, w) {
+ if g, w := libclientRustAndroidMkRlibs, []string{"librust_vendor_available.vendor.rlib-std", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
t.Errorf("wanted libclient libclientAndroidMkRlibs %q, got %q", w, g)
}
- binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustc").Args["linkFlags"]
+ // rust vendor snapshot must have ".vendor" suffix in AndroidMk
+ librustVendorAvailableSnapshotModule := ctx.ModuleForTests("librust_vendor_available.vendor_rlib.30.arm64", rlibVariant).Module()
+ librustVendorSnapshotMkName := android.AndroidMkEntriesForTest(t, ctx, librustVendorAvailableSnapshotModule)[0].EntryMap["LOCAL_MODULE"][0]
+ expectedRustVendorSnapshotName := "librust_vendor_available.vendor.rlib-std"
+ if librustVendorSnapshotMkName != expectedRustVendorSnapshotName {
+ t.Errorf("Unexpected rust vendor snapshot name in AndroidMk: %q, expected: %q\n", librustVendorSnapshotMkName, expectedRustVendorSnapshotName)
+ }
+
+ rustVendorBinModule := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Module()
+ rustVendorBinMkRlibName := android.AndroidMkEntriesForTest(t, ctx, rustVendorBinModule)[0].EntryMap["LOCAL_RLIB_LIBRARIES"][0]
+ if rustVendorBinMkRlibName != expectedRustVendorSnapshotName {
+ t.Errorf("Unexpected rust rlib name in AndroidMk: %q, expected: %q\n", rustVendorBinMkRlibName, expectedRustVendorSnapshotName)
+ }
+
+ binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustLink").Args["linkFlags"]
libVndkStaticOutputPaths := cc.GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.30.arm64"})
if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 814bd57..9367ff0 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -168,7 +168,7 @@
}
python_binary_host {
- name: "gen-kotlin-build-file.py",
+ name: "gen-kotlin-build-file",
main: "gen-kotlin-build-file.py",
srcs: [
"gen-kotlin-build-file.py",
@@ -189,6 +189,18 @@
libs: [
"linker_config_proto",
],
+ visibility: ["//system/linkerconfig"],
+}
+
+python_test_host {
+ name: "conv_linker_config_test",
+ main: "conv_linker_config_test.py",
+ srcs: [
+ "conv_linker_config_test.py",
+ "conv_linker_config.py",
+ ],
+ libs: ["linker_config_proto"],
+ test_suites: ["general-tests"],
}
python_binary_host {
@@ -199,6 +211,17 @@
],
}
+python_binary_host {
+ name: "build-apex-bundle",
+ main: "build-apex-bundle.py",
+ srcs: [
+ "build-apex-bundle.py",
+ ],
+ required: [
+ "bundletool",
+ ],
+}
+
sh_binary_host {
name: "list_image",
src: "list_image.sh",
@@ -209,3 +232,8 @@
srcs: ["rustfmt.toml"],
visibility: ["//visibility:public"],
}
+
+sh_binary_host {
+ name: "jars-to-module-info-java",
+ src: "jars-to-module-info-java.sh",
+}
diff --git a/scripts/OWNERS b/scripts/OWNERS
deleted file mode 100644
index 3f4f9c0..0000000
--- a/scripts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
-per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
-per-file construct_context.py = ngeoffray@google.com,calin@google.com,skvadrik@google.com
-per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com
-per-file gen_ndk*.sh,gen_java*.sh = sophiez@google.com, allenhair@google.com
\ No newline at end of file
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
deleted file mode 100755
index 1a16f7c..0000000
--- a/scripts/build-aml-prebuilts.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/bash -e
-
-# This script is similar to "m" but builds in --soong-only mode, and handles
-# special cases to make that mode work. All arguments are passed on to
-# build/soong/soong_ui.bash.
-#
-# --soong-only bypasses the kati step and hence the make logic that e.g. doesn't
-# handle more than two device architectures. It is particularly intended for use
-# with TARGET_PRODUCT=mainline_sdk to build 'sdk' and 'module_export' Soong
-# modules in TARGET_ARCH_SUITE=mainline_sdk mode so that they get all four
-# device architectures (artifacts get installed in $OUT_DIR/soong/mainline-sdks
-# - cf PathForMainlineSdksInstall in android/paths.go).
-#
-# TODO(b/174315599): Replace this script completely with a 'soong_ui.bash
-# --soong-only' invocation. For now it is still necessary to set up
-# build_number.txt.
-
-if [ ! -e build/soong/soong_ui.bash ]; then
- echo "$0 must be run from the top of the tree"
- exit 1
-fi
-
-export OUT_DIR=${OUT_DIR:-out}
-
-if [ -e ${OUT_DIR}/soong/.soong.kati_enabled ]; then
- # If ${OUT_DIR} has been created without --soong-only, Soong will create an
- # ${OUT_DIR}/soong/build.ninja that leaves out many targets which are
- # expected to be supplied by the .mk files, and that might cause errors in
- # "m --soong-only" below. We therefore default to a different out dir
- # location in that case.
- AML_OUT_DIR=out/aml
- echo "Avoiding in-make OUT_DIR '${OUT_DIR}' - building in '${AML_OUT_DIR}' instead"
- OUT_DIR=${AML_OUT_DIR}
-fi
-
-mkdir -p ${OUT_DIR}/soong
-
-# The --dumpvars-mode invocation will run Soong in normal make mode where it
-# creates .soong.kati_enabled. That would clobber our real out directory, so we
-# need to use a different OUT_DIR.
-vars="$(OUT_DIR=${OUT_DIR}/dumpvars_mode build/soong/soong_ui.bash \
- --dumpvars-mode --vars=BUILD_NUMBER)"
-# Assign to a variable and eval that, since bash ignores any error status
-# from the command substitution if it's directly on the eval line.
-eval $vars
-
-# Some Soong build rules may require this, and the failure mode if it's missing
-# is confusing (b/172548608).
-echo -n ${BUILD_NUMBER} > ${OUT_DIR}/soong/build_number.txt
-
-build/soong/soong_ui.bash --make-mode --soong-only "$@"
diff --git a/scripts/build-apex-bundle.py b/scripts/build-apex-bundle.py
new file mode 100644
index 0000000..dcdd9ef
--- /dev/null
+++ b/scripts/build-apex-bundle.py
@@ -0,0 +1,114 @@
+#!/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.
+#
+"""A tool to create an APEX bundle out of Soong-built base.zip"""
+
+from __future__ import print_function
+
+import argparse
+import sys
+import tempfile
+import zipfile
+import os
+import json
+import subprocess
+
+
+def parse_args():
+ """Parse commandline arguments."""
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--overwrite',
+ action='store_true',
+ help='If set, any previous existing output will be overwritten')
+ parser.add_argument('--output', help='specify the output .aab file')
+ parser.add_argument(
+ 'input', help='specify the input <apex name>-base.zip file')
+ return parser.parse_args()
+
+
+def build_bundle(input, output, overwrite):
+ base_zip = zipfile.ZipFile(input)
+
+ tmpdir = tempfile.mkdtemp()
+ tmp_base_zip = os.path.join(tmpdir, 'base.zip')
+ tmp_bundle_config = os.path.join(tmpdir, 'bundle_config.json')
+
+ bundle_config = None
+ abi = []
+
+ # This block performs three tasks
+ # - extract/load bundle_config.json from input => bundle_config
+ # - get ABI from input => abi
+ # - discard bundle_config.json from input => tmp/base.zip
+ with zipfile.ZipFile(tmp_base_zip, 'a') as out:
+ for info in base_zip.infolist():
+
+ # discard bundle_config.json
+ if info.filename == 'bundle_config.json':
+ bundle_config = json.load(base_zip.open(info.filename))
+ continue
+
+ # get ABI from apex/{abi}.img
+ dir, basename = os.path.split(info.filename)
+ name, ext = os.path.splitext(basename)
+ if dir == 'apex' and ext == '.img':
+ abi.append(name)
+
+ # copy entries to tmp/base.zip
+ out.writestr(info, base_zip.open(info.filename).read())
+
+ base_zip.close()
+
+ if not bundle_config:
+ raise ValueError(f'bundle_config.json not found in {input}')
+ if len(abi) != 1:
+ raise ValueError(f'{input} should have only a single apex/*.img file')
+
+ # add ABI to tmp/bundle_config.json
+ apex_config = bundle_config['apex_config']
+ if 'supported_abi_set' not in apex_config:
+ apex_config['supported_abi_set'] = []
+ supported_abi_set = apex_config['supported_abi_set']
+ supported_abi_set.append({'abi': abi})
+
+ with open(tmp_bundle_config, 'w') as out:
+ json.dump(bundle_config, out)
+
+ # invoke bundletool
+ cmd = [
+ 'bundletool', 'build-bundle', '--config', tmp_bundle_config, '--modules',
+ tmp_base_zip, '--output', output
+ ]
+ if overwrite:
+ cmd.append('--overwrite')
+ subprocess.check_call(cmd)
+
+
+def main():
+ """Program entry point."""
+ try:
+ args = parse_args()
+ build_bundle(args.input, args.output, args.overwrite)
+
+ # pylint: disable=broad-except
+ except Exception as err:
+ print('error: ' + str(err), file=sys.stderr)
+ sys.exit(-1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index b57963b..0d14019 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -19,7 +19,12 @@
exit 1
fi
-TARGET_PRODUCT=ndk build/soong/soong_ui.bash --make-mode --soong-only ${OUT_DIR}/soong/ndk.timestamp
+# TODO: remove ALLOW_MISSING_DEPENDENCIES=true when all the riscv64
+# dependencies exist (currently blocked by http://b/273792258).
+# TODO: remove BUILD_BROKEN_DISABLE_BAZEL=1 when bazel supports riscv64 (http://b/262192655).
+ALLOW_MISSING_DEPENDENCIES=true \
+BUILD_BROKEN_DISABLE_BAZEL=1 \
+ 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
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index a02c195..869fd3f 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -8,9 +8,11 @@
java\.io
java\.lang
java\.lang\.annotation
+java\.lang\.constant
java\.lang\.invoke
java\.lang\.ref
java\.lang\.reflect
+java\.lang\.runtime
java\.math
java\.net
java\.nio
@@ -75,6 +77,7 @@
jdk\.internal\.ref
jdk\.internal\.reflect
jdk\.internal\.util
+jdk\.internal\.util\.jar
jdk\.internal\.vm\.annotation
jdk\.net
org\.w3c\.dom
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index e46efe4..3ac1b7e 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -19,6 +19,7 @@
import collections
import json
import os
+import sys
import linker_config_pb2 #pylint: disable=import-error
from google.protobuf.descriptor import FieldDescriptor
@@ -26,14 +27,41 @@
from google.protobuf.text_format import MessageToString
-def Proto(args):
+def LoadJsonMessage(path):
+ """
+ Loads a message from a .json file with `//` comments strippedfor convenience.
+ """
json_content = ''
- with open(args.source) as f:
+ with open(path) as f:
for line in f:
if not line.lstrip().startswith('//'):
json_content += line
obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
- pb = ParseDict(obj, linker_config_pb2.LinkerConfig())
+ return ParseDict(obj, linker_config_pb2.LinkerConfig())
+
+
+def Proto(args):
+ """
+ Merges input json files (--source) into a protobuf message (--output).
+ Fails if the output file exists. Set --force or --append to deal with the existing
+ output file.
+ --force to overwrite the output file with the input (.json files).
+ --append to append the input to the output file.
+ """
+ pb = linker_config_pb2.LinkerConfig()
+ if os.path.isfile(args.output):
+ if args.force:
+ pass
+ elif args.append:
+ with open(args.output, 'rb') as f:
+ pb.ParseFromString(f.read())
+ else:
+ sys.stderr.write(f'Error: {args.output} exists. Use --force or --append.\n')
+ sys.exit(1)
+
+ if args.source:
+ for input in args.source.split(':'):
+ pb.MergeFrom(LoadJsonMessage(input))
with open(args.output, 'wb') as f:
f.write(pb.SerializeToString())
@@ -102,15 +130,26 @@
parser_proto.add_argument(
'-s',
'--source',
- required=True,
+ nargs='?',
type=str,
- help='Source linker configuration file in JSON.')
+ help='Colon-separated list of linker configuration files in JSON.')
parser_proto.add_argument(
'-o',
'--output',
required=True,
type=str,
help='Target path to create protobuf file.')
+ option_for_existing_output = parser_proto.add_mutually_exclusive_group()
+ option_for_existing_output.add_argument(
+ '-f',
+ '--force',
+ action='store_true',
+ help='Overwrite if the output file exists.')
+ option_for_existing_output.add_argument(
+ '-a',
+ '--append',
+ action='store_true',
+ help='Append the input to the output file if the output file exists.')
parser_proto.set_defaults(func=Proto)
print_proto = subparsers.add_parser(
@@ -192,8 +231,12 @@
def main():
- args = GetArgParser().parse_args()
- args.func(args)
+ parser = GetArgParser()
+ args = parser.parse_args()
+ if 'func' in args:
+ args.func(args)
+ else:
+ parser.print_help()
if __name__ == '__main__':
diff --git a/scripts/conv_linker_config_test.py b/scripts/conv_linker_config_test.py
new file mode 100644
index 0000000..d19a47b
--- /dev/null
+++ b/scripts/conv_linker_config_test.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 conv_linker_config.py."""
+
+import io
+import os
+import shutil
+import tempfile
+import unittest
+
+import conv_linker_config
+from contextlib import redirect_stderr
+from linker_config_pb2 import LinkerConfig
+
+class FileArgs:
+ def __init__(self, files, sep = ':'):
+ self.files = files
+ self.sep = sep
+
+
+class FileArg:
+ def __init__(self, file):
+ self.file = file
+
+
+class TempDirTest(unittest.TestCase):
+
+ def setUp(self):
+ self.tempdir = tempfile.mkdtemp()
+
+
+ def tearDown(self):
+ shutil.rmtree(self.tempdir)
+
+
+ def write(self, name, contents):
+ with open(os.path.join(self.tempdir, name), 'wb') as f:
+ f.write(contents)
+
+
+ def read(self, name):
+ with open(os.path.join(self.tempdir, name), 'rb') as f:
+ return f.read()
+
+
+ def resolve_paths(self, args):
+ for i in range(len(args)):
+ if isinstance(args[i], FileArgs):
+ args[i] = args[i].sep.join(os.path.join(self.tempdir, f.file) for f in args[i].files)
+ elif isinstance(args[i], FileArg):
+ args[i] = os.path.join(self.tempdir, args[i].file)
+ return args
+
+
+class ConvLinkerConfigTest(TempDirTest):
+ """Unit tests for conv_linker_config."""
+
+
+ def test_Proto_empty_input(self):
+ self.command(['proto', '-s', '-o', FileArg('out.pb')])
+ pb = LinkerConfig()
+ pb.ParseFromString(self.read('out.pb'))
+ self.assertEqual(pb, LinkerConfig())
+
+
+ def test_Proto_single_input(self):
+ self.write('foo.json', b'{ "provideLibs": ["libfoo.so"]}')
+ self.command(['proto', '-s', FileArg('foo.json'), '-o', FileArg('out.pb')])
+ pb = LinkerConfig()
+ pb.ParseFromString(self.read('out.pb'))
+ self.assertSequenceEqual(pb.provideLibs, ['libfoo.so'])
+
+
+ def test_Proto_with_multiple_input(self):
+ self.write('foo.json', b'{ "provideLibs": ["libfoo.so"]}')
+ self.write('bar.json', b'{ "provideLibs": ["libbar.so"]}')
+ self.command(['proto', '-s', FileArgs([FileArg('foo.json'), FileArg('bar.json')]), '-o', FileArg('out.pb')])
+ pb = LinkerConfig()
+ pb.ParseFromString(self.read('out.pb'))
+ self.assertSetEqual(set(pb.provideLibs), set(['libfoo.so', 'libbar.so']))
+
+
+ def test_Proto_with_existing_output(self):
+ self.write('out.pb', LinkerConfig(provideLibs=['libfoo.so']).SerializeToString())
+ buf = io.StringIO()
+ with self.assertRaises(SystemExit) as err:
+ with redirect_stderr(buf):
+ self.command(['proto', '-o', FileArg('out.pb')])
+ self.assertEqual(err.exception.code, 1)
+ self.assertRegex(buf.getvalue(), r'.*out\.pb exists')
+
+
+ def test_Proto_with_append(self):
+ self.write('out.pb', LinkerConfig(provideLibs=['libfoo.so']).SerializeToString())
+ self.write('bar.json', b'{ "provideLibs": ["libbar.so"]}')
+ self.command(['proto', '-s', FileArg('bar.json'), '-o', FileArg('out.pb'), '-a'])
+ pb = LinkerConfig()
+ pb.ParseFromString(self.read('out.pb'))
+ self.assertSetEqual(set(pb.provideLibs), set(['libfoo.so', 'libbar.so']))
+
+
+ def test_Proto_with_force(self):
+ self.write('out.pb', LinkerConfig(provideLibs=['libfoo.so']).SerializeToString())
+ self.write('bar.json', b'{ "provideLibs": ["libbar.so"]}')
+ self.command(['proto', '-s', FileArg('bar.json'), '-o', FileArg('out.pb'), '-f'])
+ pb = LinkerConfig()
+ pb.ParseFromString(self.read('out.pb'))
+ self.assertSetEqual(set(pb.provideLibs), set(['libbar.so']))
+
+
+ def command(self, args):
+ parser = conv_linker_config.GetArgParser()
+ parsed_args = parser.parse_args(self.resolve_paths(args))
+ parsed_args.func(parsed_args)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/scripts/gen_ndk_usedby_apex.sh b/scripts/gen_ndk_usedby_apex.sh
index 0d3ed5a..93d1370 100755
--- a/scripts/gen_ndk_usedby_apex.sh
+++ b/scripts/gen_ndk_usedby_apex.sh
@@ -19,6 +19,7 @@
# For example, current line llvm-readelf output is:
# 1: 00000000 0 FUNC GLOBAL DEFAULT UND dlopen@LIBC
# After the parse function below "dlopen" would be write to the output file.
+
printHelp() {
echo "**************************** Usage Instructions ****************************"
echo "This script is used to generate the Mainline modules used-by NDK symbols."
@@ -29,30 +30,33 @@
}
parseReadelfOutput() {
+ local readelfOutput=$1; shift
+ local ndkApisOutput=$1; shift
while IFS= read -r line
do
if [[ $line = *FUNC*GLOBAL*UND*@* ]] ;
then
- echo "$line" | sed -r 's/.*UND (.*@.*)/\1/g' >> "$2"
+ echo "$line" | sed -r 's/.*UND (.*@.*)/\1/g' >> "${ndkApisOutput}"
fi
- done < "$1"
- echo "" >> "$2"
+ done < "${readelfOutput}"
+ echo "" >> "${ndkApisOutput}"
}
unzipJarAndApk() {
- tmpUnzippedDir="$1"/tmpUnzipped
- [[ -e "$tmpUnzippedDir" ]] && rm -rf "$tmpUnzippedDir"
- mkdir -p "$tmpUnzippedDir"
- find "$1" -name "*.jar" -exec unzip -o {} -d "$tmpUnzippedDir" \;
- find "$1" -name "*.apk" -exec unzip -o {} -d "$tmpUnzippedDir" \;
- find "$tmpUnzippedDir" -name "*.MF" -exec rm {} \;
+ local dir="$1"; shift
+ local tmpUnzippedDir="$1"; shift
+ mkdir -p "${tmpUnzippedDir}"
+ find "$dir" -name "*.jar" -exec unzip -o {} -d "${tmpUnzippedDir}" \;
+ find "$dir" -name "*.apk" -exec unzip -o {} -d "${tmpUnzippedDir}" \;
+ find "${tmpUnzippedDir}" -name "*.MF" -exec rm {} \;
}
lookForExecFile() {
- dir="$1"
- readelf="$2"
- find "$dir" -type f -name "*.so" -exec "$2" --dyn-symbols {} >> "$dir"/../tmpReadelf.txt \;
- find "$dir" -type f -perm /111 ! -name "*.so" -exec "$2" --dyn-symbols {} >> "$dir"/../tmpReadelf.txt \;
+ local dir="$1"; shift
+ local readelf="$1"; shift
+ local tmpOutput="$1"; shift
+ find -L "$dir" -type f -name "*.so" -exec "${readelf}" --dyn-symbols {} >> "${tmpOutput}" \;
+ find -L "$dir" -type f -perm /111 ! -name "*.so" -exec "${readelf}" --dyn-symbols {} >> "${tmpOutput}" \;
}
if [[ "$1" == "help" ]]
@@ -62,11 +66,22 @@
then
echo "Wrong argument length. Expecting 3 argument representing image file directory, llvm-readelf tool path, output path."
else
- unzipJarAndApk "$1"
- lookForExecFile "$1" "$2"
- tmpReadelfOutput="$1/../tmpReadelf.txt"
- [[ -e "$3" ]] && rm "$3"
- parseReadelfOutput "$tmpReadelfOutput" "$3"
- [[ -e "$tmpReadelfOutput" ]] && rm "$tmpReadelfOutput"
- rm -rf "$1/tmpUnzipped"
-fi
\ No newline at end of file
+ imageDir="$1"; shift
+ readelf="$1"; shift
+ outputFile="$1"; shift
+
+ tmpReadelfOutput=$(mktemp /tmp/temporary-file.XXXXXXXX)
+ tmpUnzippedDir=$(mktemp -d /tmp/temporary-dir.XXXXXXXX)
+ trap 'rm -rf -- "${tmpReadelfOutput}" "${tmpUnzippedDir}"' EXIT
+
+ # If there are any jars or apks, unzip them to surface native files.
+ unzipJarAndApk "${imageDir}" "${tmpUnzippedDir}"
+ # Analyze the unzipped files.
+ lookForExecFile "${tmpUnzippedDir}" "${readelf}" "${tmpReadelfOutput}"
+
+ # Analyze the apex image staging dir itself.
+ lookForExecFile "${imageDir}" "${readelf}" "${tmpReadelfOutput}"
+
+ [[ -e "${outputFile}" ]] && rm "${outputFile}"
+ parseReadelfOutput "${tmpReadelfOutput}" "${outputFile}"
+fi
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index 07878f9..1e89efe 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -18,29 +18,31 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+python_defaults {
+ name: "hiddenapi_defaults",
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+}
+
python_binary_host {
name: "analyze_bcpf",
main: "analyze_bcpf.py",
+ defaults: ["hiddenapi_defaults"],
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",
+ defaults: ["hiddenapi_defaults"],
srcs: [
"analyze_bcpf.py",
"analyze_bcpf_test.py",
@@ -50,15 +52,6 @@
libs: [
"signature_trie",
],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
test_options: {
unit_test: true,
},
@@ -67,49 +60,25 @@
python_binary_host {
name: "merge_csv",
main: "merge_csv.py",
+ defaults: ["hiddenapi_defaults"],
srcs: ["merge_csv.py"],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
}
python_binary_host {
name: "generate_hiddenapi_lists",
main: "generate_hiddenapi_lists.py",
+ defaults: ["hiddenapi_defaults"],
srcs: ["generate_hiddenapi_lists.py"],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
}
python_test_host {
name: "generate_hiddenapi_lists_test",
main: "generate_hiddenapi_lists_test.py",
+ defaults: ["hiddenapi_defaults"],
srcs: [
"generate_hiddenapi_lists.py",
"generate_hiddenapi_lists_test.py",
],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
test_options: {
unit_test: true,
},
@@ -123,17 +92,9 @@
python_test_host {
name: "signature_trie_test",
main: "signature_trie_test.py",
+ defaults: ["hiddenapi_defaults"],
srcs: ["signature_trie_test.py"],
libs: ["signature_trie"],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
test_options: {
unit_test: true,
},
@@ -142,24 +103,17 @@
python_binary_host {
name: "verify_overlaps",
main: "verify_overlaps.py",
+ defaults: ["hiddenapi_defaults"],
srcs: ["verify_overlaps.py"],
libs: [
"signature_trie",
],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
}
python_test_host {
name: "verify_overlaps_test",
main: "verify_overlaps_test.py",
+ defaults: ["hiddenapi_defaults"],
srcs: [
"verify_overlaps.py",
"verify_overlaps_test.py",
@@ -167,15 +121,6 @@
libs: [
"signature_trie",
],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
test_options: {
unit_test: true,
},
@@ -184,34 +129,18 @@
python_binary_host {
name: "signature_patterns",
main: "signature_patterns.py",
+ defaults: ["hiddenapi_defaults"],
srcs: ["signature_patterns.py"],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
}
python_test_host {
name: "signature_patterns_test",
main: "signature_patterns_test.py",
+ defaults: ["hiddenapi_defaults"],
srcs: [
"signature_patterns.py",
"signature_patterns_test.py",
],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
test_options: {
unit_test: true,
},
diff --git a/scripts/hiddenapi/signature_trie.py b/scripts/hiddenapi/signature_trie.py
index 3650fa1..2ff0c5f 100644
--- a/scripts/hiddenapi/signature_trie.py
+++ b/scripts/hiddenapi/signature_trie.py
@@ -150,10 +150,6 @@
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
diff --git a/scripts/hiddenapi/signature_trie_test.py b/scripts/hiddenapi/signature_trie_test.py
index 6d4e660..bd4a9a8 100755
--- a/scripts/hiddenapi/signature_trie_test.py
+++ b/scripts/hiddenapi/signature_trie_test.py
@@ -117,14 +117,6 @@
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"),
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index 2d3103b..58079aa 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -39,6 +39,8 @@
parser = argparse.ArgumentParser()
parser.add_argument('--minSdkVersion', default='', dest='min_sdk_version',
help='specify minSdkVersion used by the build system')
+ parser.add_argument('--replaceMaxSdkVersionPlaceholder', default='', dest='max_sdk_version',
+ help='specify maxSdkVersion used by the build system')
parser.add_argument('--targetSdkVersion', default='', dest='target_sdk_version',
help='specify targetSdkVersion used by the build system')
parser.add_argument('--raise-min-sdk-version', dest='raise_min_sdk_version', action='store_true',
@@ -68,6 +70,8 @@
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('--override-placeholder-version', dest='new_version',
+ help='Overrides the versionCode if it\'s set to the placeholder value of 0')
parser.add_argument('input', help='input AndroidManifest.xml file')
parser.add_argument('output', help='output AndroidManifest.xml file')
return parser.parse_args()
@@ -342,6 +346,37 @@
attr.value = 'true'
application.setAttributeNode(attr)
+def set_max_sdk_version(doc, max_sdk_version):
+ """Replace the maxSdkVersion attribute value for permission and
+ uses-permission tags if the value was originally set to 'current'.
+ Used for cts test cases where the maxSdkVersion should equal to
+ Build.SDK_INT.
+
+ Args:
+ doc: The XML document. May be modified by this function.
+ max_sdk_version: The requested maxSdkVersion attribute.
+ """
+ manifest = parse_manifest(doc)
+ for tag in ['permission', 'uses-permission']:
+ children = get_children_with_tag(manifest, tag)
+ for child in children:
+ max_attr = child.getAttributeNodeNS(android_ns, 'maxSdkVersion')
+ if max_attr and max_attr.value == 'current':
+ max_attr.value = max_sdk_version
+
+def override_placeholder_version(doc, new_version):
+ """Replace the versionCode attribute value if it\'s currently
+ set to the placeholder version of 0.
+
+ Args:
+ doc: The XML document. May be modified by this function.
+ new_version: The new version to set if versionCode is equal to 0.
+ """
+ manifest = parse_manifest(doc)
+ version = manifest.getAttribute("android:versionCode")
+ if (version == '0'):
+ manifest.setAttribute("android:versionCode", new_version)
+
def main():
"""Program entry point."""
try:
@@ -354,6 +389,9 @@
if args.raise_min_sdk_version:
raise_min_sdk_version(doc, args.min_sdk_version, args.target_sdk_version, args.library)
+ if args.max_sdk_version:
+ set_max_sdk_version(doc, args.max_sdk_version)
+
if args.uses_libraries:
add_uses_libraries(doc, args.uses_libraries, True)
@@ -378,6 +416,9 @@
if args.extract_native_libs is not None:
add_extract_native_libs(doc, args.extract_native_libs)
+ if args.new_version:
+ override_placeholder_version(doc, args.new_version)
+
with open(args.output, 'w') as f:
write_xml(f, doc)
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index 199b279..0a62b10 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -571,5 +571,111 @@
output = self.run_test(manifest_input)
self.assert_xml_equal(output, manifest_input)
+
+class SetMaxSdkVersionTest(unittest.TestCase):
+ """Unit tests for set_max_sdk_version function."""
+
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
+ def run_test(self, input_manifest, max_sdk_version):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.set_max_sdk_version(doc, max_sdk_version)
+ 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 permission(self, max=None):
+ if max is None:
+ return ' <permission/>'
+ return ' <permission android:maxSdkVersion="%s"/>\n' % max
+
+ def uses_permission(self, max=None):
+ if max is None:
+ return ' <uses-permission/>'
+ return ' <uses-permission android:maxSdkVersion="%s"/>\n' % max
+
+ def test_permission_no_max_sdk_version(self):
+ """Tests if permission has no maxSdkVersion attribute"""
+ manifest_input = self.manifest_tmpl % self.permission()
+ expected = self.manifest_tmpl % self.permission()
+ output = self.run_test(manifest_input, '9000')
+ self.assert_xml_equal(output, expected)
+
+ def test_permission_max_sdk_version_changed(self):
+ """Tests if permission maxSdkVersion attribute is set to current"""
+ manifest_input = self.manifest_tmpl % self.permission('current')
+ expected = self.manifest_tmpl % self.permission(9000)
+ output = self.run_test(manifest_input, '9000')
+ self.assert_xml_equal(output, expected)
+
+ def test_permission_max_sdk_version_not_changed(self):
+ """Tests if permission maxSdkVersion attribute is not set to current"""
+ manifest_input = self.manifest_tmpl % self.permission(30)
+ expected = self.manifest_tmpl % self.permission(30)
+ output = self.run_test(manifest_input, '9000')
+ self.assert_xml_equal(output, expected)
+
+ def test_uses_permission_no_max_sdk_version(self):
+ """Tests if uses-permission has no maxSdkVersion attribute"""
+ manifest_input = self.manifest_tmpl % self.uses_permission()
+ expected = self.manifest_tmpl % self.uses_permission()
+ output = self.run_test(manifest_input, '9000')
+ self.assert_xml_equal(output, expected)
+
+ def test_uses_permission_max_sdk_version_changed(self):
+ """Tests if uses-permission maxSdkVersion attribute is set to current"""
+ manifest_input = self.manifest_tmpl % self.uses_permission('current')
+ expected = self.manifest_tmpl % self.uses_permission(9000)
+ output = self.run_test(manifest_input, '9000')
+ self.assert_xml_equal(output, expected)
+
+ def test_uses_permission_max_sdk_version_not_changed(self):
+ """Tests if uses-permission maxSdkVersion attribute is not set to current"""
+ manifest_input = self.manifest_tmpl % self.uses_permission(30)
+ expected = self.manifest_tmpl % self.uses_permission(30)
+ output = self.run_test(manifest_input, '9000')
+ self.assert_xml_equal(output, expected)
+
+class OverrideDefaultVersionTest(unittest.TestCase):
+ """Unit tests for override_default_version function."""
+
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
+ def run_test(self, input_manifest, version):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.override_placeholder_version(doc, version)
+ 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" '
+ 'android:versionCode="%s">\n'
+ '</manifest>\n')
+
+ def test_doesnt_override_existing_version(self):
+ """Tests that an existing version is not overridden"""
+ manifest_input = self.manifest_tmpl % '12345'
+ expected = manifest_input
+ output = self.run_test(manifest_input, '67890')
+ self.assert_xml_equal(output, expected)
+
+ def test_overrides_default_version(self):
+ """Tests that a default version is overridden"""
+ manifest_input = self.manifest_tmpl % '0'
+ expected = self.manifest_tmpl % '67890'
+ output = self.run_test(manifest_input, '67890')
+ self.assert_xml_equal(output, expected)
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/scripts/microfactory.bash b/scripts/microfactory.bash
index 192b38f..ce4a0e4 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 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" \
+ EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path prebuilts/bazel/common/proto=${TOP}/prebuilts/bazel/common/proto -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/mkcratersp.py b/scripts/mkcratersp.py
new file mode 100755
index 0000000..86b4aa3
--- /dev/null
+++ b/scripts/mkcratersp.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+This script is used as a replacement for the Rust linker. It converts a linker
+command line into a rspfile that can be used during the link phase.
+"""
+
+import os
+import shutil
+import subprocess
+import sys
+
+def create_archive(out, objects, archives):
+ mricmd = f'create {out}\n'
+ for o in objects:
+ mricmd += f'addmod {o}\n'
+ for a in archives:
+ mricmd += f'addlib {a}\n'
+ mricmd += 'save\nend\n'
+ subprocess.run([os.getenv('AR'), '-M'], encoding='utf-8', input=mricmd, check=True)
+
+objects = []
+archives = []
+linkdirs = []
+libs = []
+temp_archives = []
+version_script = None
+
+for i, arg in enumerate(sys.argv):
+ if arg == '-o':
+ out = sys.argv[i+1]
+ if arg == '-L':
+ linkdirs.append(sys.argv[i+1])
+ if arg.startswith('-l') or arg == '-shared':
+ libs.append(arg)
+ if arg.startswith('-Wl,--version-script='):
+ version_script = arg[21:]
+ if arg[0] == '-':
+ continue
+ if arg.endswith('.o') or arg.endswith('.rmeta'):
+ objects.append(arg)
+ if arg.endswith('.rlib'):
+ if arg.startswith(os.getenv('TMPDIR')):
+ temp_archives.append(arg)
+ else:
+ archives.append(arg)
+
+create_archive(f'{out}.whole.a', objects, [])
+create_archive(f'{out}.a', [], temp_archives)
+
+with open(out, 'w') as f:
+ print(f'-Wl,--whole-archive', file=f)
+ print(f'{out}.whole.a', file=f)
+ print(f'-Wl,--no-whole-archive', file=f)
+ print(f'{out}.a', file=f)
+ for a in archives:
+ print(a, file=f)
+ for linkdir in linkdirs:
+ print(f'-L{linkdir}', file=f)
+ for l in libs:
+ print(l, file=f)
+ if version_script:
+ shutil.copyfile(version_script, f'{out}.version_script')
+ print(f'-Wl,--version-script={out}.version_script', file=f)
diff --git a/scripts/run-ckati.sh b/scripts/run-ckati.sh
new file mode 100755
index 0000000..b670c8a
--- /dev/null
+++ b/scripts/run-ckati.sh
@@ -0,0 +1,91 @@
+#! /bin/bash -eu
+
+# Run CKati step separately, tracing given Makefile variables.
+# It is expected that the regular Android null build (`m nothing`)
+# has been run so that $OUT_DIR/soong/Android-${TARGET_PRODUCT}.mk,
+# $OUT_DIR/soong/make_vars-${TARGET_PRODUCT}.mk, etc. files exist.
+#
+# The output file is in JSON format and can be processed with, say,
+# `jq`. For instance, the following invocation outputs all assignment
+# traces concisely:
+# jq -c '.assignments[] | (select (.operation == "assign")) | {("n"): .name, ("l"): .value_stack[0]?, ("v"): .value }' out/ckati.trace
+# generates
+# {"n":"<var1>","l":"<file>:<line>","v":"<value1>"}
+# ...
+
+function die() { format=$1; shift; printf "$format\n" $@; exit 1; }
+function usage() { die "Usage: %s [-o FILE] VAR ...\n(without -o the output goes to ${outfile})" ${0##*/}; }
+
+[[ -d build/soong ]] || die "run this script from the top of the Android source tree"
+declare -r out=${OUT_DIR:-out}
+[[ -x ${out}/soong_ui ]] || die "run Android build first"
+: ${TARGET_PRODUCT:?not set, run lunch?}
+: ${TARGET_BUILD_VARIANT:?not set, run lunch?}
+declare -r androidmk=${out}/soong/Android-${TARGET_PRODUCT}.mk
+declare -r makevarsmk=${out}/soong/make_vars-${TARGET_PRODUCT}.mk
+declare -r target_device_dir=$(${out}/soong_ui --dumpvar-mode TARGET_DEVICE_DIR)
+: ${target_device_dir:?cannot find device directory for ${TARGET_PRODUCT}}
+declare -r target_device=$(${out}/soong_ui --dumpvar-mode TARGET_DEVICE)
+: ${target_device:?cannot find target device for ${TARGET_PRODUCT}}
+declare -r timestamp_file=${out}/build_date.txt
+# Files should exist, so ls should succeed:
+ls -1d "$androidmk" "$makevarsmk" "$target_device_dir" "$timestamp_file" >/dev/null
+
+outfile=${out}/ckati.trace
+while getopts "ho:" opt; do
+ case $opt in
+ h) usage ;;
+ o) outfile=$OPTARG ;;
+ ?) usage ;;
+ esac
+done
+
+if (($#>0)); then
+ declare -a tracing=(--variable_assignment_trace_filter="$*" --dump_variable_assignment_trace "$outfile")
+else
+ printf "running ckati without tracing variables\n"
+fi
+
+# Touch one input for ckati, otherwise it will just print
+# 'No need to regenerate ninja file' and exit.
+touch "$androidmk"
+prebuilts/build-tools/linux-x86/bin/ckati \
+ --gen_all_targets \
+ -i \
+ --ignore_optional_include=out/%.P \
+ --ninja \
+ --ninja_dir=out \
+ --ninja_suffix=-${TARGET_PRODUCT} \
+ --no_builtin_rules \
+ --no_ninja_prelude \
+ --regen \
+ --top_level_phony \
+ --use_find_emulator \
+ --use_ninja_phony_output \
+ --use_ninja_symlink_outputs \
+ --werror_find_emulator \
+ --werror_implicit_rules \
+ --werror_overriding_commands \
+ --werror_phony_looks_real \
+ --werror_real_to_phony \
+ --werror_suffix_rules \
+ --werror_writable \
+ --writable out/ \
+ -f build/make/core/main.mk \
+ "${tracing[@]}" \
+ ANDROID_JAVA_HOME=prebuilts/jdk/jdk17/linux-x86 \
+ ASAN_SYMBOLIZER_PATH=$PWD/prebuilts/clang/host/linux-x86/llvm-binutils-stable/llvm-symbolizer \
+ BUILD_DATETIME_FILE="$timestamp_file" \
+ BUILD_HOSTNAME=$(hostname) \
+ BUILD_USERNAME="$USER" \
+ JAVA_HOME=$PWD/prebuilts/jdk/jdk17/linux-x86 \
+ KATI_PACKAGE_MK_DIR="{$out}/target/product/${target_device}/CONFIG/kati_packaging" \
+ OUT_DIR="$out" \
+ PATH="$PWD/prebuilts/build-tools/path/linux-x86:$PWD/${out}/.path" \
+ PYTHONDONTWRITEBYTECODE=1 \
+ SOONG_ANDROID_MK="$androidmk" \
+ SOONG_MAKEVARS_MK="$makevarsmk" \
+ TARGET_BUILD_VARIANT="$TARGET_BUILD_VARIANT" \
+ TARGET_DEVICE_DIR="$target_device_dir" \
+ TARGET_PRODUCT=${TARGET_PRODUCT} \
+ TMPDIR="$PWD/$out/soong/.temp"
diff --git a/scripts/test_config_fixer.py b/scripts/test_config_fixer.py
index 3dbc22e..07e01a1 100644
--- a/scripts/test_config_fixer.py
+++ b/scripts/test_config_fixer.py
@@ -31,6 +31,8 @@
KNOWN_PREPARERS = ['com.android.tradefed.targetprep.TestAppInstallSetup',
'com.android.tradefed.targetprep.suite.SuiteApkInstaller']
+MAINLINE_CONTROLLER = 'com.android.tradefed.testtype.suite.module.MainlineTestModuleController'
+
def parse_args():
"""Parse commandline arguments."""
@@ -41,6 +43,8 @@
help=('overwrite package fields in the test config'))
parser.add_argument('--test-file-name', default='', dest='test_file_name',
help=('overwrite test file name in the test config'))
+ parser.add_argument('--mainline-package-name', default='', dest='mainline_package_name',
+ help=('overwrite mainline module package name in the test config'))
parser.add_argument('input', help='input test config file')
parser.add_argument('output', help='output test config file')
return parser.parse_args()
@@ -72,6 +76,16 @@
if option.getAttribute('name') == "test-file-name":
option.setAttribute('value', test_file_name)
+def overwrite_mainline_module_package_name(test_config_doc, mainline_package_name):
+
+ test_config = parse_test_config(test_config_doc)
+
+ for obj in get_children_with_tag(test_config, 'object'):
+ if obj.getAttribute('class') == MAINLINE_CONTROLLER:
+ for option in get_children_with_tag(obj, 'option'):
+ if option.getAttribute('name') == "mainline-module-package-name":
+ option.setAttribute('value', mainline_package_name)
+
def main():
"""Program entry point."""
try:
@@ -88,6 +102,9 @@
if args.test_file_name:
overwrite_test_file_name(doc, args.test_file_name)
+ if args.mainline_package_name:
+ overwrite_mainline_module_package_name(doc, args.mainline_package_name)
+
with open(args.output, 'w') as f:
write_xml(f, doc)
diff --git a/scripts/test_config_fixer_test.py b/scripts/test_config_fixer_test.py
index 39ce5b3..699f91e 100644
--- a/scripts/test_config_fixer_test.py
+++ b/scripts/test_config_fixer_test.py
@@ -23,6 +23,8 @@
import test_config_fixer
+from manifest import write_xml
+
sys.dont_write_bytecode = True
@@ -117,5 +119,39 @@
self.assertEqual(expected, output.getvalue())
+class OverwriteMainlineModulePackageNameTest(unittest.TestCase):
+ """ Unit tests for overwrite_mainline_module_package_name function """
+
+ test_config = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<configuration description="Runs some tests.">\n'
+ ' <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">\n'
+ ' <option name="test-file-name" value="foo.apk"/>\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'
+ ' <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">\n'
+ ' <option name="enable" value="true"/>\n'
+ ' <option name="mainline-module-package-name" value="%s"/>\n'
+ ' </object>\n'
+ '</configuration>\n')
+
+ def test_testappinstallsetup(self):
+ doc = minidom.parseString(self.test_config % ("com.android.old.package.name"))
+
+ test_config_fixer.overwrite_mainline_module_package_name(doc, "com.android.new.package.name")
+ output = io.StringIO()
+ test_config_fixer.write_xml(output, doc)
+
+ # Only the mainline module package name should be updated. Format the xml
+ # with minidom first to avoid mismatches due to trivial reformatting.
+ expected = io.StringIO()
+ write_xml(expected, minidom.parseString(self.test_config % ("com.android.new.package.name")))
+ self.maxDiff = None
+ self.assertEqual(expected.getvalue(), output.getvalue())
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/scripts/unpack-prebuilt-apex.sh b/scripts/unpack-prebuilt-apex.sh
index f34a480..b244f79 100755
--- a/scripts/unpack-prebuilt-apex.sh
+++ b/scripts/unpack-prebuilt-apex.sh
@@ -17,23 +17,28 @@
# limitations under the License.
# Tool to unpack an apex file and verify that the required files were extracted.
-if [ $# -lt 5 ]; then
- echo "usage: $0 <deapaxer_path> <debugfs_path> <apex file> <output_dir> <required_files>+" >&2
+if [ $# -lt 7 ]; then
+ echo "usage: $0 <deapaxer_path> <debugfs_path> <blkid_path> <fsck.erofs_path> <apex file> <output_dir> <required_files>+" >&2
exit 1
fi
DEAPEXER_PATH=$1
DEBUGFS_PATH=$2
-APEX_FILE=$3
-OUTPUT_DIR=$4
-shift 4
+BLKID_PATH=$3
+FSCK_EROFS_PATH=$4
+APEX_FILE=$5
+OUTPUT_DIR=$6
+shift 6
REQUIRED_PATHS=$@
rm -fr $OUTPUT_DIR
mkdir -p $OUTPUT_DIR
# Unpack the apex file contents.
-$DEAPEXER_PATH --debugfs_path $DEBUGFS_PATH extract $APEX_FILE $OUTPUT_DIR
+$DEAPEXER_PATH --debugfs_path $DEBUGFS_PATH \
+ --blkid_path $BLKID_PATH \
+ --fsckerofs_path $FSCK_EROFS_PATH \
+ extract $APEX_FILE $OUTPUT_DIR
# Verify that the files that the build expects to be in the .apex file actually
# exist, and make sure they have a fresh mtime to not confuse ninja.
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 1b64130..bef82d6 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -27,6 +27,11 @@
// fixtureAddPlatformBootclasspathForBootclasspathFragment adds a platform_bootclasspath module that
// references the bootclasspath fragment.
func fixtureAddPlatformBootclasspathForBootclasspathFragment(apex, fragment string) android.FixturePreparer {
+ return fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(apex, fragment, "")
+}
+
+// fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra is the same as above, but also adds extra fragments.
+func fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(apex, fragment, extraFragments string) android.FixturePreparer {
return android.GroupFixturePreparers(
// Add a platform_bootclasspath module.
android.FixtureAddTextFile("frameworks/base/boot/Android.bp", fmt.Sprintf(`
@@ -37,9 +42,10 @@
apex: "%s",
module: "%s",
},
+ %s
],
}
- `, apex, fragment)),
+ `, apex, fragment, extraFragments)),
android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
android.FixtureAddFile("frameworks/base/config/boot-image-profile.txt", nil),
android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
@@ -68,7 +74,7 @@
func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForSdkTestWithJava,
- java.PrepareForTestWithJavaDefaultModules,
+ java.PrepareForTestWithDexpreopt,
prepareForSdkTestWithApex,
// Some additional files needed for the art apex.
@@ -79,9 +85,11 @@
}),
// Add a platform_bootclasspath that depends on the fragment.
- fixtureAddPlatformBootclasspathForBootclasspathFragment("com.android.art", "mybootclasspathfragment"),
+ fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(
+ "com.android.art", "mybootclasspathfragment", java.ApexBootJarFragmentsForPlatformBootclasspath),
java.PrepareForBootImageConfigTest,
+ java.PrepareApexBootJarConfigsAndModules,
android.FixtureWithRootAndroidBp(`
sdk {
name: "mysdk",
@@ -196,9 +204,15 @@
snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
// Make sure that the boot jars package check rule includes the dex jars retrieved from the prebuilt apex.
checkBootJarsPackageCheckRule(t, result,
- "out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core1.jar",
- "out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core2.jar",
- "out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar")
+ append(
+ []string{
+ "out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core1.jar",
+ "out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core2.jar",
+ "out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
+ },
+ java.ApexBootJarDexJarPaths...,
+ )...,
+ )
java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/snapshot/mybootclasspathfragment/android_common_com.android.art/meta_lic")
java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
}),
@@ -222,9 +236,15 @@
// Make sure that the boot jars package check rule includes the dex jars created from the source.
checkBootJarsPackageCheckRule(t, result,
- "out/soong/.intermediates/core1/android_common_apex10000/aligned/core1.jar",
- "out/soong/.intermediates/core2/android_common_apex10000/aligned/core2.jar",
- "out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar")
+ append(
+ []string{
+ "out/soong/.intermediates/core1/android_common_apex10000/aligned/core1.jar",
+ "out/soong/.intermediates/core2/android_common_apex10000/aligned/core2.jar",
+ "out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
+ },
+ java.ApexBootJarDexJarPaths...,
+ )...,
+ )
}
// checkBootJarsPackageCheckRule checks that the supplied module is an input to the boot jars
@@ -358,6 +378,7 @@
visibility: ["//visibility:public"],
apex_available: ["myapex"],
jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
+ min_sdk_version: "2",
permitted_packages: ["mybootlib"],
}
@@ -663,7 +684,15 @@
android.GroupFixturePreparers(
prepareForSdkTestWithApex,
prepareForSdkTestWithJava,
- android.FixtureAddFile("java/mybootlib.jar", nil),
+ android.FixtureMergeMockFs(android.MockFS{
+ "java/mybootlib.jar": nil,
+ "hiddenapi/annotation-flags.csv": nil,
+ "hiddenapi/index.csv": nil,
+ "hiddenapi/metadata.csv": nil,
+ "hiddenapi/signature-patterns.csv": nil,
+ "hiddenapi/filtered-stub-flags.csv": nil,
+ "hiddenapi/filtered-flags.csv": nil,
+ }),
android.FixtureWithRootAndroidBp(`
sdk {
name: "mysdk",
@@ -690,26 +719,27 @@
compile_dex: true,
}
- sdk_snapshot {
- name: "mysdk@1",
- bootclasspath_fragments: ["mysdk_mybootclasspathfragment@1"],
- }
-
prebuilt_bootclasspath_fragment {
- name: "mysdk_mybootclasspathfragment@1",
- sdk_member_name: "mybootclasspathfragment",
+ name: "mybootclasspathfragment",
prefer: false,
visibility: ["//visibility:public"],
apex_available: [
"myapex",
],
image_name: "art",
- contents: ["mysdk_mybootlib@1"],
+ contents: ["mybootlib"],
+ 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@1",
- sdk_member_name: "mybootlib",
+ name: "mybootlib",
visibility: ["//visibility:public"],
apex_available: ["com.android.art"],
jars: ["java/mybootlib.jar"],
@@ -809,6 +839,7 @@
compile_dex: true,
public: {enabled: true},
permitted_packages: ["mysdklibrary"],
+ min_sdk_version: "current",
}
java_sdk_library {
@@ -877,6 +908,7 @@
visibility: ["//visibility:public"],
apex_available: ["myapex"],
jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
+ min_sdk_version: "1",
permitted_packages: ["mybootlib"],
}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 51903ce3..6159ea9 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -19,12 +19,14 @@
"testing"
"android/soong/android"
+ "android/soong/dexpreopt"
"android/soong/java"
)
var prepareForSdkTestWithJava = android.GroupFixturePreparers(
java.PrepareForTestWithJavaBuildComponents,
PrepareForTestWithSdkBuildComponents,
+ dexpreopt.PrepareForTestWithFakeDex2oatd,
// Ensure that all source paths are provided. This helps ensure that the snapshot generation is
// consistent and all files referenced from the snapshot's Android.bp file have actually been
@@ -33,7 +35,8 @@
// Files needs by most of the tests.
android.MockFS{
- "Test.java": nil,
+ "Test.java": nil,
+ "art-profile": nil,
}.AddToFixture(),
)
@@ -352,6 +355,73 @@
})
}
+func TestSnapshotWithJavaLibrary_MinSdkVersion(t *testing.T) {
+ runTest := func(t *testing.T, targetBuildRelease, minSdkVersion, expectedMinSdkVersion string) {
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_version_active_codenames = []string{"S", "Tiramisu", "Unfinalized"}
+ }),
+ android.FixtureMergeEnv(map[string]string{
+ "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease,
+ }),
+ ).RunTestWithBp(t, fmt.Sprintf(`
+ sdk {
+ name: "mysdk",
+ java_header_libs: ["mylib"],
+ }
+
+ java_library {
+ name: "mylib",
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ min_sdk_version: "%s",
+ }
+ `, minSdkVersion))
+
+ expectedMinSdkVersionLine := ""
+ if expectedMinSdkVersion != "" {
+ expectedMinSdkVersionLine = fmt.Sprintf(" min_sdk_version: %q,\n", expectedMinSdkVersion)
+ }
+
+ CheckSnapshot(t, result, "mysdk", "",
+ checkAndroidBpContents(fmt.Sprintf(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mylib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/mylib.jar"],
+%s}
+`, expectedMinSdkVersionLine)),
+ )
+ }
+
+ t.Run("min_sdk_version=S in S", func(t *testing.T) {
+ // min_sdk_version was not added to java_import until Tiramisu.
+ runTest(t, "S", "S", "")
+ })
+
+ t.Run("min_sdk_version=S in Tiramisu", func(t *testing.T) {
+ // The canonical form of S is 31.
+ runTest(t, "Tiramisu", "S", "31")
+ })
+
+ t.Run("min_sdk_version=24 in Tiramisu", func(t *testing.T) {
+ // A numerical min_sdk_version is already in canonical form.
+ runTest(t, "Tiramisu", "24", "24")
+ })
+
+ t.Run("min_sdk_version=Unfinalized in latest", func(t *testing.T) {
+ // An unfinalized min_sdk_version has no numeric value yet.
+ runTest(t, "", "Unfinalized", "Unfinalized")
+ })
+}
+
func TestSnapshotWithJavaSystemserverLibrary(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForSdkTestWithJava,
diff --git a/sdk/sdk.go b/sdk/sdk.go
index aeeedb4..4d4a2a2 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -17,7 +17,6 @@
import (
"fmt"
"io"
- "strconv"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -38,7 +37,6 @@
func registerSdkBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("sdk", SdkModuleFactory)
ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
- ctx.PreDepsMutators(RegisterPreDepsMutators)
}
type sdk struct {
@@ -275,14 +273,6 @@
var _ android.SdkDependencyContext = (*dependencyContext)(nil)
-// RegisterPreDepsMutators registers pre-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 RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("SdkMember", memberMutator).Parallel()
- ctx.TopDown("SdkMember_deps", memberDepsMutator).Parallel()
-}
-
type dependencyTag struct {
blueprint.BaseDependencyTag
}
@@ -292,68 +282,35 @@
var _ android.ExcludeFromApexContentsTag = dependencyTag{}
-// Step 1: create dependencies from an SDK module to its members.
-func memberMutator(mctx android.BottomUpMutatorContext) {
- if s, ok := mctx.Module().(*sdk); ok {
- // Add dependencies from enabled and non CommonOS variants to the sdk member variants.
- if s.Enabled() && !s.IsCommonOSVariant() {
- ctx := s.newDependencyContext(mctx)
- for _, memberListProperty := range s.memberTypeListProperties() {
- if memberListProperty.getter == nil {
- continue
- }
- names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
- if len(names) > 0 {
- memberType := memberListProperty.memberType
+func (s *sdk) DepsMutator(mctx android.BottomUpMutatorContext) {
+ // Add dependencies from non CommonOS variants to the sdk member variants.
+ if s.IsCommonOSVariant() {
+ return
+ }
- // Verify that the member type supports the specified traits.
- supportedTraits := memberType.SupportedTraits()
- for _, name := range names {
- requiredTraits := ctx.RequiredTraits(name)
- unsupportedTraits := requiredTraits.Subtract(supportedTraits)
- if !unsupportedTraits.Empty() {
- ctx.ModuleErrorf("sdk member %q has traits %s that are unsupported by its member type %q", name, unsupportedTraits, memberType.SdkPropertyName())
- }
- }
+ ctx := s.newDependencyContext(mctx)
+ for _, memberListProperty := range s.memberTypeListProperties() {
+ if memberListProperty.getter == nil {
+ continue
+ }
+ names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
+ if len(names) > 0 {
+ memberType := memberListProperty.memberType
- // Add dependencies using the appropriate tag.
- tag := memberListProperty.dependencyTag
- memberType.AddDependencies(ctx, tag, names)
+ // Verify that the member type supports the specified traits.
+ supportedTraits := memberType.SupportedTraits()
+ for _, name := range names {
+ requiredTraits := ctx.RequiredTraits(name)
+ unsupportedTraits := requiredTraits.Subtract(supportedTraits)
+ if !unsupportedTraits.Empty() {
+ ctx.ModuleErrorf("sdk member %q has traits %s that are unsupported by its member type %q",
+ name, unsupportedTraits, memberType.SdkPropertyName())
}
}
+
+ // Add dependencies using the appropriate tag.
+ tag := memberListProperty.dependencyTag
+ memberType.AddDependencies(ctx, tag, names)
}
}
}
-
-// Step 2: record that dependencies of SDK modules are members of the SDK modules
-func memberDepsMutator(mctx android.TopDownMutatorContext) {
- if s, ok := mctx.Module().(*sdk); ok {
- mySdkRef := android.ParseSdkRef(mctx, mctx.ModuleName(), "name")
- if s.snapshot() && mySdkRef.Unversioned() {
- mctx.PropertyErrorf("name", "sdk_snapshot should be named as <name>@<version>. "+
- "Did you manually modify Android.bp?")
- }
- if !s.snapshot() && !mySdkRef.Unversioned() {
- mctx.PropertyErrorf("name", "sdk shouldn't be named as <name>@<version>.")
- }
- if mySdkRef.Version != "" && mySdkRef.Version != "current" {
- if _, err := strconv.Atoi(mySdkRef.Version); err != nil {
- mctx.PropertyErrorf("name", "version %q is neither a number nor \"current\"", mySdkRef.Version)
- }
- }
-
- mctx.VisitDirectDeps(func(child android.Module) {
- if member, ok := child.(android.SdkAware); ok {
- member.MakeMemberOf(mySdkRef)
- }
- })
- }
-}
-
-// 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
-}
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 1ac405d..66c44c8 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -62,6 +62,9 @@
min_sdk_version: "2",
compile_dex: true,
permitted_packages: ["mylib"],
+ dex_preopt: {
+ profile: "art-profile",
+ },
}
java_sdk_library {
@@ -71,6 +74,9 @@
shared_library: false,
public: {enabled: true},
min_sdk_version: "2",
+ dex_preopt: {
+ profile: "art-profile",
+ },
}
`),
).RunTest(t)
@@ -105,6 +111,9 @@
visibility: ["//visibility:public"],
apex_available: ["myapex"],
shared_library: false,
+ dex_preopt: {
+ profile_guided: true,
+ },
public: {
jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
@@ -120,7 +129,11 @@
visibility: ["//visibility:public"],
apex_available: ["myapex"],
jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+ min_sdk_version: "2",
permitted_packages: ["mylib"],
+ dex_preopt: {
+ profile_guided: true,
+ },
}
prebuilt_systemserverclasspath_fragment {
@@ -181,6 +194,7 @@
visibility: ["//visibility:public"],
apex_available: ["myapex"],
jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+ min_sdk_version: "2",
permitted_packages: ["mylib"],
}
@@ -197,6 +211,54 @@
`)
})
+ t.Run("target-u", func(t *testing.T) {
+ testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "UpsideDownCake", `
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "mysdklibrary",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["myapex"],
+ shared_library: false,
+ dex_preopt: {
+ profile_guided: true,
+ },
+ 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: "mylib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["myapex"],
+ jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+ min_sdk_version: "2",
+ permitted_packages: ["mylib"],
+ dex_preopt: {
+ profile_guided: true,
+ },
+}
+
+prebuilt_systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["myapex"],
+ contents: [
+ "mylib",
+ "mysdklibrary",
+ ],
+}
+`)
+ })
+
t.Run("added-directly", func(t *testing.T) {
testSnapshotWithSystemServerClasspathFragment(t, commonSdk, `latest`, expectedLatestSnapshot)
})
diff --git a/sdk/update.go b/sdk/update.go
index baa2033..d98ab68 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -171,9 +171,9 @@
exportedComponentsInfo = ctx.OtherModuleProvider(child, android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
}
- var container android.SdkAware
+ var container android.Module
if parent != ctx.Module() {
- container = parent.(android.SdkAware)
+ container = parent.(android.Module)
}
minApiLevel := android.MinApiLevelForSdkSnapshot(ctx, child)
@@ -182,7 +182,7 @@
s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{
sdkVariant: s,
memberType: memberType,
- variant: child.(android.SdkAware),
+ variant: child.(android.Module),
minApiLevel: minApiLevel,
container: container,
export: export,
@@ -269,7 +269,7 @@
return supportedByTargetBuildRelease
}
-func appendUniqueVariants(variants []android.SdkAware, newVariant android.SdkAware) []android.SdkAware {
+func appendUniqueVariants(variants []android.Module, newVariant android.Module) []android.Module {
for _, v := range variants {
if v == newVariant {
return variants
@@ -357,6 +357,12 @@
// If the minApiLevel of the member is greater than the target API level then exclude it from
// this snapshot.
exclude := memberVariantDep.minApiLevel.GreaterThan(targetApiLevel)
+ // Always include host variants (e.g. host tools) in the snapshot.
+ // Host variants should not be guarded by a min_sdk_version check. In fact, host variants
+ // do not have a `min_sdk_version`.
+ if memberVariantDep.Host() {
+ exclude = false
+ }
addMember(name, export, exclude)
@@ -565,7 +571,7 @@
if m.deps != nil {
writeObjectPair("@deps", m.deps)
}
- for _, k := range android.SortedStringKeys(m.memberSpecific) {
+ for _, k := range android.SortedKeys(m.memberSpecific) {
v := m.memberSpecific[k]
writeObjectPair(k, v)
}
@@ -626,7 +632,7 @@
getModuleInfo(memberVariantDep.variant)
}
- for _, memberName := range android.SortedStringKeys(name2Info) {
+ for _, memberName := range android.SortedKeys(name2Info) {
info := name2Info[memberName]
modules = append(modules, info)
}
@@ -1246,12 +1252,12 @@
memberType android.SdkMemberType
// The variant that is added to the sdk.
- variant android.SdkAware
+ variant android.Module
// 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
+ container android.Module
// True if the member should be exported, i.e. accessible, from outside the sdk.
export bool
@@ -1263,6 +1269,11 @@
minApiLevel android.ApiLevel
}
+// Host returns true if the sdk member is a host variant (e.g. host tool)
+func (s *sdkMemberVariantDep) Host() bool {
+ return s.variant.Target().Os.Class == android.Host
+}
+
var _ android.SdkMember = (*sdkMember)(nil)
// sdkMember groups all the variants of a specific member module together along with the name of the
@@ -1270,14 +1281,14 @@
type sdkMember struct {
memberType android.SdkMemberType
name string
- variants []android.SdkAware
+ variants []android.Module
}
func (m *sdkMember) Name() string {
return m.name
}
-func (m *sdkMember) Variants() []android.SdkAware {
+func (m *sdkMember) Variants() []android.Module {
return m.variants
}
@@ -1362,24 +1373,24 @@
// by apex variant, where one is the default/platform variant and one is the APEX variant. In that
// case it picks the APEX variant. It picks the APEX variant because that is the behavior that would
// be expected
-func selectApexVariantsWhereAvailable(ctx *memberContext, variants []android.SdkAware) []android.SdkAware {
+func selectApexVariantsWhereAvailable(ctx *memberContext, variants []android.Module) []android.Module {
moduleCtx := ctx.sdkMemberContext
// Group the variants by coordinates.
- variantsByCoord := make(map[variantCoordinate][]android.SdkAware)
+ variantsByCoord := make(map[variantCoordinate][]android.Module)
for _, variant := range variants {
coord := getVariantCoordinate(ctx, variant)
variantsByCoord[coord] = append(variantsByCoord[coord], variant)
}
- toDiscard := make(map[android.SdkAware]struct{})
+ toDiscard := make(map[android.Module]struct{})
for coord, list := range variantsByCoord {
count := len(list)
if count == 1 {
continue
}
- variantsByApex := make(map[string]android.SdkAware)
+ variantsByApex := make(map[string]android.Module)
conflictDetected := false
for _, variant := range list {
apexInfo := moduleCtx.OtherModuleProvider(variant, android.ApexInfoProvider).(android.ApexInfo)
@@ -1421,7 +1432,7 @@
// If there are any variants to discard then remove them from the list of variants, while
// preserving the order.
if len(toDiscard) > 0 {
- filtered := []android.SdkAware{}
+ filtered := []android.Module{}
for _, variant := range variants {
if _, ok := toDiscard[variant]; !ok {
filtered = append(filtered, variant)
@@ -1708,7 +1719,7 @@
}
// Create the image variant info in a fixed order.
- for _, imageVariantName := range android.SortedStringKeys(variantsByImage) {
+ for _, imageVariantName := range android.SortedKeys(variantsByImage) {
variants := variantsByImage[imageVariantName]
archInfo.imageVariantInfos = append(archInfo.imageVariantInfos, newImageVariantSpecificInfo(ctx, imageVariantName, variantPropertiesFactory, variants))
}
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index d1beaba..c921ca6 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -103,12 +103,6 @@
Recovery_available *bool
}
-// Test option struct.
-type TestOptions struct {
- // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
- Unit_test *bool
-}
-
type TestProperties struct {
// list of compatibility suites (for example "cts", "vts") that the module should be
// installed into.
@@ -153,7 +147,7 @@
Per_testcase_directory *bool
// Test options.
- Test_options TestOptions
+ Test_options android.CommonTestOptions
}
type ShBinary struct {
@@ -323,7 +317,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...),
@@ -385,8 +379,16 @@
}
configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.PushFilePreparer", options})
}
- s.testConfig = tradefed.AutoGenShellTestConfig(ctx, s.testProperties.Test_config,
- s.testProperties.Test_config_template, s.testProperties.Test_suites, configs, s.testProperties.Auto_gen_config, s.outputFilePath.Base())
+ s.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: s.testProperties.Test_config,
+ TestConfigTemplateProp: s.testProperties.Test_config_template,
+ TestSuites: s.testProperties.Test_suites,
+ Config: configs,
+ AutoGenConfig: s.testProperties.Auto_gen_config,
+ OutputFileName: s.outputFilePath.Base(),
+ DeviceTemplate: "${ShellTestConfigTemplate}",
+ HostTemplate: "${ShellTestConfigTemplate}",
+ })
s.dataModules = make(map[string]android.Path)
ctx.VisitDirectDeps(func(dep android.Module) {
@@ -464,10 +466,9 @@
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))
+
+ s.testProperties.Test_options.SetAndroidMkEntries(entries)
},
},
}}
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
index 9793218..edcc163 100644
--- a/snapshot/host_snapshot.go
+++ b/snapshot/host_snapshot.go
@@ -96,6 +96,7 @@
var jsonData []SnapshotJsonFlags
var metaPaths android.Paths
+ installedNotices := make(map[string]bool)
metaZipFile := android.PathForModuleOut(ctx, fileName).OutputPath
// Create JSON file based on the direct dependencies
@@ -104,12 +105,14 @@
if desc != nil {
jsonData = append(jsonData, *desc)
}
- if len(dep.EffectiveLicenseFiles()) > 0 {
- noticeFile := android.PathForModuleOut(ctx, "NOTICE_FILES", dep.Name()+".txt").OutputPath
- android.CatFileRule(ctx, dep.EffectiveLicenseFiles(), noticeFile)
- metaPaths = append(metaPaths, noticeFile)
+ for _, notice := range dep.EffectiveLicenseFiles() {
+ if _, ok := installedNotices[notice.String()]; !ok {
+ installedNotices[notice.String()] = true
+ noticeOut := android.PathForModuleOut(ctx, "NOTICE_FILES", notice.String()).OutputPath
+ CopyFileToOutputPathRule(pctx, ctx, notice, noticeOut)
+ metaPaths = append(metaPaths, noticeOut)
+ }
}
-
})
// Sort notice paths and json data for repeatble build
sort.Slice(jsonData, func(i, j int) bool {
@@ -220,8 +223,7 @@
}
if path.Valid() && path.String() != "" {
- return &SnapshotJsonFlags{
- ModuleName: m.Name(),
+ props := &SnapshotJsonFlags{
ModuleStemName: moduleStem,
Filename: path.String(),
Required: append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...),
@@ -229,6 +231,8 @@
RustProcMacro: procMacro,
CrateName: crateName,
}
+ props.InitBaseSnapshotProps(m)
+ return props
}
return nil
}
diff --git a/snapshot/snapshot.go b/snapshot/snapshot.go
index 206ecc9..c95a537 100644
--- a/snapshot/snapshot.go
+++ b/snapshot/snapshot.go
@@ -26,6 +26,10 @@
var pctx = android.NewPackageContext("android/soong/snapshot")
+func init() {
+ pctx.Import("android/soong/android")
+}
+
type SnapshotSingleton struct {
// Name, e.g., "vendor", "recovery", "ramdisk".
name string
@@ -48,8 +52,18 @@
Fake bool
}
+// The output files to be included in the snapshot.
+type SnapshotPaths struct {
+ // All files to be included in the snapshot
+ OutputFiles android.Paths
+
+ // Notice files of the snapshot output files
+ NoticeFiles android.Paths
+}
+
// Interface of function to capture snapshot from each module
-type GenerateSnapshotAction func(snapshot SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths
+// Returns snapshot ouputs and notice files.
+type GenerateSnapshotAction func(snapshot SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) SnapshotPaths
var snapshotActionList []GenerateSnapshotAction
@@ -74,9 +88,19 @@
snapshotDir = filepath.Join("fake", snapshotDir)
}
snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
+ noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+ installedNotices := make(map[string]bool)
for _, f := range snapshotActionList {
- snapshotOutputs = append(snapshotOutputs, f(*c, ctx, snapshotArchDir)...)
+ snapshotPaths := f(*c, ctx, snapshotArchDir)
+ snapshotOutputs = append(snapshotOutputs, snapshotPaths.OutputFiles...)
+ for _, notice := range snapshotPaths.NoticeFiles {
+ if _, ok := installedNotices[notice.String()]; !ok {
+ installedNotices[notice.String()] = true
+ snapshotOutputs = append(snapshotOutputs, CopyFileRule(
+ pctx, ctx, notice, filepath.Join(noticeDir, notice.String())))
+ }
+ }
}
// All artifacts are ready. Sort them to normalize ninja and then zip.
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index 8e5dfe4..fb4ee0c 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -118,5 +118,21 @@
CrateName string `json:",omitempty"`
// dependencies
- Required []string `json:",omitempty"`
+ Required []string `json:",omitempty"`
+ Overrides []string `json:",omitempty"`
+
+ // license information
+ LicenseKinds []string `json:",omitempty"`
+ LicenseTexts []string `json:",omitempty"`
+}
+
+func (prop *SnapshotJsonFlags) InitBaseSnapshotPropsWithName(m android.Module, name string) {
+ prop.ModuleName = name
+
+ prop.LicenseKinds = m.EffectiveLicenseKinds()
+ prop.LicenseTexts = m.EffectiveLicenseFiles().Strings()
+}
+
+func (prop *SnapshotJsonFlags) InitBaseSnapshotProps(m android.Module) {
+ prop.InitBaseSnapshotPropsWithName(m, m.Name())
}
diff --git a/snapshot/util.go b/snapshot/util.go
index 806ac90..c87c508 100644
--- a/snapshot/util.go
+++ b/snapshot/util.go
@@ -21,17 +21,25 @@
return outPath
}
-func CopyFileRule(pctx android.PackageContext, ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
- outPath := android.PathForOutput(ctx, out)
+type buildContext interface {
+ Build(pctx android.PackageContext, params android.BuildParams)
+}
+
+func CopyFileToOutputPathRule(pctx android.PackageContext, ctx buildContext, path android.Path, outPath android.OutputPath) {
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: path,
Output: outPath,
- Description: "copy " + path.String() + " -> " + out,
+ Description: "copy " + path.String() + " -> " + outPath.String(),
Args: map[string]string{
- "cpFlags": "-f -L",
+ "cpFlags": "-L",
},
})
+}
+
+func CopyFileRule(pctx android.PackageContext, ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+ outPath := android.PathForOutput(ctx, out)
+ CopyFileToOutputPathRule(pctx, ctx, path, outPath)
return outPath
}
diff --git a/soong_ui.bash b/soong_ui.bash
index 49c4b78..8e7cd19 100755
--- a/soong_ui.bash
+++ b/soong_ui.bash
@@ -14,38 +14,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# To track how long we took to startup. %N isn't supported on Darwin, but
-# that's detected in the Go code, which skips calculating the startup time.
-export TRACE_BEGIN_SOONG=$(date +%s%N)
+# To track how long we took to startup.
+case $(uname -s) in
+ Darwin)
+ export TRACE_BEGIN_SOONG=`$T/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
+ ;;
+ *)
+ export TRACE_BEGIN_SOONG=$(date +%s%N)
+ ;;
+esac
-# Function to find top of the source tree (if $TOP isn't set) by walking up the
-# tree.
-function gettop
-{
- local TOPFILE=build/soong/root.bp
- if [ -n "${TOP-}" -a -f "${TOP-}/${TOPFILE}" ] ; then
- # The following circumlocution ensures we remove symlinks from TOP.
- (cd $TOP; PWD= /bin/pwd)
- else
- if [ -f $TOPFILE ] ; then
- # The following circumlocution (repeated below as well) ensures
- # that we record the true directory name and not one that is
- # faked up with symlink names.
- PWD= /bin/pwd
- else
- local HERE=$PWD
- T=
- while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
- \cd ..
- T=`PWD= /bin/pwd -P`
- done
- \cd $HERE
- if [ -f "$T/$TOPFILE" ]; then
- echo $T
- fi
- fi
- fi
-}
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../make/shell_utils.sh
+require_top
# Save the current PWD for use in soong_ui
export ORIGINAL_PWD=${PWD}
@@ -53,8 +33,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
+soong_build_go mk2rbc android/soong/mk2rbc/mk2rbc
+soong_build_go rbcrun rbcrun/rbcrun
cd ${TOP}
exec "$(getoutdir)/soong_ui" "$@"
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
index 3e51fa1..a97f71b 100644
--- a/starlark_fmt/format.go
+++ b/starlark_fmt/format.go
@@ -17,6 +17,7 @@
import (
"fmt"
"sort"
+ "strconv"
"strings"
)
@@ -34,7 +35,11 @@
// PrintBool returns a Starlark compatible bool string.
func PrintBool(item bool) string {
- return strings.Title(fmt.Sprintf("%t", item))
+ if item {
+ return "True"
+ } else {
+ return "False"
+ }
}
// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
@@ -84,6 +89,16 @@
return PrintDict(formattedValueDict, indentLevel)
}
+// PrintStringIntDict returns a Starlark-compatible string formatted as dictionary with
+// string keys and int values.
+func PrintStringIntDict(dict map[string]int, indentLevel int) string {
+ valDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ valDict[k] = strconv.Itoa(v)
+ }
+ return PrintDict(valDict, 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 {
diff --git a/symbol_inject/cmd/symbol_inject.go b/symbol_inject/cmd/symbol_inject.go
index 1397b37..89b3619 100644
--- a/symbol_inject/cmd/symbol_inject.go
+++ b/symbol_inject/cmd/symbol_inject.go
@@ -94,4 +94,13 @@
os.Remove(*output)
os.Exit(5)
}
+
+ if file.IsMachoFile {
+ err = symbol_inject.CodeSignMachoFile(*output)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Remove(*output)
+ os.Exit(6)
+ }
+ }
}
diff --git a/symbol_inject/macho.go b/symbol_inject/macho.go
index 6ee3f4f..ca3d50e 100644
--- a/symbol_inject/macho.go
+++ b/symbol_inject/macho.go
@@ -16,8 +16,12 @@
import (
"debug/macho"
+ "encoding/binary"
"fmt"
"io"
+ "os"
+ "os/exec"
+ "path/filepath"
"sort"
"strings"
)
@@ -40,7 +44,7 @@
return symbols[i].Value < symbols[j].Value
})
- file := &File{}
+ file := &File{IsMachoFile: true}
for _, section := range machoFile.Sections {
file.Sections = append(file.Sections, &Section{
@@ -95,3 +99,82 @@
return nil
}
+
+func CodeSignMachoFile(path string) error {
+ filename := filepath.Base(path)
+ cmd := exec.Command("/usr/bin/codesign", "--force", "-s", "-", "-i", filename, path)
+ if err := cmd.Run(); err != nil {
+ return err
+ }
+ return modifyCodeSignFlags(path)
+}
+
+const LC_CODE_SIGNATURE = 0x1d
+const CSSLOT_CODEDIRECTORY = 0
+
+// To make codesign not invalidated by stripping, modify codesign flags to 0x20002
+// (adhoc | linkerSigned).
+func modifyCodeSignFlags(path string) error {
+ f, err := os.OpenFile(path, os.O_RDWR, 0)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ // Step 1: find code signature section.
+ machoFile, err := macho.NewFile(f)
+ if err != nil {
+ return err
+ }
+ var codeSignSectionOffset uint32 = 0
+ var codeSignSectionSize uint32 = 0
+ for _, l := range machoFile.Loads {
+ data := l.Raw()
+ cmd := machoFile.ByteOrder.Uint32(data)
+ if cmd == LC_CODE_SIGNATURE {
+ codeSignSectionOffset = machoFile.ByteOrder.Uint32(data[8:])
+ codeSignSectionSize = machoFile.ByteOrder.Uint32(data[12:])
+ }
+ }
+ if codeSignSectionOffset == 0 {
+ return fmt.Errorf("code signature section not found")
+ }
+
+ data := make([]byte, codeSignSectionSize)
+ _, err = f.ReadAt(data, int64(codeSignSectionOffset))
+ if err != nil {
+ return err
+ }
+
+ // Step 2: get flags offset.
+ blobCount := binary.BigEndian.Uint32(data[8:])
+ off := 12
+ var codeDirectoryOff uint32 = 0
+ for blobCount > 0 {
+ blobType := binary.BigEndian.Uint32(data[off:])
+ if blobType == CSSLOT_CODEDIRECTORY {
+ codeDirectoryOff = binary.BigEndian.Uint32(data[off+4:])
+ break
+ }
+ blobCount--
+ off += 8
+ }
+ if codeDirectoryOff == 0 {
+ return fmt.Errorf("no code directory in code signature section")
+ }
+ flagsOff := codeSignSectionOffset + codeDirectoryOff + 12
+
+ // Step 3: modify flags.
+ flagsData := make([]byte, 4)
+ _, err = f.ReadAt(flagsData, int64(flagsOff))
+ if err != nil {
+ return err
+ }
+ oldFlags := binary.BigEndian.Uint32(flagsData)
+ if oldFlags != 0x2 {
+ return fmt.Errorf("unexpected flags in code signature section: 0x%x", oldFlags)
+ }
+ binary.BigEndian.PutUint32(flagsData, 0x20002)
+ _, err = f.WriteAt(flagsData, int64(flagsOff))
+ return err
+}
diff --git a/symbol_inject/symbol_inject.go b/symbol_inject/symbol_inject.go
index 2a3d67e..77aff6f 100644
--- a/symbol_inject/symbol_inject.go
+++ b/symbol_inject/symbol_inject.go
@@ -161,9 +161,10 @@
}
type File struct {
- r io.ReaderAt
- Symbols []*Symbol
- Sections []*Section
+ r io.ReaderAt
+ Symbols []*Symbol
+ Sections []*Section
+ IsMachoFile bool
}
type Symbol struct {
diff --git a/sysprop/Android.bp b/sysprop/Android.bp
index 1d5eb31..e5263fe 100644
--- a/sysprop/Android.bp
+++ b/sysprop/Android.bp
@@ -9,6 +9,7 @@
"blueprint",
"soong",
"soong-android",
+ "soong-bp2build",
"soong-cc",
"soong-java",
],
@@ -18,6 +19,7 @@
],
testSrcs: [
"sysprop_test.go",
+ "sysprop_library_conversion_test.go",
],
pluginFor: ["soong_build"],
}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index a29d4c3..0edbb7c 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -23,6 +23,7 @@
"path"
"sync"
+ "android/soong/bazel"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -125,6 +126,7 @@
type syspropLibrary struct {
android.ModuleBase
android.ApexModuleBase
+ android.BazelModuleBase
properties syspropLibraryProperties
@@ -334,8 +336,8 @@
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
// sysprop_library module itself is defined as a FAKE module to perform API check.
// Actual implementation libraries are created on LoadHookMutator
- fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
- fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name())
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # sysprop.syspropLibrary")
+ fmt.Fprintln(w, "LOCAL_MODULE :=", m.Name())
data.Entries.WriteLicenseVariables(w)
fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n")
fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n")
@@ -363,7 +365,10 @@
// sysprop_library creates schematized APIs from sysprop description files (.sysprop).
// Both Java and C++ modules can link against sysprop_library, and API stability check
// against latest APIs (see build/soong/scripts/freeze-sysprop-api-files.sh)
-// is performed.
+// is performed. Note that the generated C++ module has its name prefixed with
+// `lib`, and it is this module that should be depended on from other C++
+// modules; i.e., if the sysprop_library module is named `foo`, C++ modules
+// should depend on `libfoo`.
func syspropLibraryFactory() android.Module {
m := &syspropLibrary{}
@@ -372,6 +377,7 @@
)
android.InitAndroidModule(m)
android.InitApexModule(m)
+ android.InitBazelModule(m)
android.AddLoadHook(m, func(ctx android.LoadHookContext) { syspropLibraryHook(ctx, m) })
return m
}
@@ -403,6 +409,9 @@
Host_supported *bool
Apex_available []string
Min_sdk_version *string
+ Bazel_module struct {
+ Bp2build_available *bool
+ }
}
type javaLibraryProperties struct {
@@ -483,6 +492,11 @@
ccProps.Host_supported = m.properties.Host_supported
ccProps.Apex_available = m.ApexProperties.Apex_available
ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
+ // A Bazel macro handles this, so this module does not need to be handled
+ // in bp2build
+ // TODO(b/237810289) perhaps do something different here so that we aren't
+ // also disabling these modules in mixed builds
+ ccProps.Bazel_module.Bp2build_available = proptools.BoolPtr(false)
ctx.CreateModule(cc.LibraryFactory, &ccProps)
scope := "internal"
@@ -557,3 +571,16 @@
*libraries = append(*libraries, "//"+ctx.ModuleDir()+":"+ctx.ModuleName())
}
}
+
+// TODO(b/240463568): Additional properties will be added for API validation
+func (m *syspropLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ labels := cc.SyspropLibraryLabels{
+ SyspropLibraryLabel: m.BaseModuleName(),
+ SharedLibraryLabel: m.CcImplementationModuleName(),
+ StaticLibraryLabel: cc.BazelLabelNameForStaticModule(m.CcImplementationModuleName()),
+ }
+ cc.Bp2buildSysprop(ctx,
+ labels,
+ bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Srcs)),
+ m.properties.Cpp.Min_sdk_version)
+}
diff --git a/sysprop/sysprop_library_conversion_test.go b/sysprop/sysprop_library_conversion_test.go
new file mode 100644
index 0000000..89adf7d
--- /dev/null
+++ b/sysprop/sysprop_library_conversion_test.go
@@ -0,0 +1,110 @@
+// 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 sysprop
+
+import (
+ "testing"
+
+ "android/soong/bp2build"
+)
+
+func TestSyspropLibrarySimple(t *testing.T) {
+ bp2build.RunBp2BuildTestCaseSimple(t, bp2build.Bp2buildTestCase{
+ Description: "sysprop_library simple",
+ ModuleTypeUnderTest: "sysprop_library",
+ ModuleTypeUnderTestFactory: syspropLibraryFactory,
+ Filesystem: map[string]string{
+ "foo.sysprop": "",
+ "bar.sysprop": "",
+ },
+ Blueprint: `
+sysprop_library {
+ name: "sysprop_foo",
+ srcs: [
+ "foo.sysprop",
+ "bar.sysprop",
+ ],
+ property_owner: "Platform",
+}
+`,
+ ExpectedBazelTargets: []string{
+ bp2build.MakeBazelTargetNoRestrictions("sysprop_library",
+ "sysprop_foo",
+ bp2build.AttrNameToString{
+ "srcs": `[
+ "foo.sysprop",
+ "bar.sysprop",
+ ]`,
+ }),
+ bp2build.MakeBazelTargetNoRestrictions("cc_sysprop_library_shared",
+ "libsysprop_foo",
+ bp2build.AttrNameToString{
+ "dep": `":sysprop_foo"`,
+ }),
+ bp2build.MakeBazelTargetNoRestrictions("cc_sysprop_library_static",
+ "libsysprop_foo_bp2build_cc_library_static",
+ bp2build.AttrNameToString{
+ "dep": `":sysprop_foo"`,
+ }),
+ },
+ })
+}
+
+func TestSyspropLibraryCppMinSdkVersion(t *testing.T) {
+ bp2build.RunBp2BuildTestCaseSimple(t, bp2build.Bp2buildTestCase{
+ Description: "sysprop_library with min_sdk_version",
+ ModuleTypeUnderTest: "sysprop_library",
+ ModuleTypeUnderTestFactory: syspropLibraryFactory,
+ Filesystem: map[string]string{
+ "foo.sysprop": "",
+ "bar.sysprop": "",
+ },
+ Blueprint: `
+sysprop_library {
+ name: "sysprop_foo",
+ srcs: [
+ "foo.sysprop",
+ "bar.sysprop",
+ ],
+ cpp: {
+ min_sdk_version: "5",
+ },
+ property_owner: "Platform",
+}
+`,
+ ExpectedBazelTargets: []string{
+ bp2build.MakeBazelTargetNoRestrictions("sysprop_library",
+ "sysprop_foo",
+ bp2build.AttrNameToString{
+ "srcs": `[
+ "foo.sysprop",
+ "bar.sysprop",
+ ]`,
+ }),
+ bp2build.MakeBazelTargetNoRestrictions("cc_sysprop_library_shared",
+ "libsysprop_foo",
+ bp2build.AttrNameToString{
+ "dep": `":sysprop_foo"`,
+ "min_sdk_version": `"5"`,
+ }),
+ bp2build.MakeBazelTargetNoRestrictions("cc_sysprop_library_static",
+ "libsysprop_foo_bp2build_cc_library_static",
+ bp2build.AttrNameToString{
+ "dep": `":sysprop_foo"`,
+ "min_sdk_version": `"5"`,
+ }),
+ },
+ })
+}
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 88ef615..80b86e0 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -209,32 +209,32 @@
cc_library {
name: "cc-client-platform",
srcs: ["d.cpp"],
- static_libs: ["sysprop-platform"],
+ static_libs: ["libsysprop-platform"],
}
cc_library_static {
name: "cc-client-platform-static",
srcs: ["d.cpp"],
- whole_static_libs: ["sysprop-platform"],
+ whole_static_libs: ["libsysprop-platform"],
}
cc_library {
name: "cc-client-product",
srcs: ["d.cpp"],
product_specific: true,
- static_libs: ["sysprop-platform-on-product", "sysprop-vendor-on-product"],
+ static_libs: ["libsysprop-platform-on-product", "libsysprop-vendor-on-product"],
}
cc_library {
name: "cc-client-vendor",
srcs: ["d.cpp"],
soc_specific: true,
- static_libs: ["sysprop-platform", "sysprop-vendor"],
+ static_libs: ["libsysprop-platform", "libsysprop-vendor"],
}
cc_binary_host {
name: "hostbin",
- static_libs: ["sysprop-platform"],
+ static_libs: ["libsysprop-platform"],
}
`)
diff --git a/tests/androidmk_test.sh b/tests/androidmk_test.sh
index 331dc77..b81828b 100755
--- a/tests/androidmk_test.sh
+++ b/tests/androidmk_test.sh
@@ -5,7 +5,7 @@
# How to run: bash path-to-script/androidmk_test.sh
# Tests of converting license functionality of the androidmk tool
REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
-$REAL_TOP/build/soong/soong_ui.bash --make-mode androidmk
+"$REAL_TOP/build/soong/soong_ui.bash" --make-mode androidmk
source "$(dirname "$0")/lib.sh"
@@ -113,11 +113,14 @@
run_androidmk_test "a/b/c/d/Android.mk" "a/b/c/d/Android.bp"
}
-run_androidmk_test () {
+function run_androidmk_test {
export ANDROID_BUILD_TOP="$MOCK_TOP"
-
- local out=$($REAL_TOP/*/host/*/bin/androidmk "$1")
- local expected=$(<"$2")
+ local -r androidmk=("$REAL_TOP"/*/host/*/bin/androidmk)
+ if [[ ${#androidmk[@]} -ne 1 ]]; then
+ fail "Multiple androidmk binaries found: ${androidmk[*]}"
+ fi
+ local -r out=$("${androidmk[0]}" "$1")
+ local -r expected=$(<"$2")
if [[ "$out" != "$expected" ]]; then
ANDROID_BUILD_TOP="$REAL_TOP"
@@ -130,6 +133,4 @@
echo "Succeeded"
}
-test_rewrite_license_property_inside_current_directory
-
-test_rewrite_license_property_outside_current_directory
+scan_and_run_tests
diff --git a/tests/apex_cc_module_arch_variant_tests.sh b/tests/apex_cc_module_arch_variant_tests.sh
new file mode 100755
index 0000000..1f5e003
--- /dev/null
+++ b/tests/apex_cc_module_arch_variant_tests.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+# 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.
+
+set -uo pipefail
+
+# Integration test for verifying arch variant cflags set on cc modules included
+# in Bazel-built apexes in the real source tree.
+
+if [ ! -e "build/make/core/Makefile" ]; then
+ echo "$0 must be run from the top of the Android source tree."
+ exit 1
+fi
+
+############
+# Test Setup
+############
+
+OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
+BAZEL_OUTPUT_DIR="$OUTPUT_DIR/bazel"
+
+export TARGET_PRODUCT="aosp_arm64"
+[ "$#" -ge 1 ] && export TARGET_PRODUCT="$1"
+ARCH_VARIANT_CFLAG="armv8-a"
+[ "$#" -ge 2 ] && ARCH_VARIANT_CFLAG="$2"
+CPU_VARIANT_CFLAG=""
+[ "$#" -ge 3 ] && CPU_VARIANT_CFLAG="$3"
+
+function call_bazel() {
+ build/bazel/bin/bazel --output_base="$BAZEL_OUTPUT_DIR" $@
+}
+
+function cleanup {
+ # call bazel clean because some bazel outputs don't have w bits.
+ call_bazel clean
+ rm -rf "${OUTPUT_DIR}"
+}
+trap cleanup EXIT
+
+######################
+# Run bp2build / Bazel
+######################
+build/soong/soong_ui.bash --make-mode BP2BUILD_VERBOSE=1 --skip-soong-tests bp2build
+
+# Number of CppCompile actions with arch variant flag
+actions_with_arch_variant_num=$(call_bazel aquery --config=bp2build --config=ci --config=android \
+ 'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal))' | grep -c \'-march=$ARCH_VARIANT_CFLAG\')
+
+# Number of all CppCompile actions
+all_cppcompile_actions_num=0
+aquery_summary=$(call_bazel aquery --config=bp2build --config=ci --config=android --output=summary \
+ 'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal))' \
+ | egrep -o '.*opt-ST.*: ([0-9]+)$' \
+ | cut -d: -f2 -)
+
+while read -r num;
+do
+ all_cppcompile_actions_num=$(($all_cppcompile_actions_num + $num))
+done <<< "$aquery_summary"
+
+if [ $actions_with_arch_variant_num -eq $all_cppcompile_actions_num ]
+then
+ echo "Pass: arch variant is set."
+else
+ echo "Error: number of CppCompile actions with arch variant set: actual=$actions_with_arch_variant_num, expected=$all_cppcompile_actions_num"
+ exit 1
+fi
+
+if [ $CPU_VARIANT_CFLAG ]
+then
+ # Number of CppCompiler actions with cpu variant flag
+ actions_with_cpu_variant_num=$(call_bazel aquery --config=bp2build --config=ci --config=android \
+ 'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal))' | grep -c "\-mcpu=$CPU_VARIANT_CFLAG")
+
+ if [ $actions_with_cpu_variant_num -eq $all_cppcompile_actions_num ]
+ then
+ echo "Pass: cpu variant is set."
+ else
+ echo "Error: number of CppCompile actions with cpu variant set: actual=$actions_with_cpu_variant_num, expected=$all_cppcompile_actions_num"
+ exit 1
+ fi
+fi
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
new file mode 100755
index 0000000..e350323
--- /dev/null
+++ b/tests/apex_comparison_tests.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+
+# 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.
+
+set -euo pipefail
+
+# Soong/Bazel integration test for building unbundled apexes in the real source tree.
+#
+# These tests build artifacts from head and compares their contents.
+
+if [ ! -e "build/make/core/Makefile" ]; then
+ echo "$0 must be run from the top of the Android source tree."
+ exit 1
+fi
+
+############
+# Test Setup
+############
+
+OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
+SOONG_OUTPUT_DIR="$OUTPUT_DIR/soong"
+BAZEL_OUTPUT_DIR="$OUTPUT_DIR/bazel"
+
+export TARGET_PRODUCT="module_arm"
+[ "$#" -eq 1 ] && export TARGET_PRODUCT="$1"
+
+function call_bazel() {
+ build/bazel/bin/bazel --output_base="$BAZEL_OUTPUT_DIR" $@
+}
+
+function cleanup {
+ # call bazel clean because some bazel outputs don't have w bits.
+ call_bazel clean
+ rm -rf "${OUTPUT_DIR}"
+}
+trap cleanup EXIT
+
+###########
+# Run Soong
+###########
+export UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true # don't rely on prebuilts
+export TARGET_BUILD_APPS="com.android.adbd com.android.tzdata build.bazel.examples.apex.minimal"
+packages/modules/common/build/build_unbundled_mainline_module.sh \
+ --product "$TARGET_PRODUCT" \
+ --dist_dir "$SOONG_OUTPUT_DIR"
+
+######################
+# Run bp2build / Bazel
+######################
+build/soong/soong_ui.bash --make-mode BP2BUILD_VERBOSE=1 --skip-soong-tests bp2build
+
+BAZEL_OUT="$(call_bazel info --config=bp2build output_path)"
+
+call_bazel build --config=bp2build --config=ci --config=android \
+ //packages/modules/adb/apex:com.android.adbd \
+ //system/timezone/apex:com.android.tzdata \
+ //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal
+BAZEL_ADBD="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files //packages/modules/adb/apex:com.android.adbd))"
+BAZEL_TZDATA="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files //system/timezone/apex:com.android.tzdata))"
+BAZEL_MINIMAL="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal))"
+
+# # Build debugfs separately, as it's not a dep of apexer, but needs to be an explicit arg.
+call_bazel build --config=bp2build --config=linux_x86_64 //external/e2fsprogs/debugfs //system/apex/tools:deapexer
+DEBUGFS_PATH="$(realpath $(call_bazel cquery --config=bp2build --config=linux_x86_64 --config=ci --output=files //external/e2fsprogs/debugfs))"
+DEAPEXER="bazel-bin/system/apex/tools/deapexer"
+DEAPEXER="$DEAPEXER --debugfs_path=$DEBUGFS_PATH"
+
+#######
+# Tests
+#######
+
+function compare_deapexer_list() {
+ local BAZEL_APEX=$1; shift
+ local APEX=$1; shift
+
+ # Compare the outputs of `deapexer list`, which lists the contents of the apex filesystem image.
+ local SOONG_APEX="$SOONG_OUTPUT_DIR/$APEX"
+
+ local SOONG_LIST="$OUTPUT_DIR/soong.list"
+ local BAZEL_LIST="$OUTPUT_DIR/bazel.list"
+
+ $DEAPEXER list "$SOONG_APEX" > "$SOONG_LIST"
+ $DEAPEXER list "$BAZEL_APEX" > "$BAZEL_LIST"
+
+ if cmp -s "$SOONG_LIST" "$BAZEL_LIST"
+ then
+ echo "ok: $APEX"
+ else
+ echo "contents of $APEX are different between Soong and Bazel:"
+ echo
+ echo expected
+ echo
+ cat "$SOONG_LIST"
+ echo
+ echo got
+ echo
+ cat "$BAZEL_LIST"
+ exit 1
+ fi
+}
+
+compare_deapexer_list "${BAZEL_ADBD}" com.android.adbd.apex
+compare_deapexer_list "${BAZEL_TZDATA}" com.android.tzdata.apex
+compare_deapexer_list "${BAZEL_MINIMAL}" build.bazel.examples.apex.minimal.apex
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index e92a561..5935247 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -17,11 +17,11 @@
function test_null_build() {
setup
run_soong
- local bootstrap_mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
- local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r bootstrap_mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
+ local -r output_mtime1=$(stat -c "%y" out/soong/build.ninja)
run_soong
- local bootstrap_mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
- local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r bootstrap_mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
+ local -r output_mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then
# Bootstrapping is always done. It doesn't take a measurable amount of time.
@@ -36,12 +36,12 @@
function test_soong_build_rebuilt_if_blueprint_changes() {
setup
run_soong
- local mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
+ local -r mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
sed -i 's/pluginGenSrcCmd/pluginGenSrcCmd2/g' build/blueprint/bootstrap/bootstrap.go
run_soong
- local mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
+ local -r mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
if [[ "$mtime1" == "$mtime2" ]]; then
fail "Bootstrap Ninja file did not change"
@@ -75,11 +75,10 @@
grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found"
}
-
function test_add_android_bp() {
setup
run_soong
- local mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
mkdir -p a
cat > a/Android.bp <<'EOF'
@@ -91,7 +90,7 @@
touch a/my_little_binary_host.py
run_soong
- local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$mtime1" == "$mtime2" ]]; then
fail "Output Ninja file did not change"
fi
@@ -142,7 +141,7 @@
EOF
touch a/my_little_binary_host.py
run_soong
- local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
local glob_deps_file=out/soong/globs/build/0.d
@@ -151,7 +150,7 @@
fi
run_soong
- local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
# There is an ineffiencency in glob that requires bpglob to rerun once for each glob to update
# the entry in the .ninja_log. It doesn't update the output file, but we can detect the rerun
@@ -160,15 +159,15 @@
fail "Glob deps file missing after second build"
fi
- local glob_deps_mtime2=$(stat -c "%y" "$glob_deps_file")
+ local -r glob_deps_mtime2=$(stat -c "%y" "$glob_deps_file")
if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
fail "Ninja file rewritten on null incremental build"
fi
run_soong
- local ninja_mtime3=$(stat -c "%y" out/soong/build.ninja)
- local glob_deps_mtime3=$(stat -c "%y" "$glob_deps_file")
+ local -r ninja_mtime3=$(stat -c "%y" out/soong/build.ninja)
+ local -r glob_deps_mtime3=$(stat -c "%y" "$glob_deps_file")
if [[ "$ninja_mtime2" != "$ninja_mtime3" ]]; then
fail "Ninja file rewritten on null incremental build"
@@ -192,12 +191,12 @@
EOF
touch a/my_little_binary_host.py
run_soong
- local mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
touch a/my_little_library.py
run_soong
- local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$mtime1" == "$mtime2" ]]; then
fail "Output Ninja file did not change"
fi
@@ -275,20 +274,48 @@
run_soong
grep -q "CHERRY IS RED" out/soong/build.ninja \
|| fail "second value of environment variable not used"
- local mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
run_soong
- local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$mtime1" != "$mtime2" ]]; then
fail "Output Ninja file changed when environment variable did not"
fi
}
+function test_create_global_include_directory() {
+ setup
+ run_soong
+ local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ # Soong needs to know if top level directories like hardware/ exist for use
+ # as global include directories. Make sure that doesn't cause regens for
+ # unrelated changes to the top level directory.
+ mkdir -p system/core
+
+ run_soong
+ local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+ if [[ "$mtime1" != "$mtime2" ]]; then
+ fail "Output Ninja file changed when top level directory changed"
+ fi
+
+ # Make sure it does regen if a missing directory in the path of a global
+ # include directory is added.
+ mkdir -p system/core/include
+
+ run_soong
+ local -r mtime3=$(stat -c "%y" out/soong/build.ninja)
+ if [[ "$mtime2" = "$mtime3" ]]; then
+ fail "Output Ninja file did not change when global include directory created"
+ fi
+
+}
+
function test_add_file_to_soong_build() {
setup
run_soong
- local mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
mkdir -p a
cat > a/Android.bp <<'EOF'
@@ -350,7 +377,7 @@
EOF
run_soong
- local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$mtime1" == "$mtime2" ]]; then
fail "Output Ninja file did not change"
fi
@@ -428,7 +455,7 @@
EOF
run_soong
- local mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
grep -q "Make it so" out/soong/build.ninja || fail "Original action not present"
@@ -460,7 +487,7 @@
EOF
run_soong
- local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$mtime1" == "$mtime2" ]]; then
fail "Output Ninja file did not change"
fi
@@ -485,20 +512,20 @@
setup
run_soong
- local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
run_soong soong_docs
- local docs_mtime1=$(stat -c "%y" out/soong/docs/soong_build.html)
+ local -r docs_mtime1=$(stat -c "%y" out/soong/docs/soong_build.html)
run_soong soong_docs
- local docs_mtime2=$(stat -c "%y" out/soong/docs/soong_build.html)
+ local -r docs_mtime2=$(stat -c "%y" out/soong/docs/soong_build.html)
if [[ "$docs_mtime1" != "$docs_mtime2" ]]; then
fail "Output Ninja file changed on null build"
fi
run_soong
- local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
fail "Output Ninja file changed on null build"
@@ -523,7 +550,7 @@
run_ninja BUILD_BROKEN_SRC_DIR_IS_WRITABLE=false ${EXPECTED_OUT} &> /dev/null && \
fail "Write to source tree should not work in a ReadOnly source tree"
- if grep -q "${ERROR_MSG}" ${ERROR_LOG} && grep -q "${ERROR_HINT_PATTERN}" ${ERROR_LOG} ; then
+ if grep -q "${ERROR_MSG}" "${ERROR_LOG}" && grep -q "${ERROR_HINT_PATTERN}" "${ERROR_LOG}" ; then
echo Error message and error hint found in logs >/dev/null
else
fail "Did not find Read-only error AND error hint in error.log"
@@ -533,7 +560,7 @@
run_ninja BUILD_BROKEN_SRC_DIR_IS_WRITABLE=true ${EXPECTED_OUT} &> /dev/null || \
fail "Write to source tree did not succeed in a ReadWrite source tree"
- if grep -q "${ERROR_MSG}\|${ERROR_HINT_PATTERN}" ${ERROR_LOG} ; then
+ if grep -q "${ERROR_MSG}\|${ERROR_HINT_PATTERN}" "${ERROR_LOG}" ; then
fail "Found read-only error OR error hint in error.log"
fi
}
@@ -547,12 +574,48 @@
function test_bp2build_generates_marker_file {
setup
- create_mock_bazel
run_soong bp2build
+ if [[ ! -f "./out/soong/bp2build_files_marker" ]]; then
+ fail "bp2build marker file was not generated"
+ fi
+
if [[ ! -f "./out/soong/bp2build_workspace_marker" ]]; then
- fail "Marker file was not generated"
+ fail "symlink forest marker file was not generated"
+ fi
+}
+
+function test_bp2build_add_irrelevant_file {
+ setup
+
+ mkdir -p a/b
+ touch a/b/c.txt
+ cat > a/b/Android.bp <<'EOF'
+filegroup {
+ name: "c",
+ srcs: ["c.txt"],
+ bazel_module: { bp2build_available: true },
+}
+EOF
+
+ run_soong bp2build
+ if [[ ! -e out/soong/bp2build/a/b/BUILD.bazel ]]; then
+ fail "BUILD file in symlink forest was not created";
+ fi
+
+ local -r mtime1=$(stat -c "%y" out/soong/bp2build/a/b/BUILD.bazel)
+
+ touch a/irrelevant.txt
+ run_soong bp2build
+ local -r mtime2=$(stat -c "%y" out/soong/bp2build/a/b/BUILD.bazel)
+
+ if [[ "$mtime1" != "$mtime2" ]]; then
+ fail "BUILD.bazel file was regenerated"
+ fi
+
+ if [[ ! -e "out/soong/workspace/a/irrelevant.txt" ]]; then
+ fail "New file was not symlinked into symlink forest"
fi
}
@@ -592,10 +655,10 @@
setup
run_soong bp2build
- local mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+ local -r mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
run_soong bp2build
- local mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+ local -r mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
if [[ "$mtime1" != "$mtime2" ]]; then
fail "Output Ninja file changed on null build"
@@ -652,26 +715,25 @@
setup
run_soong
- local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
run_soong json-module-graph
- local json_mtime1=$(stat -c "%y" out/soong/module-graph.json)
+ local -r json_mtime1=$(stat -c "%y" out/soong/module-graph.json)
run_soong
- local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
fail "Output Ninja file changed after writing JSON module graph"
fi
run_soong json-module-graph
- local json_mtime2=$(stat -c "%y" out/soong/module-graph.json)
+ local -r json_mtime2=$(stat -c "%y" out/soong/module-graph.json)
if [[ "$json_mtime1" != "$json_mtime2" ]]; then
fail "JSON module graph file changed after writing Ninja file"
fi
}
-
function test_bp2build_bazel_workspace_structure {
setup
@@ -737,11 +799,10 @@
|| fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place"
}
-function test_bp2build_reports_multiple_errors {
+function test_bp2build_fails_fast {
setup
mkdir -p "a/${GENERATED_BUILD_FILE_NAME}"
- touch a/a.txt
cat > a/Android.bp <<EOF
filegroup {
name: "a",
@@ -751,7 +812,6 @@
EOF
mkdir -p "b/${GENERATED_BUILD_FILE_NAME}"
- touch b/b.txt
cat > b/Android.bp <<EOF
filegroup {
name: "b",
@@ -764,27 +824,27 @@
fail "Build should have failed"
fi
- grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found"
- grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found"
+ # we should expect at least one error
+ grep -q -E "(a|b)/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for ${GENERATED_BUILD_FILE_NAME} not found"
}
function test_bp2build_back_and_forth_null_build {
setup
run_soong
- local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
+ local -r output_mtime1=$(stat -c "%y" out/soong/build.ninja)
run_soong bp2build
- local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
+ local -r output_mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$output_mtime1" != "$output_mtime2" ]]; then
fail "Output Ninja file changed when switching to bp2build"
fi
- local marker_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+ local -r marker_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
run_soong
- local output_mtime3=$(stat -c "%y" out/soong/build.ninja)
- local marker_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+ local -r output_mtime3=$(stat -c "%y" out/soong/build.ninja)
+ local -r marker_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
if [[ "$output_mtime1" != "$output_mtime3" ]]; then
fail "Output Ninja file changed when switching to regular build from bp2build"
fi
@@ -793,8 +853,8 @@
fi
run_soong bp2build
- local output_mtime4=$(stat -c "%y" out/soong/build.ninja)
- local marker_mtime3=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+ local -r output_mtime4=$(stat -c "%y" out/soong/build.ninja)
+ local -r marker_mtime3=$(stat -c "%y" out/soong/bp2build_workspace_marker)
if [[ "$output_mtime1" != "$output_mtime4" ]]; then
fail "Output Ninja file changed when switching back to bp2build"
fi
@@ -815,42 +875,47 @@
setup
run_soong queryview
- local output_mtime1=$(stat -c "%y" out/soong/queryview.marker)
+ local -r output_mtime1=$(stat -c "%y" out/soong/queryview.marker)
run_soong queryview
- local output_mtime2=$(stat -c "%y" out/soong/queryview.marker)
+ local -r output_mtime2=$(stat -c "%y" out/soong/queryview.marker)
if [[ "$output_mtime1" != "$output_mtime2" ]]; then
fail "Queryview marker file changed on null build"
fi
}
-test_smoke
-test_null_build
-test_soong_docs_smoke
-test_null_build_after_soong_docs
-test_soong_build_rebuilt_if_blueprint_changes
-test_glob_noop_incremental
-test_add_file_to_glob
-test_add_android_bp
-test_change_android_bp
-test_delete_android_bp
-test_add_file_to_soong_build
-test_glob_during_bootstrapping
-test_soong_build_rerun_iff_environment_changes
-test_multiple_soong_build_modes
-test_dump_json_module_graph
-test_json_module_graph_back_and_forth_null_build
-test_write_to_source_tree
-test_queryview_smoke
-test_queryview_null_build
-test_bp2build_smoke
-test_bp2build_generates_marker_file
-test_bp2build_null_build
-test_bp2build_back_and_forth_null_build
-test_bp2build_add_android_bp
-test_bp2build_add_to_glob
-test_bp2build_bazel_workspace_structure
-test_bp2build_bazel_workspace_add_file
-test_bp2build_build_file_precedence
-test_bp2build_reports_multiple_errors
+# This test verifies that adding a new glob to a blueprint file only
+# causes build.ninja to be regenerated on the *next* build, and *not*
+# the build after. (This is a regression test for a bug where globs
+# resulted in two successive regenerations.)
+function test_new_glob_incrementality {
+ setup
+
+ run_soong nothing
+ local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ mkdir -p globdefpkg/
+ cat > globdefpkg/Android.bp <<'EOF'
+filegroup {
+ name: "fg_with_glob",
+ srcs: ["*.txt"],
+}
+EOF
+
+ run_soong nothing
+ local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+ if [[ "$mtime1" == "$mtime2" ]]; then
+ fail "Ninja file was not regenerated, despite a new bp file"
+ fi
+
+ run_soong nothing
+ local -r mtime3=$(stat -c "%y" out/soong/build.ninja)
+
+ if [[ "$mtime2" != "$mtime3" ]]; then
+ fail "Ninja file was regenerated despite no previous bp changes"
+ fi
+}
+
+scan_and_run_tests
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 4f37c2b..68d7f8d 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -8,22 +8,82 @@
readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
-function test_bp2build_null_build() {
+function test_bp2build_null_build {
setup
run_soong bp2build
- local output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+ local -r output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
run_soong bp2build
- local output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+ local -r output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
if [[ "$output_mtime1" != "$output_mtime2" ]]; then
fail "Output bp2build marker file changed on null build"
fi
}
-test_bp2build_null_build
+# Tests that, if bp2build reruns due to a blueprint file changing, that
+# BUILD files whose contents are unchanged are not regenerated.
+function test_bp2build_unchanged {
+ setup
-function test_bp2build_null_build_with_globs() {
+ mkdir -p pkg
+ touch pkg/x.txt
+ cat > pkg/Android.bp <<'EOF'
+filegroup {
+ name: "x",
+ srcs: ["x.txt"],
+ bazel_module: {bp2build_available: true},
+ }
+EOF
+
+ run_soong bp2build
+ local -r buildfile_mtime1=$(stat -c "%y" out/soong/bp2build/pkg/BUILD.bazel)
+ local -r marker_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+
+ # Force bp2build to rerun by updating the timestamp of a blueprint file.
+ touch pkg/Android.bp
+
+ run_soong bp2build
+ local -r buildfile_mtime2=$(stat -c "%y" out/soong/bp2build/pkg/BUILD.bazel)
+ local -r marker_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+
+ if [[ "$marker_mtime1" == "$marker_mtime2" ]]; then
+ fail "Expected bp2build marker file to change"
+ fi
+ if [[ "$buildfile_mtime1" != "$buildfile_mtime2" ]]; then
+ fail "BUILD.bazel was updated even though contents are same"
+ fi
+}
+
+# Tests that blueprint files that are deleted are not present when the
+# bp2build tree is regenerated.
+function test_bp2build_deleted_blueprint {
+ setup
+
+ mkdir -p pkg
+ touch pkg/x.txt
+ cat > pkg/Android.bp <<'EOF'
+filegroup {
+ name: "x",
+ srcs: ["x.txt"],
+ bazel_module: {bp2build_available: true},
+ }
+EOF
+
+ run_soong bp2build
+ if [[ ! -e "./out/soong/bp2build/pkg/BUILD.bazel" ]]; then
+ fail "Expected pkg/BUILD.bazel to be generated"
+ fi
+
+ rm pkg/Android.bp
+
+ run_soong bp2build
+ if [[ -e "./out/soong/bp2build/pkg/BUILD.bazel" ]]; then
+ fail "Expected pkg/BUILD.bazel to be deleted"
+ fi
+}
+
+function test_bp2build_null_build_with_globs {
setup
mkdir -p foo/bar
@@ -36,21 +96,58 @@
touch foo/bar/a.txt foo/bar/b.txt
run_soong bp2build
- local output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+ local -r output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
run_soong bp2build
- local output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+ local -r output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
if [[ "$output_mtime1" != "$output_mtime2" ]]; then
fail "Output bp2build marker file changed on null build"
fi
}
-test_bp2build_null_build_with_globs
-
-function test_bp2build_generates_all_buildfiles {
+function test_different_relative_outdir {
setup
- create_mock_bazel
+
+ mkdir -p a
+ touch a/g.txt
+ cat > a/Android.bp <<'EOF'
+filegroup {
+ name: "g",
+ srcs: ["g.txt"],
+ bazel_module: {bp2build_available: true},
+ }
+EOF
+
+ # A directory under $MOCK_TOP
+ outdir=out2
+ trap "rm -rf $outdir" EXIT
+ # Modify OUT_DIR in a subshell so it doesn't affect the top level one.
+ (export OUT_DIR=$outdir; run_soong bp2build && run_bazel build --config=bp2build --config=ci //a:g)
+}
+
+function test_different_absolute_outdir {
+ setup
+
+ mkdir -p a
+ touch a/g.txt
+ cat > a/Android.bp <<'EOF'
+filegroup {
+ name: "g",
+ srcs: ["g.txt"],
+ bazel_module: {bp2build_available: true},
+ }
+EOF
+
+ # A directory under /tmp/...
+ outdir=$(mktemp -t -d st.XXXXX)
+ trap 'rm -rf $outdir' EXIT
+ # Modify OUT_DIR in a subshell so it doesn't affect the top level one.
+ (export OUT_DIR=$outdir; run_soong bp2build && run_bazel build --config=bp2build --config=ci //a:g)
+}
+
+function _bp2build_generates_all_buildfiles {
+ setup
mkdir -p foo/convertible_soong_module
cat > foo/convertible_soong_module/Android.bp <<'EOF'
@@ -103,15 +200,172 @@
fi
# NOTE: We don't actually use the extra BUILD file for anything here
- run_bazel build --package_path=out/soong/workspace //foo/...
+ run_bazel build --config=android --config=bp2build --config=ci //foo/...
- local the_answer_file="bazel-out/android_target-fastbuild/bin/foo/convertible_soong_module/the_answer.txt"
+ local -r the_answer_file="$(find -L bazel-out -name the_answer.txt)"
if [[ ! -f "${the_answer_file}" ]]; then
- fail "Expected '${the_answer_file}' to be generated, but was missing"
+ fail "Expected the_answer.txt to be generated, but was missing"
fi
if ! grep 42 "${the_answer_file}"; then
fail "Expected to find 42 in '${the_answer_file}'"
fi
}
-test_bp2build_generates_all_buildfiles
+function test_bp2build_generates_all_buildfiles {
+ _save_trap=$(trap -p EXIT)
+ trap '[[ $? -ne 0 ]] && echo Are you running this locally? Try changing --sandbox_tmpfs_path to something other than /tmp/ in build/bazel/linux.bazelrc.' EXIT
+ _bp2build_generates_all_buildfiles
+ eval "${_save_trap}"
+}
+
+function test_bp2build_symlinks_files {
+ setup
+ mkdir -p foo
+ touch foo/BLANK1
+ touch foo/BLANK2
+ touch foo/F2D
+ touch foo/BUILD
+
+ run_soong bp2build
+
+ if [[ -e "./out/soong/workspace/foo/BUILD" ]]; then
+ fail "./out/soong/workspace/foo/BUILD should be omitted"
+ fi
+ for file in BLANK1 BLANK2 F2D
+ do
+ if [[ ! -L "./out/soong/workspace/foo/$file" ]]; then
+ fail "./out/soong/workspace/foo/$file should exist"
+ fi
+ done
+ local -r BLANK1_BEFORE=$(stat -c %y "./out/soong/workspace/foo/BLANK1")
+
+ rm foo/BLANK2
+ rm foo/F2D
+ mkdir foo/F2D
+ touch foo/F2D/BUILD
+
+ run_soong bp2build
+
+ if [[ -e "./out/soong/workspace/foo/BUILD" ]]; then
+ fail "./out/soong/workspace/foo/BUILD should be omitted"
+ fi
+ local -r BLANK1_AFTER=$(stat -c %y "./out/soong/workspace/foo/BLANK1")
+ if [[ "$BLANK1_AFTER" != "$BLANK1_BEFORE" ]]; then
+ fail "./out/soong/workspace/foo/BLANK1 should be untouched"
+ fi
+ if [[ -e "./out/soong/workspace/foo/BLANK2" ]]; then
+ fail "./out/soong/workspace/foo/BLANK2 should be removed"
+ fi
+ if [[ -L "./out/soong/workspace/foo/F2D" ]] || [[ ! -d "./out/soong/workspace/foo/F2D" ]]; then
+ fail "./out/soong/workspace/foo/F2D should be a dir"
+ fi
+}
+
+function test_cc_correctness {
+ setup
+
+ 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 --config=android --config=bp2build --config=ci //a:qq
+ local -r output_mtime1=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
+
+ run_bazel build --config=android --config=bp2build --config=ci //a:qq
+ local -r 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 --config=android --config=bp2build --config=ci //a:qq
+ local -r 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
+}
+
+# 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
+}
+
+# Smoke test to verify api_bp2build worksapce does not contain any errors
+function test_api_bp2build_empty_build() {
+ setup
+ run_soong api_bp2build
+ run_bazel build --config=android --config=api_bp2build //:empty
+}
+
+# Verify that an *_api_contribution target can refer to an api file from
+# another Bazel package.
+function test_api_export_from_another_bazel_package() {
+ setup
+ # Parent dir Android.bp
+ mkdir -p foo
+ cat > foo/Android.bp << 'EOF'
+cc_library {
+ name: "libfoo",
+ stubs: {
+ symbol_file: "api/libfoo.map.txt",
+ },
+}
+EOF
+ # Child dir Android.bp
+ mkdir -p foo/api
+ cat > foo/api/Android.bp << 'EOF'
+package{}
+EOF
+ touch foo/api/libfoo.map.txt
+ # Run test
+ run_soong api_bp2build
+ run_bazel build --config=android --config=api_bp2build //foo:libfoo.contribution
+}
+
+scan_and_run_tests
diff --git a/tests/dcla_apex_comparison_test.sh b/tests/dcla_apex_comparison_test.sh
new file mode 100755
index 0000000..97ae97e
--- /dev/null
+++ b/tests/dcla_apex_comparison_test.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+# Soong/Bazel integration test to build the mainline modules in mixed build and
+# compare the DCLA libs extracted from those modules to ensure they are identical.
+
+if [ ! -e "build/make/core/Makefile" ]; then
+ echo "$0 must be run from the top of the Android source tree."
+ exit 1
+fi
+
+TARGET_PRODUCTS=(
+ module_arm64
+ module_x86_64
+)
+
+MODULES=(
+ # These modules depend on the DCLA libs
+ com.android.adbd
+ com.android.art
+ com.android.art.debug
+ com.android.art.testing
+ com.android.btservices
+ com.android.conscrypt
+ com.android.i18n
+ com.android.media
+ com.android.media.swcodec
+ com.android.resolv
+ com.android.runtime
+ com.android.tethering
+)
+
+DCLA_LIBS=(
+ libbase.so
+ libc++.so
+ libcrypto.so
+ libcutils.so
+)
+
+if [[ -z ${OUT_DIR+x} ]]; then
+ OUT_DIR="out"
+fi
+
+if [[ -z ${ANDROID_HOST_OUT+x} ]]; then
+ export ANDROID_HOST_OUT="out/host/linux-x86"
+fi
+
+######################
+# Build deapexer and debugfs
+######################
+DEAPEXER="${ANDROID_HOST_OUT}/bin/deapexer"
+DEBUGFS="${ANDROID_HOST_OUT}/bin/debugfs"
+if [[ ! -f "${DEAPEXER}" ]] || [[ ! -f "${DEBUGFS}" ]]; then
+ build/soong/soong_ui.bash --make-mode --skip-soong-tests deapexer debugfs
+fi
+
+DEAPEXER="${DEAPEXER} --debugfs_path=${DEBUGFS}"
+
+############
+# Test Setup
+############
+OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
+
+function cleanup {
+ rm -rf "${OUTPUT_DIR}"
+}
+trap cleanup EXIT
+
+#######
+# Tests
+#######
+
+function extract_dcla_libs() {
+ local product=$1; shift
+ for module in "${MODULES[@]}"; do
+ local apex="${OUTPUT_DIR}/${product}/${module}.apex"
+ local extract_dir="${OUTPUT_DIR}/${product}/${module}/extract"
+
+ $DEAPEXER extract "${apex}" "${extract_dir}"
+ done
+}
+
+function compare_dcla_libs() {
+ local product=$1; shift
+
+ for lib in "${DCLA_LIBS[@]}"; do
+ for arch in lib lib64; do
+ local prev_sha=""
+ for module in "${MODULES[@]}"; do
+ local file="${OUTPUT_DIR}/${product}/${module}/extract/${arch}/${lib}"
+ if [[ ! -f "${file}" ]]; then
+ # not all libs are present in a module
+ echo "file doesn't exist: ${file}"
+ continue
+ fi
+ sha=$(sha1sum ${file})
+ sha="${sha% *}"
+ if [ "${prev_sha}" == "" ]; then
+ prev_sha="${sha}"
+ elif [ "${sha}" != "${prev_sha}" ] && { [ "${lib}" != "libcrypto.so" ] || [ "${module}" != "com.android.tethering" ]; }; then
+ echo "Test failed, ${lib} has different hash value"
+ exit 1
+ fi
+ done
+ done
+ done
+}
+
+export UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true # don't rely on prebuilts
+export TARGET_BUILD_APPS="${MODULES[@]}"
+for product in "${TARGET_PRODUCTS[@]}"; do
+ ###########
+ # Build the mainline modules
+ ###########
+ packages/modules/common/build/build_unbundled_mainline_module.sh \
+ --product "${product}" \
+ --dist_dir "${OUTPUT_DIR}/${product}"
+
+ extract_dcla_libs "${product}"
+ compare_dcla_libs "${product}"
+done
+
+echo "Test passed"
diff --git a/tests/lib.sh b/tests/lib.sh
index 1bb2df9..715eac1 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -8,7 +8,7 @@
REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
-if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
+if [[ -n "$HARDWIRED_MOCK_TOP" ]]; then
MOCK_TOP="$HARDWIRED_MOCK_TOP"
else
MOCK_TOP=$(mktemp -t -d st.XXXXX)
@@ -36,37 +36,38 @@
}
function info {
- echo -e "\e[92;1m[TEST HARNESS INFO]\e[0m" $*
+ echo -e "\e[92;1m[TEST HARNESS INFO]\e[0m" "$*"
}
function fail {
- echo -e "\e[91;1mFAILED:\e[0m" $*
+ echo -e "\e[91;1mFAILED:\e[0m" "$*"
exit 1
}
-function copy_directory() {
+function copy_directory {
local dir="$1"
- local parent="$(dirname "$dir")"
+ local -r parent="$(dirname "$dir")"
mkdir -p "$MOCK_TOP/$parent"
cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
}
-function symlink_file() {
+function symlink_file {
local file="$1"
mkdir -p "$MOCK_TOP/$(dirname "$file")"
ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
}
-function symlink_directory() {
+function symlink_directory {
local dir="$1"
mkdir -p "$MOCK_TOP/$dir"
# We need to symlink the contents of the directory individually instead of
# using one symlink for the whole directory because finder.go doesn't follow
# symlinks when looking for Android.bp files
- for i in $(ls "$REAL_TOP/$dir"); do
+ for i in "$REAL_TOP/$dir"/*; do
+ i=$(basename "$i")
local target="$MOCK_TOP/$dir/$i"
local source="$REAL_TOP/$dir/$i"
@@ -81,21 +82,28 @@
}
function create_mock_soong {
+ create_mock_bazel
copy_directory build/blueprint
copy_directory build/soong
- copy_directory build/make/tools/rbcrun
+ copy_directory build/make
+ symlink_directory prebuilts/sdk
symlink_directory prebuilts/go
symlink_directory prebuilts/build-tools
symlink_directory prebuilts/clang/host
+ symlink_directory external/compiler-rt
symlink_directory external/go-cmp
symlink_directory external/golang-protobuf
+ symlink_directory external/licenseclassifier
symlink_directory external/starlark-go
+ symlink_directory external/python
+ symlink_directory external/sqlite
+ symlink_directory external/spdx-tools
touch "$MOCK_TOP/Android.bp"
}
-function setup() {
+function setup {
cleanup_mock_top
mkdir -p "$MOCK_TOP"
@@ -104,36 +112,61 @@
info "Running test case \e[96;1m${FUNCNAME[1]}\e[0m"
cd "$MOCK_TOP"
- tar xzf "$WARMED_UP_MOCK_TOP"
+ tar xzf "$WARMED_UP_MOCK_TOP" --warning=no-timestamp
}
-function run_soong() {
- build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
+# shellcheck disable=SC2120
+function run_soong {
+ USE_RBE=false build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
}
-function create_mock_bazel() {
+function create_mock_bazel {
copy_directory build/bazel
+ copy_directory build/bazel_common_rules
+ symlink_directory packages/modules/common/build
symlink_directory prebuilts/bazel
+ symlink_directory prebuilts/clang
symlink_directory prebuilts/jdk
symlink_directory external/bazel-skylib
+ symlink_directory external/bazelbuild-rules_android
+ symlink_directory external/bazelbuild-rules_license
+ symlink_directory external/bazelbuild-kotlin-rules
symlink_file WORKSPACE
symlink_file BUILD
- symlink_file tools/bazel
}
-run_bazel() {
- tools/bazel "$@"
+function 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
+
+ build/bazel/bin/bazel "$@"
}
-run_ninja() {
+function run_ninja {
build/soong/soong_ui.bash --make-mode --skip-config --soong-only --skip-soong-tests "$@"
}
-info "Starting Soong integration test suite $(basename $0)"
+info "Starting Soong integration test suite $(basename "$0")"
info "Mock top: $MOCK_TOP"
export ALLOW_MISSING_DEPENDENCIES=true
+export ALLOW_BP_UNDER_SYMLINKS=true
warmup_mock_top
+
+function scan_and_run_tests {
+ # find all test_ functions
+ # NB "declare -F" output is sorted, hence test order is deterministic
+ readarray -t test_fns < <(declare -F | sed -n -e 's/^declare -f \(test_.*\)$/\1/p')
+ info "Found ${#test_fns[*]} tests"
+ if [[ ${#test_fns[*]} -eq 0 ]]; then
+ fail "No tests found"
+ fi
+ for f in ${test_fns[*]}; do
+ $f
+ info "Completed test case \e[96;1m$f\e[0m"
+ done
+}
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index b408fd3..05d3a66 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -12,9 +12,88 @@
function test_bazel_smoke {
setup
- create_mock_bazel
- STANDALONE_BAZEL=true run_bazel info
+ run_soong bp2build
+
+ run_bazel info --config=bp2build
}
-test_bazel_smoke
+function test_add_irrelevant_file {
+ setup
+
+ mkdir -p soong_tests/a/b
+ touch soong_tests/a/b/c.txt
+ cat > soong_tests/a/b/Android.bp <<'EOF'
+filegroup {
+ name: "c",
+ srcs: ["c.txt"],
+ bazel_module: { bp2build_available: true },
+}
+EOF
+
+ run_soong --bazel-mode-staging nothing
+
+ if [[ ! -e out/soong/bp2build/soong_tests/a/b/BUILD.bazel ]]; then
+ fail "BUILD.bazel not created"
+ fi
+
+ if [[ ! -e out/soong/build.ninja ]]; then
+ fail "build.ninja not created"
+ fi
+
+ local mtime_build1=$(stat -c "%y" out/soong/bp2build/soong_tests/a/b/BUILD.bazel)
+ local mtime_ninja1=$(stat -c "%y" out/soong/build.ninja)
+
+ touch soong_tests/a/irrelevant.txt
+
+ run_soong --bazel-mode-staging nothing
+ local mtime_build2=$(stat -c "%y" out/soong/bp2build/soong_tests/a/b/BUILD.bazel)
+ local mtime_ninja2=$(stat -c "%y" out/soong/build.ninja)
+
+ if [[ "$mtime_build1" != "$mtime_build2" ]]; then
+ fail "BUILD.bazel was generated"
+ fi
+
+ if [[ "$mtime_ninja1" != "$mtime_ninja2" ]]; then
+ fail "build.ninja was regenerated"
+ fi
+
+ if [[ ! -e out/soong/workspace/soong_tests/a/irrelevant.txt ]]; then
+ fail "new file was not symlinked"
+ fi
+}
+
+function test_force_enabled_modules {
+ setup
+ # b/273910287 - test force enable modules
+ mkdir -p soong_tests/a/b
+ cat > soong_tests/a/b/Android.bp <<'EOF'
+genrule {
+ name: "touch-file",
+ out: ["fake-out.txt"],
+ cmd: "touch $(out)",
+ bazel_module: { bp2build_available: true },
+}
+
+genrule {
+ name: "unenabled-touch-file",
+ out: ["fake-out2.txt"],
+ cmd: "touch $(out)",
+ bazel_module: { bp2build_available: false },
+}
+EOF
+ run_soong --bazel-mode-staging --bazel-force-enabled-modules=touch-file nothing
+ local bazel_contained=`grep out/soong/.intermediates/soong_tests/a/b/touch-file/gen/fake-out.txt out/soong/build.ninja`
+ if [[ $bazel_contained == '' ]]; then
+ fail "Bazel actions not found for force-enabled module"
+ fi
+
+ local exit_code=`run_soong --bazel-force-enabled-modules=unenabled-touch-file nothing`
+
+ if [[ $exit_code -ne 1 ]]; then
+ fail "Expected failure due to force-enabling an unenabled module "
+ fi
+}
+
+
+scan_and_run_tests
\ No newline at end of file
diff --git a/tests/persistent_bazel_test.sh b/tests/persistent_bazel_test.sh
new file mode 100755
index 0000000..4e2982a
--- /dev/null
+++ b/tests/persistent_bazel_test.sh
@@ -0,0 +1,83 @@
+#!/bin/bash -eu
+
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o pipefail
+
+source "$(dirname "$0")/lib.sh"
+
+# This test verifies that adding USE_PERSISTENT_BAZEL creates a Bazel process
+# that outlasts the build process.
+# This test should only be run in sandboxed environments (because this test
+# verifies a Bazel process using global process list, and may spawn lingering
+# Bazel processes).
+function test_persistent_bazel {
+ setup
+
+ # Ensure no existing Bazel process.
+ if [[ -e out/bazel/output/server/server.pid.txt ]]; then
+ kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
+ if kill -0 $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null ; then
+ fail "Error killing pre-setup bazel"
+ fi
+ fi
+
+ USE_PERSISTENT_BAZEL=1 run_soong nothing
+
+ if ! kill -0 $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null ; then
+ fail "Persistent bazel process expected, but not found after first build"
+ fi
+ BAZEL_PID=$(cat out/bazel/output/server/server.pid.txt)
+
+ USE_PERSISTENT_BAZEL=1 run_soong nothing
+
+ if ! kill -0 $BAZEL_PID 2>/dev/null ; then
+ fail "Bazel pid $BAZEL_PID was killed after second build"
+ fi
+
+ kill $BAZEL_PID 2>/dev/null
+ if ! kill -0 $BAZEL_PID 2>/dev/null ; then
+ fail "Error killing bazel on shutdown"
+ fi
+}
+
+# Verifies that USE_PERSISTENT_BAZEL mode operates as expected in the event
+# that there are Bazel failures.
+function test_bazel_failure {
+ setup
+
+ # Ensure no existing Bazel process.
+ if [[ -e out/bazel/output/server/server.pid.txt ]]; then
+ kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
+ if kill -0 $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null ; then
+ fail "Error killing pre-setup bazel"
+ fi
+ fi
+
+ # Introduce a syntax error in a BUILD file which is used in every build
+ # (Note this is a BUILD file which is copied as part of test setup, so this
+ # has no effect on sources outside of this test.
+ rm -rf build/bazel/rules
+
+ USE_PERSISTENT_BAZEL=1 run_soong nothing 1>out/failurelog.txt 2>&1 && fail "Expected build failure" || true
+
+ if ! grep -sq "'build/bazel/rules' is not a package" out/failurelog.txt ; then
+ fail "Expected error to contain 'build/bazel/rules' is not a package, instead got:\n$(cat out/failurelog.txt)"
+ fi
+
+ kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
+}
+
+scan_and_run_tests
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 76a918b..e1aa70c 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -7,5 +7,18 @@
"$TOP/build/soong/tests/bootstrap_test.sh"
"$TOP/build/soong/tests/mixed_mode_test.sh"
"$TOP/build/soong/tests/bp2build_bazel_test.sh"
+"$TOP/build/soong/tests/persistent_bazel_test.sh"
"$TOP/build/soong/tests/soong_test.sh"
"$TOP/build/bazel/ci/rbc_regression_test.sh" aosp_arm64-userdebug
+
+# The following tests build against the full source tree and don't rely on the
+# mock client.
+"$TOP/build/soong/tests/apex_comparison_tests.sh"
+"$TOP/build/soong/tests/apex_comparison_tests.sh" "module_arm64only"
+extra_build_params=--bazel-mode-staging "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
+BUILD_BROKEN_DISABLE_BAZEL=true "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
+"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh"
+"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
+"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_cf_arm64_phone" "armv8-a" "cortex-a53"
+
+"$TOP/build/soong/tests/sbom_test.sh"
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
new file mode 100755
index 0000000..2f154cd
--- /dev/null
+++ b/tests/sbom_test.sh
@@ -0,0 +1,214 @@
+#!/bin/bash
+
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -uo pipefail
+
+# Integration test for verifying generated SBOM for cuttlefish device.
+
+if [ ! -e "build/make/core/Makefile" ]; then
+ echo "$0 must be run from the top of the Android source tree."
+ exit 1
+fi
+
+tmp_dir="$(mktemp -d tmp.XXXXXX)"
+function cleanup {
+ rm -rf "${tmp_dir}"
+}
+trap cleanup EXIT
+
+out_dir=$tmp_dir
+droid_target=droid
+
+debug=false
+if [ $debug = "true" ]; then
+ out_dir=out
+ droid_target=
+fi
+
+function run_soong {
+ TARGET_PRODUCT="aosp_cf_x86_64_phone" TARGET_BUILD_VARIANT=userdebug OUT_DIR=$out_dir \
+ build/soong/soong_ui.bash --make-mode "$@"
+}
+
+# m droid, build sbom later in case additional dependencies might be built and included in partition images.
+run_soong $droid_target dump.erofs lz4
+
+product_out=$out_dir/target/product/vsoc_x86_64
+sbom_test=$product_out/sbom_test
+mkdir $sbom_test
+cp $product_out/*.img $sbom_test
+
+# m sbom
+run_soong sbom
+
+# Generate installed file list from .img files in PRODUCT_OUT
+dump_erofs=$out_dir/host/linux-x86/bin/dump.erofs
+lz4=$out_dir/host/linux-x86/bin/lz4
+
+declare -A diff_excludes
+diff_excludes[odm]="-I /odm/lib/modules"
+diff_excludes[vendor]=\
+"-I /vendor/lib64/libkeystore2_crypto.so \
+ -I /vendor/lib/modules \
+ -I /vendor/odm"
+diff_excludes[system]=\
+"-I /bin \
+ -I /bugreports \
+ -I /cache \
+ -I /d \
+ -I /etc \
+ -I /init \
+ -I /odm/app \
+ -I /odm/bin \
+ -I /odm_dlkm/etc \
+ -I /odm/etc \
+ -I /odm/firmware \
+ -I /odm/framework \
+ -I /odm/lib \
+ -I /odm/lib64 \
+ -I /odm/overlay \
+ -I /odm/priv-app \
+ -I /odm/usr \
+ -I /sdcard \
+ -I /system/lib64/android.hardware.confirmationui@1.0.so \
+ -I /system/lib64/android.hardware.confirmationui-V1-ndk.so \
+ -I /system/lib64/android.hardware.keymaster@4.1.so \
+ -I /system/lib64/android.hardware.security.rkp-V3-ndk.so \
+ -I /system/lib64/android.hardware.security.sharedsecret-V1-ndk.so \
+ -I /system/lib64/android.security.compat-ndk.so \
+ -I /system/lib64/libkeymaster4_1support.so \
+ -I /system/lib64/libkeymint.so \
+ -I /system/lib64/libkeystore2_aaid.so \
+ -I /system/lib64/libkeystore2_apc_compat.so \
+ -I /system/lib64/libkeystore2_crypto.so \
+ -I /system/lib64/libkm_compat_service.so \
+ -I /system/lib64/libkm_compat.so \
+ -I /system/lib64/vndk-29 \
+ -I /system/lib64/vndk-sp-29 \
+ -I /system/lib/vndk-29 \
+ -I /system/lib/vndk-sp-29 \
+ -I /system/usr/icu \
+ -I /vendor_dlkm/etc"
+
+function diff_files {
+ file_list_file="$1"; shift
+ files_in_spdx_file="$1"; shift
+ partition_name="$1"; shift
+ exclude=
+ if [ -v 'diff_excludes[$partition_name]' ]; then
+ exclude=${diff_excludes[$partition_name]}
+ fi
+
+ diff "$file_list_file" "$files_in_spdx_file" $exclude
+ if [ $? != "0" ]; then
+ echo Found diffs in $f and SBOM.
+ exit 1
+ else
+ echo No diffs.
+ fi
+ }
+
+# Example output of dump.erofs is as below, and the data used in the test start
+# at line 11. Column 1 is inode id, column 2 is inode type and column 3 is name.
+# Each line is captured in variable "entry", awk is used to get type and name.
+# Output of dump.erofs:
+# File : /
+# Size: 160 On-disk size: 160 directory
+# NID: 39 Links: 10 Layout: 2 Compression ratio: 100.00%
+# Inode size: 64 Extent size: 0 Xattr size: 16
+# Uid: 0 Gid: 0 Access: 0755/rwxr-xr-x
+# Timestamp: 2023-02-14 01:15:54.000000000
+#
+# NID TYPE FILENAME
+# 39 2 .
+# 39 2 ..
+# 47 2 app
+# 1286748 2 bin
+# 1286754 2 etc
+# 5304814 2 lib
+# 5309056 2 lib64
+# 5309130 2 media
+# 5388910 2 overlay
+# 5479537 2 priv-app
+EROFS_IMAGES="\
+ $sbom_test/product.img \
+ $sbom_test/system.img \
+ $sbom_test/system_ext.img \
+ $sbom_test/system_dlkm.img \
+ $sbom_test/system_other.img \
+ $sbom_test/odm.img \
+ $sbom_test/odm_dlkm.img \
+ $sbom_test/vendor.img \
+ $sbom_test/vendor_dlkm.img"
+for f in $EROFS_IMAGES; do
+ partition_name=$(basename $f | cut -d. -f1)
+ file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
+ files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
+ rm "$file_list_file" > /dev/null 2>&1
+ all_dirs="/"
+ while [ ! -z "$all_dirs" ]; do
+ dir=$(echo "$all_dirs" | cut -d ' ' -f1)
+ all_dirs=$(echo "$all_dirs" | cut -d ' ' -f1 --complement -s)
+ entries=$($dump_erofs --ls --path "$dir" $f | tail -n +11)
+ while read -r entry; do
+ inode_type=$(echo $entry | awk -F ' ' '{print $2}')
+ name=$(echo $entry | awk -F ' ' '{print $3}')
+ case $inode_type in
+ "2") # directory
+ all_dirs=$(echo "$all_dirs $dir/$name" | sed 's/^\s*//')
+ ;;
+ "1"|"7") # 1: file, 7: symlink
+ (
+ if [ "$partition_name" != "system" ]; then
+ # system partition is mounted to /, not to prepend partition name.
+ printf %s "/$partition_name"
+ fi
+ echo "$dir/$name" | sed 's#^//#/#'
+ ) >> "$file_list_file"
+ ;;
+ esac
+ done <<< "$entries"
+ done
+ sort -n -o "$file_list_file" "$file_list_file"
+
+ grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_spdx_file"
+ if [ "$partition_name" = "system" ]; then
+ # system partition is mounted to /, so include FileName starts with /root/ too.
+ grep "FileName: /root/" $product_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_spdx_file"
+ fi
+ sort -n -o "$files_in_spdx_file" "$files_in_spdx_file"
+
+ echo ============ Diffing files in $f and SBOM
+ diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name"
+done
+
+RAMDISK_IMAGES="$product_out/ramdisk.img"
+for f in $RAMDISK_IMAGES; do
+ partition_name=$(basename $f | cut -d. -f1)
+ file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
+ files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
+ # lz4 decompress $f to stdout
+ # cpio list all entries like ls -l
+ # grep filter normal files and symlinks
+ # awk get entry names
+ # sed remove partition name from entry names
+ $lz4 -c -d $f | cpio -tv 2>/dev/null | grep '^[-l]' | awk -F ' ' '{print $9}' | sed "s:^:/$partition_name/:" | sort -n > "$file_list_file"
+
+ grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_spdx_file"
+
+ echo ============ Diffing files in $f and SBOM
+ diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name"
+done
\ No newline at end of file
diff --git a/tests/soong_test.sh b/tests/soong_test.sh
index 905d708..f7bee40 100755
--- a/tests/soong_test.sh
+++ b/tests/soong_test.sh
@@ -19,4 +19,4 @@
run_soong clean
}
-test_m_clean_works
+scan_and_run_tests
\ No newline at end of file
diff --git a/tradefed/Android.bp b/tradefed/Android.bp
index f0336a3..a161108 100644
--- a/tradefed/Android.bp
+++ b/tradefed/Android.bp
@@ -11,6 +11,7 @@
],
srcs: [
"autogen.go",
+ "autogen_bazel.go",
"config.go",
"makevars.go",
],
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index c2429ab..49b09af 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -107,15 +107,10 @@
}
-func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, configs []Config, testInstallBase string) {
- autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), output, template, configs, "", testInstallBase)
-}
-
-func autogenTemplateWithName(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, testInstallBase string) {
- autogenTemplateWithNameAndOutputFile(ctx, name, output, template, configs, "", testInstallBase)
-}
-
-func autogenTemplateWithNameAndOutputFile(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, outputFileName string, testInstallBase string) {
+func autogenTemplate(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, outputFileName string, testInstallBase string) {
+ if template == "" {
+ ctx.ModuleErrorf("Empty template")
+ }
var configStrings []string
for _, config := range configs {
configStrings = append(configStrings, config.Config())
@@ -137,144 +132,53 @@
})
}
-func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string,
- testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool, testInstallBase string) android.Path {
+// AutoGenTestConfigOptions is used so that we can supply many optional
+// arguments to the AutoGenTestConfig function.
+type AutoGenTestConfigOptions struct {
+ Name string
+ OutputFileName string
+ TestConfigProp *string
+ TestConfigTemplateProp *string
+ TestSuites []string
+ Config []Config
+ OptionsForAutogenerated []Option
+ AutoGenConfig *bool
+ UnitTest *bool
+ TestInstallBase string
+ DeviceTemplate string
+ HostTemplate string
+ HostUnitTestTemplate string
+}
- path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
+func AutoGenTestConfig(ctx android.ModuleContext, options AutoGenTestConfigOptions) android.Path {
+ configs := append([]Config{}, options.Config...)
+ for _, c := range options.OptionsForAutogenerated {
+ configs = append(configs, c)
+ }
+ name := options.Name
+ if name == "" {
+ name = ctx.ModuleName()
+ }
+ path, autogenPath := testConfigPath(ctx, options.TestConfigProp, options.TestSuites, options.AutoGenConfig, options.TestConfigTemplateProp)
if autogenPath != nil {
- templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+ templatePath := getTestConfigTemplate(ctx, options.TestConfigTemplateProp)
if templatePath.Valid() {
- autogenTemplate(ctx, autogenPath, templatePath.String(), config, testInstallBase)
+ autogenTemplate(ctx, name, autogenPath, templatePath.String(), configs, options.OutputFileName, options.TestInstallBase)
} else {
if ctx.Device() {
- autogenTemplate(ctx, autogenPath, "${NativeTestConfigTemplate}", config, testInstallBase)
+ autogenTemplate(ctx, name, autogenPath, options.DeviceTemplate, configs, options.OutputFileName, options.TestInstallBase)
} else {
- autogenTemplate(ctx, autogenPath, "${NativeHostTestConfigTemplate}", config, testInstallBase)
- }
- }
- return autogenPath
- }
- return path
-}
-
-func AutoGenShellTestConfig(ctx android.ModuleContext, testConfigProp *string,
- testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool, outputFileName string) android.Path {
- path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
- if autogenPath != nil {
- templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
- if templatePath.Valid() {
- autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), autogenPath, templatePath.String(), config, outputFileName, "")
- } else {
- autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), autogenPath, "${ShellTestConfigTemplate}", config, outputFileName, "")
- }
- return autogenPath
- }
- return path
-}
-
-func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string,
- testConfigTemplateProp *string, testSuites []string, configs []Config, autoGenConfig *bool) android.Path {
- path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
- if autogenPath != nil {
- templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
- if templatePath.Valid() {
- autogenTemplate(ctx, autogenPath, templatePath.String(), configs, "")
- } else {
- autogenTemplate(ctx, autogenPath, "${NativeBenchmarkTestConfigTemplate}", configs, "")
- }
- return autogenPath
- }
- return path
-}
-
-func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
- testSuites []string, config []Config, autoGenConfig *bool, unitTest *bool) android.Path {
- path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
- if autogenPath != nil {
- templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
- if templatePath.Valid() {
- autogenTemplate(ctx, autogenPath, templatePath.String(), config, "")
- } else {
- if ctx.Device() {
- autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", config, "")
- } else {
- if Bool(unitTest) {
- autogenTemplate(ctx, autogenPath, "${JavaHostUnitTestConfigTemplate}", config, "")
+ if Bool(options.UnitTest) {
+ autogenTemplate(ctx, name, autogenPath, options.HostUnitTestTemplate, configs, options.OutputFileName, options.TestInstallBase)
} else {
- autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", config, "")
+ autogenTemplate(ctx, name, autogenPath, options.HostTemplate, configs, options.OutputFileName, options.TestInstallBase)
}
}
}
return autogenPath
}
- return path
-}
-
-func AutoGenPythonBinaryHostTestConfig(ctx android.ModuleContext, testConfigProp *string,
- testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path {
-
- path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
- if autogenPath != nil {
- templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
- if templatePath.Valid() {
- autogenTemplate(ctx, autogenPath, templatePath.String(), nil, "")
- } else {
- autogenTemplate(ctx, autogenPath, "${PythonBinaryHostTestConfigTemplate}", nil, "")
- }
- return autogenPath
- }
- return path
-}
-
-func AutoGenRustTestConfig(ctx android.ModuleContext, testConfigProp *string,
- testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool, testInstallBase string) android.Path {
- path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
- if autogenPath != nil {
- templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
- if templatePath.Valid() {
- autogenTemplate(ctx, autogenPath, templatePath.String(), config, testInstallBase)
- } else {
- if ctx.Device() {
- autogenTemplate(ctx, autogenPath, "${RustDeviceTestConfigTemplate}", config, testInstallBase)
- } else {
- autogenTemplate(ctx, autogenPath, "${RustHostTestConfigTemplate}", config, testInstallBase)
- }
- }
- return autogenPath
- }
- return path
-}
-
-func AutoGenRustBenchmarkConfig(ctx android.ModuleContext, testConfigProp *string,
- testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path {
- path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
- if autogenPath != nil {
- templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
- if templatePath.Valid() {
- autogenTemplate(ctx, autogenPath, templatePath.String(), config, "")
- } else {
- if ctx.Device() {
- autogenTemplate(ctx, autogenPath, "${RustDeviceBenchmarkConfigTemplate}", config, "")
- } else {
- autogenTemplate(ctx, autogenPath, "${RustHostBenchmarkConfigTemplate}", config, "")
- }
- }
- return autogenPath
- }
- return path
-}
-
-func AutoGenRobolectricTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
- testSuites []string, autoGenConfig *bool) android.Path {
- path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
- if autogenPath != nil {
- templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
- if templatePath.Valid() {
- autogenTemplate(ctx, autogenPath, templatePath.String(), nil, "")
- } else {
- autogenTemplate(ctx, autogenPath, "${RobolectricTestConfigTemplate}", nil, "")
- }
- return autogenPath
+ if len(options.OptionsForAutogenerated) > 0 {
+ ctx.ModuleErrorf("Extra tradefed configurations were provided for an autogenerated xml file, but the autogenerated xml file was not used.")
}
return path
}
diff --git a/tradefed/autogen_bazel.go b/tradefed/autogen_bazel.go
new file mode 100644
index 0000000..d3109d9
--- /dev/null
+++ b/tradefed/autogen_bazel.go
@@ -0,0 +1,105 @@
+// 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 tradefed
+
+import (
+ "android/soong/android"
+ "android/soong/bazel"
+
+ "github.com/google/blueprint/proptools"
+)
+
+const (
+ InstrumentationTestConfigTemplate = "build/make/core/instrumentation_test_config_template.xml"
+ JavaTestConfigTemplate = "build/make/core/java_test_config_template.xml"
+ JavaHostTestConfigTemplate = "build/make/core/java_host_test_config_template.xml"
+ JavaHostUnitTestConfigTemplate = "build/make/core/java_host_unit_test_config_template.xml"
+ NativeBenchmarkTestConfigTemplate = "build/make/core/native_benchmark_test_config_template.xml"
+ NativeHostTestConfigTemplate = "build/make/core/native_host_test_config_template.xml"
+ NativeTestConfigTemplate = "build/make/core/native_test_config_template.xml"
+ PythonBinaryHostTestConfigTemplate = "build/make/core/python_binary_host_test_config_template.xml"
+ RustDeviceTestConfigTemplate = "build/make/core/rust_device_test_config_template.xml"
+ RustHostTestConfigTemplate = "build/make/core/rust_host_test_config_template.xml"
+ RustDeviceBenchmarkConfigTemplate = "build/make/core/rust_device_benchmark_config_template.xml"
+ RustHostBenchmarkConfigTemplate = "build/make/core/rust_host_benchmark_config_template.xml"
+ RobolectricTestConfigTemplate = "build/make/core/robolectric_test_config_template.xml"
+ ShellTestConfigTemplate = "build/make/core/shell_test_config_template.xml"
+)
+
+type TestConfigAttributes struct {
+ Test_config *bazel.Label
+
+ Auto_generate_test_config *bool
+ Template_test_config *bazel.Label
+ Template_configs []string
+ Template_install_base *string
+}
+
+func GetTestConfigAttributes(
+ ctx android.TopDownMutatorContext,
+ testConfig *string,
+ extraTestConfigs []string,
+ autoGenConfig *bool,
+ testSuites []string,
+ template *string,
+ templateConfigs []Config,
+ templateInstallBase *string) TestConfigAttributes {
+
+ attrs := TestConfigAttributes{}
+ attrs.Test_config = GetTestConfig(ctx, testConfig)
+ // do not generate a test config if
+ // 1) test config already found
+ // 2) autoGenConfig == false
+ // 3) CTS tests and no template specified.
+ // CTS Modules can be used for test data, so test config files must be explicitly specified.
+ if (attrs.Template_test_config != nil) ||
+ proptools.Bool(autoGenConfig) == false ||
+ (template == nil && !android.InList("cts", testSuites)) {
+
+ return attrs
+ }
+
+ // Add properties for the bazel rule to generate a test config
+ // since a test config was not specified.
+ templateLabel := android.BazelLabelForModuleSrcSingle(ctx, *template)
+ attrs.Template_test_config = &templateLabel
+ attrs.Auto_generate_test_config = autoGenConfig
+ var configStrings []string
+ for _, c := range templateConfigs {
+ configString := proptools.NinjaAndShellEscape(c.Config())
+ configStrings = append(configStrings, configString)
+ }
+ attrs.Template_configs = configStrings
+ attrs.Template_install_base = templateInstallBase
+ return attrs
+}
+
+func GetTestConfig(
+ ctx android.TopDownMutatorContext,
+ testConfig *string,
+) *bazel.Label {
+
+ if testConfig != nil {
+ c, _ := android.BazelStringOrLabelFromProp(ctx, testConfig)
+ if c.Value != nil {
+ return c.Value
+ }
+ }
+
+ // check for default AndroidTest.xml
+ defaultTestConfigPath := ctx.ModuleDir() + "/AndroidTest.xml"
+ c, _ := android.BazelStringOrLabelFromProp(ctx, &defaultTestConfigPath)
+ return c.Value
+}
diff --git a/tradefed/config.go b/tradefed/config.go
index 999424c..326a006 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -31,6 +31,7 @@
pctx.SourcePathVariable("NativeBenchmarkTestConfigTemplate", "build/make/core/native_benchmark_test_config_template.xml")
pctx.SourcePathVariable("NativeHostTestConfigTemplate", "build/make/core/native_host_test_config_template.xml")
pctx.SourcePathVariable("NativeTestConfigTemplate", "build/make/core/native_test_config_template.xml")
+ pctx.SourcePathVariable("PythonBinaryHostMoblyTestConfigTemplate", "build/make/core/python_binary_host_mobly_test_config_template.xml")
pctx.SourcePathVariable("PythonBinaryHostTestConfigTemplate", "build/make/core/python_binary_host_test_config_template.xml")
pctx.SourcePathVariable("RustDeviceTestConfigTemplate", "build/make/core/rust_device_test_config_template.xml")
pctx.SourcePathVariable("RustHostTestConfigTemplate", "build/make/core/rust_host_test_config_template.xml")
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 3dc87f5..b79754c 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -34,22 +34,23 @@
deps: [
"blueprint",
"blueprint-bootstrap",
+ "blueprint-microfactory",
+ "soong-finder",
+ "soong-remoteexec",
+ "soong-shared",
"soong-ui-build-paths",
"soong-ui-logger",
"soong-ui-metrics",
"soong-ui-status",
"soong-ui-terminal",
"soong-ui-tracer",
- "soong-shared",
- "soong-finder",
- "blueprint-microfactory",
],
srcs: [
- "bazel.go",
"build.go",
"cleanbuild.go",
"config.go",
"context.go",
+ "staging_snapshot.go",
"dumpvars.go",
"environment.go",
"exec.go",
@@ -70,10 +71,11 @@
"cleanbuild_test.go",
"config_test.go",
"environment_test.go",
+ "proc_sync_test.go",
"rbe_test.go",
+ "staging_snapshot_test.go",
"upload_test.go",
"util_test.go",
- "proc_sync_test.go",
],
darwin: {
srcs: [
diff --git a/ui/build/bazel.go b/ui/build/bazel.go
deleted file mode 100644
index 0ebfcd8..0000000
--- a/ui/build/bazel.go
+++ /dev/null
@@ -1,257 +0,0 @@
-// 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 build
-
-import (
- "bytes"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
-
- "android/soong/bazel"
- "android/soong/shared"
- "android/soong/ui/metrics"
-)
-
-func getBazelInfo(ctx Context, config Config, bazelExecutable string, bazelEnv map[string]string, query string) string {
- infoCmd := Command(ctx, config, "bazel", bazelExecutable)
-
- if extraStartupArgs, ok := infoCmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
- infoCmd.Args = append(infoCmd.Args, strings.Fields(extraStartupArgs)...)
- }
-
- // Obtain the output directory path in the execution root.
- infoCmd.Args = append(infoCmd.Args,
- "info",
- query,
- )
-
- for k, v := range bazelEnv {
- infoCmd.Environment.Set(k, v)
- }
-
- infoCmd.Dir = filepath.Join(config.OutDir(), "..")
-
- queryResult := strings.TrimSpace(string(infoCmd.OutputOrFatal()))
- return queryResult
-}
-
-// Main entry point to construct the Bazel build command line, environment
-// variables and post-processing steps (e.g. converge output directories)
-func runBazel(ctx Context, config Config) {
- ctx.BeginTrace(metrics.RunBazel, "bazel")
- defer ctx.EndTrace()
-
- // "droid" is the default ninja target.
- // TODO(b/160568333): stop hardcoding 'droid' to support building any
- // Ninja target.
- outputGroups := "droid"
- if len(config.ninjaArgs) > 0 {
- // At this stage, the residue slice of args passed to ninja
- // are the ninja targets to build, which can correspond directly
- // to ninja_build's output_groups.
- outputGroups = strings.Join(config.ninjaArgs, ",")
- }
-
- // Environment variables are the primary mechanism to pass information from
- // soong_ui configuration or context to Bazel.
- bazelEnv := make(map[string]string)
-
- // Use *_NINJA variables to pass the root-relative path of the combined,
- // kati-generated, soong-generated, and packaging Ninja files to Bazel.
- // Bazel reads these from the lunch() repository rule.
- bazelEnv["COMBINED_NINJA"] = config.CombinedNinjaFile()
- bazelEnv["KATI_NINJA"] = config.KatiBuildNinjaFile()
- bazelEnv["PACKAGE_NINJA"] = config.KatiPackageNinjaFile()
- bazelEnv["SOONG_NINJA"] = config.SoongNinjaFile()
-
- // NOTE: When Bazel is used, config.DistDir() is rigged to return a fake distdir under config.OutDir()
- // This is to ensure that Bazel can actually write there. See config.go for more details.
- bazelEnv["DIST_DIR"] = config.DistDir()
-
- bazelEnv["SHELL"] = "/bin/bash"
-
- // `tools/bazel` is the default entry point for executing Bazel in the AOSP
- // source tree.
- bazelExecutable := filepath.Join("tools", "bazel")
- cmd := Command(ctx, config, "bazel", bazelExecutable)
-
- // Append custom startup flags to the Bazel command. Startup flags affect
- // the Bazel server itself, and any changes to these flags would incur a
- // restart of the server, losing much of the in-memory incrementality.
- if extraStartupArgs, ok := cmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
- cmd.Args = append(cmd.Args, strings.Fields(extraStartupArgs)...)
- }
-
- // Start constructing the `build` command.
- actionName := bazel.BazelNinjaExecRunName
- cmd.Args = append(cmd.Args,
- "build",
- // Use output_groups to select the set of outputs to produce from a
- // ninja_build target.
- "--output_groups="+outputGroups,
- // Generate a performance profile
- "--profile="+filepath.Join(shared.BazelMetricsFilename(config, actionName)),
- "--slim_profile=true",
- )
-
- if config.UseRBE() {
- for _, envVar := range []string{
- // RBE client
- "RBE_compare",
- "RBE_exec_strategy",
- "RBE_invocation_id",
- "RBE_log_dir",
- "RBE_num_retries_if_mismatched",
- "RBE_platform",
- "RBE_remote_accept_cache",
- "RBE_remote_update_cache",
- "RBE_server_address",
- // TODO: remove old FLAG_ variables.
- "FLAG_compare",
- "FLAG_exec_root",
- "FLAG_exec_strategy",
- "FLAG_invocation_id",
- "FLAG_log_dir",
- "FLAG_platform",
- "FLAG_remote_accept_cache",
- "FLAG_remote_update_cache",
- "FLAG_server_address",
- } {
- cmd.Args = append(cmd.Args,
- "--action_env="+envVar)
- }
-
- // We need to calculate --RBE_exec_root ourselves
- ctx.Println("Getting Bazel execution_root...")
- cmd.Args = append(cmd.Args, "--action_env=RBE_exec_root="+getBazelInfo(ctx, config, bazelExecutable, bazelEnv, "execution_root"))
- }
-
- // Ensure that the PATH environment variable value used in the action
- // environment is the restricted set computed from soong_ui, and not a
- // user-provided one, for hermeticity reasons.
- if pathEnvValue, ok := config.environ.Get("PATH"); ok {
- cmd.Environment.Set("PATH", pathEnvValue)
- cmd.Args = append(cmd.Args, "--action_env=PATH="+pathEnvValue)
- }
-
- // Allow Bazel actions to see the SHELL variable (passed to Bazel above)
- cmd.Args = append(cmd.Args, "--action_env=SHELL")
-
- // Append custom build flags to the Bazel command. Changes to these flags
- // may invalidate Bazel's analysis cache.
- // These should be appended as the final args, so that they take precedence.
- if extraBuildArgs, ok := cmd.Environment.Get("BAZEL_BUILD_ARGS"); ok {
- cmd.Args = append(cmd.Args, strings.Fields(extraBuildArgs)...)
- }
-
- // Append the label of the default ninja_build target.
- cmd.Args = append(cmd.Args,
- "//:"+config.TargetProduct()+"-"+config.TargetBuildVariant(),
- )
-
- // Execute the command at the root of the directory.
- cmd.Dir = filepath.Join(config.OutDir(), "..")
-
- for k, v := range bazelEnv {
- cmd.Environment.Set(k, v)
- }
-
- // Make a human-readable version of the bazelEnv map
- bazelEnvStringBuffer := new(bytes.Buffer)
- for k, v := range bazelEnv {
- fmt.Fprintf(bazelEnvStringBuffer, "%s=%s ", k, v)
- }
-
- // Print the implicit command line
- ctx.Println("Bazel implicit command line: " + strings.Join(cmd.Environment.Environ(), " ") + " " + cmd.Cmd.String() + "\n")
-
- // Print the explicit command line too
- ctx.Println("Bazel explicit command line: " + bazelEnvStringBuffer.String() + cmd.Cmd.String() + "\n")
-
- // Execute the build command.
- cmd.RunAndStreamOrFatal()
-
- // Post-processing steps start here. Once the Bazel build completes, the
- // output files are still stored in the execution root, not in $OUT_DIR.
- // Ensure that the $OUT_DIR contains the expected set of files by symlinking
- // the files from the execution root's output direction into $OUT_DIR.
-
- ctx.Println("Getting Bazel output_path...")
- outputBasePath := getBazelInfo(ctx, config, bazelExecutable, bazelEnv, "output_path")
- // TODO: Don't hardcode out/ as the bazel output directory. This is
- // currently hardcoded as ninja_build.output_root.
- bazelNinjaBuildOutputRoot := filepath.Join(outputBasePath, "..", "out")
-
- ctx.Println("Populating output directory...")
- populateOutdir(ctx, config, bazelNinjaBuildOutputRoot, ".")
-}
-
-// For all files F recursively under rootPath/relativePath, creates symlinks
-// such that OutDir/F resolves to rootPath/F via symlinks.
-// NOTE: For distdir paths we rename files instead of creating symlinks, so that the distdir is independent.
-func populateOutdir(ctx Context, config Config, rootPath string, relativePath string) {
- destDir := filepath.Join(rootPath, relativePath)
- os.MkdirAll(destDir, 0755)
- files, err := ioutil.ReadDir(destDir)
- if err != nil {
- ctx.Fatal(err)
- }
-
- for _, f := range files {
- // The original Bazel file path
- destPath := filepath.Join(destDir, f.Name())
-
- // The desired Soong file path
- srcPath := filepath.Join(config.OutDir(), relativePath, f.Name())
-
- destLstatResult, destLstatErr := os.Lstat(destPath)
- if destLstatErr != nil {
- ctx.Fatalf("Unable to Lstat dest %s: %s", destPath, destLstatErr)
- }
-
- srcLstatResult, srcLstatErr := os.Lstat(srcPath)
-
- if srcLstatErr == nil {
- if srcLstatResult.IsDir() && destLstatResult.IsDir() {
- // src and dest are both existing dirs - recurse on the dest dir contents...
- populateOutdir(ctx, config, rootPath, filepath.Join(relativePath, f.Name()))
- } else {
- // Ignore other pre-existing src files (could be pre-existing files, directories, symlinks, ...)
- // This can arise for files which are generated under OutDir outside of soong_build, such as .bootstrap files.
- // FIXME: This might cause a problem later e.g. if a symlink in the build graph changes...
- }
- } else {
- if !os.IsNotExist(srcLstatErr) {
- ctx.Fatalf("Unable to Lstat src %s: %s", srcPath, srcLstatErr)
- }
-
- if strings.Contains(destDir, config.DistDir()) {
- // We need to make a "real" file/dir instead of making a symlink (because the distdir can't have symlinks)
- // Rename instead of copy in order to save disk space.
- if err := os.Rename(destPath, srcPath); err != nil {
- ctx.Fatalf("Unable to rename %s -> %s due to error %s", srcPath, destPath, err)
- }
- } else {
- // src does not exist, so try to create a src -> dest symlink (i.e. a Soong path -> Bazel path symlink)
- if err := os.Symlink(destPath, srcPath); err != nil {
- ctx.Fatalf("Unable to create symlink %s -> %s due to error %s", srcPath, destPath, err)
- }
- }
- }
- }
-}
diff --git a/ui/build/build.go b/ui/build/build.go
index aadf4af..6874ef7 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -90,7 +90,7 @@
}
}
-// These are bitmasks which can be used to check whether various flags are set e.g. whether to use Bazel.
+// These are bitmasks which can be used to check whether various flags are set
const (
_ = iota
// Whether to run the kati config step.
@@ -102,14 +102,30 @@
// Whether to include the kati-generated ninja file in the combined ninja.
RunKatiNinja = 1 << iota
// Whether to run ninja on the combined ninja.
- RunNinja = 1 << iota
- // Whether to run bazel on the combined ninja.
- RunBazel = 1 << iota
- RunBuildTests = 1 << iota
- RunAll = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja
- RunAllWithBazel = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunBazel
+ RunNinja = 1 << iota
+ RunDistActions = 1 << iota
+ RunBuildTests = 1 << iota
)
+// checkBazelMode fails the build if there are conflicting arguments for which bazel
+// build mode to use.
+func checkBazelMode(ctx Context, config Config) {
+ count := 0
+ if config.bazelProdMode {
+ count++
+ }
+ if config.bazelDevMode {
+ count++
+ }
+ if config.bazelStagingMode {
+ count++
+ }
+ if count > 1 {
+ ctx.Fatalln("Conflicting bazel mode.\n" +
+ "Do not specify more than one of --bazel-mode and --bazel-mode-dev and --bazel-mode-staging ")
+ }
+}
+
// checkProblematicFiles fails the build if existing Android.mk or CleanSpec.mk files are found at the root of the tree.
func checkProblematicFiles(ctx Context) {
files := []string{"Android.mk", "CleanSpec.mk"}
@@ -183,8 +199,8 @@
}
}
-// Build the tree. The 'what' argument can be used to chose which components of
-// the build to run, via checking various bitmasks.
+// Build the tree. Various flags in `config` govern which components of
+// the build to run.
func Build(ctx Context, config Config) {
ctx.Verboseln("Starting build with args:", config.Arguments())
ctx.Verboseln("Environment:", config.Environment().Environ())
@@ -201,13 +217,28 @@
buildLock := BecomeSingletonOrFail(ctx, config)
defer buildLock.Unlock()
+ logArgsOtherThan := func(specialTargets ...string) {
+ var ignored []string
+ for _, a := range config.Arguments() {
+ if !inList(a, specialTargets) {
+ ignored = append(ignored, a)
+ }
+ }
+ if len(ignored) > 0 {
+ ctx.Printf("ignoring arguments %q", ignored)
+ }
+ }
+
if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
+ logArgsOtherThan("clean", "clobber")
clean(ctx, config)
return
}
defer waitForDist(ctx)
+ checkBazelMode(ctx, config)
+
// checkProblematicFiles aborts the build if Android.mk or CleanSpec.mk are found at the root of the tree.
checkProblematicFiles(ctx)
@@ -222,52 +253,22 @@
SetupPath(ctx, config)
- what := RunAll
- if config.UseBazel() {
- what = RunAllWithBazel
- }
- if config.Checkbuild() {
- what |= RunBuildTests
- }
- if config.SkipConfig() {
- ctx.Verboseln("Skipping Config as requested")
- what = what &^ RunProductConfig
- }
- if config.SkipKati() {
- ctx.Verboseln("Skipping Kati as requested")
- what = what &^ RunKati
- }
- if config.SkipKatiNinja() {
- ctx.Verboseln("Skipping use of Kati ninja as requested")
- what = what &^ RunKatiNinja
- }
- if config.SkipSoong() {
- ctx.Verboseln("Skipping use of Soong as requested")
- what = what &^ RunSoong
- }
-
- if config.SkipNinja() {
- ctx.Verboseln("Skipping Ninja as requested")
- what = what &^ RunNinja
- }
-
- if !config.SoongBuildInvocationNeeded() {
- // This means that the output of soong_build is not needed and thus it would
- // run unnecessarily. In addition, if this code wasn't there invocations
- // with only special-cased target names like "m bp2build" would result in
- // passing Ninja the empty target list and it would then build the default
- // targets which is not what the user asked for.
- what = what &^ RunNinja
- what = what &^ RunKati
- }
+ what := evaluateWhatToRun(config, ctx.Verboseln)
if config.StartGoma() {
startGoma(ctx, config)
}
+ rbeCh := make(chan bool)
if config.StartRBE() {
- startRBE(ctx, config)
+ cleanupRBELogsDir(ctx, config)
+ go func() {
+ startRBE(ctx, config)
+ close(rbeCh)
+ }()
defer DumpRBEMetrics(ctx, config, filepath.Join(config.LogsDir(), "rbe_metrics.pb"))
+ } else {
+ close(rbeCh)
}
if what&RunProductConfig != 0 {
@@ -278,6 +279,7 @@
if inList("installclean", config.Arguments()) ||
inList("install-clean", config.Arguments()) {
+ logArgsOtherThan("installclean", "install-clean")
installClean(ctx, config)
ctx.Println("Deleted images and staging directories.")
return
@@ -285,6 +287,7 @@
if inList("dataclean", config.Arguments()) ||
inList("data-clean", config.Arguments()) {
+ logArgsOtherThan("dataclean", "data-clean")
dataClean(ctx, config)
ctx.Println("Deleted data files.")
return
@@ -318,20 +321,68 @@
testForDanglingRules(ctx, config)
}
+ <-rbeCh
if what&RunNinja != 0 {
if what&RunKati != 0 {
installCleanIfNecessary(ctx, config)
}
-
runNinjaForBuild(ctx, config)
}
- // Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
- if what&RunBazel != 0 {
- runBazel(ctx, config)
+ if what&RunDistActions != 0 {
+ runDistActions(ctx, config)
}
}
+func evaluateWhatToRun(config Config, verboseln func(v ...interface{})) int {
+ //evaluate what to run
+ what := 0
+ if config.Checkbuild() {
+ what |= RunBuildTests
+ }
+ if !config.SkipConfig() {
+ what |= RunProductConfig
+ } else {
+ verboseln("Skipping Config as requested")
+ }
+ if !config.SkipSoong() {
+ what |= RunSoong
+ } else {
+ verboseln("Skipping use of Soong as requested")
+ }
+ if !config.SkipKati() {
+ what |= RunKati
+ } else {
+ verboseln("Skipping Kati as requested")
+ }
+ if !config.SkipKatiNinja() {
+ what |= RunKatiNinja
+ } else {
+ verboseln("Skipping use of Kati ninja as requested")
+ }
+ if !config.SkipNinja() {
+ what |= RunNinja
+ } else {
+ verboseln("Skipping Ninja as requested")
+ }
+
+ if !config.SoongBuildInvocationNeeded() {
+ // This means that the output of soong_build is not needed and thus it would
+ // run unnecessarily. In addition, if this code wasn't there invocations
+ // with only special-cased target names like "m bp2build" would result in
+ // passing Ninja the empty target list and it would then build the default
+ // targets which is not what the user asked for.
+ what = what &^ RunNinja
+ what = what &^ RunKati
+ }
+
+ if config.Dist() {
+ what |= RunDistActions
+ }
+
+ return what
+}
+
var distWaitGroup sync.WaitGroup
// waitForDist waits for all backgrounded distGzipFile and distFile writes to finish
@@ -387,3 +438,9 @@
}
}()
}
+
+// Actions to run on every build where 'dist' is in the actions.
+// Be careful, anything added here slows down EVERY CI build
+func runDistActions(ctx Context, config Config) {
+ runStagingSnapshot(ctx, config)
+}
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 1c80cff..fd60177 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -167,6 +167,7 @@
productOut("debug_ramdisk"),
productOut("vendor_ramdisk"),
productOut("vendor_debug_ramdisk"),
+ productOut("vendor_kernel_ramdisk"),
productOut("test_harness_ramdisk"),
productOut("recovery"),
productOut("root"),
diff --git a/ui/build/config.go b/ui/build/config.go
index e271bfc..bf4aec9 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -19,12 +19,14 @@
"encoding/json"
"fmt"
"io/ioutil"
+ "math/rand"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
+ "syscall"
"time"
"android/soong/shared"
@@ -42,6 +44,16 @@
envConfigFetchTimeout = 10 * time.Second
)
+var (
+ rbeRandPrefix int
+ googleProdCredsExistCache bool
+)
+
+func init() {
+ rand.Seed(time.Now().UnixNano())
+ rbeRandPrefix = rand.Intn(1000)
+}
+
type Config struct{ *configImpl }
type configImpl struct {
@@ -52,24 +64,31 @@
environ *Environment
distDir string
buildDateTime string
+ logsPrefix string
// From the arguments
- parallel int
- keepGoing int
- verbose bool
- checkbuild bool
- dist bool
- jsonModuleGraph bool
- bp2build bool
- queryview bool
- reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
- soongDocs bool
- skipConfig bool
- skipKati bool
- skipKatiNinja bool
- skipSoong bool
- skipNinja bool
- skipSoongTests bool
+ parallel int
+ keepGoing int
+ verbose bool
+ checkbuild bool
+ dist bool
+ jsonModuleGraph bool
+ apiBp2build bool // Generate BUILD files for Soong modules that contribute APIs
+ bp2build bool
+ queryview bool
+ reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
+ soongDocs bool
+ multitreeBuild bool // This is a multitree build.
+ skipConfig bool
+ skipKati bool
+ skipKatiNinja bool
+ skipSoong bool
+ skipNinja bool
+ skipSoongTests bool
+ searchApiDir bool // Scan the Android.bp files generated in out/api_surfaces
+ skipMetricsUpload bool
+ buildStartedTime int64 // For metrics-upload-only - manually specify a build-started time
+ buildFromTextStub bool
// From the product config
katiArgs []string
@@ -88,18 +107,38 @@
pathReplaced bool
- useBazel bool
-
- // During Bazel execution, Bazel cannot write outside OUT_DIR.
- // So if DIST_DIR is set to an external dir (outside of OUT_DIR), we need to rig it temporarily and then migrate files at the end of the build.
- riggedDistDirForBazel string
+ bazelProdMode bool
+ bazelDevMode bool
+ bazelStagingMode bool
// Set by multiproduct_kati
emptyNinjaFile bool
metricsUploader string
+
+ bazelForceEnabledModules string
+
+ includeTags []string
+ sourceRootDirs []string
+
+ // Data source to write ninja weight list
+ ninjaWeightListSource NinjaWeightListSource
}
+type NinjaWeightListSource uint
+
+const (
+ // ninja doesn't use weight list.
+ NOT_USED NinjaWeightListSource = iota
+ // ninja uses weight list based on previous builds by ninja log
+ NINJA_LOG
+ // ninja thinks every task has the same weight.
+ EVENLY_DISTRIBUTED
+ // ninja uses an external custom weight list
+ EXTERNAL_FILE
+ // ninja uses a prioritized module list from Soong
+ HINT_FROM_SOONG
+)
const srcDirFileCheck = "build/soong/root.bp"
var buildFiles = []string{"Android.mk", "Android.bp"}
@@ -119,18 +158,6 @@
BUILD_MODULES
)
-type bazelBuildMode int
-
-// Bazel-related build modes.
-const (
- // Don't use bazel at all.
- noBazel bazelBuildMode = iota
-
- // Generate synthetic build files and incorporate these files into a build which
- // partially uses Bazel. Build metadata may come from Android.bp or BUILD files.
- mixedBuild
-)
-
// checkTopDir validates that the current directory is at the root directory of the source tree.
func checkTopDir(ctx Context) {
if _, err := os.Stat(srcDirFileCheck); err != nil {
@@ -141,14 +168,21 @@
}
}
-// fetchEnvConfig optionally fetches environment config from an
-// experiments system to control Soong features dynamically.
+// fetchEnvConfig optionally fetches a configuration file that can then subsequently be
+// loaded into Soong environment to control certain aspects of build behavior (e.g., enabling RBE).
+// If a configuration file already exists on disk, the fetch is run in the background
+// so as to NOT block the rest of the build execution.
func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
configName := envConfigName + "." + jsonSuffix
- expConfigFetcher := &smpb.ExpConfigFetcher{}
+ expConfigFetcher := &smpb.ExpConfigFetcher{Filename: &configName}
defer func() {
ctx.Metrics.ExpConfigFetcher(expConfigFetcher)
}()
+ if !config.GoogleProdCredsExist() {
+ status := smpb.ExpConfigFetcher_MISSING_GCERT
+ expConfigFetcher.Status = &status
+ return nil
+ }
s, err := os.Stat(configFetcher)
if err != nil {
@@ -163,8 +197,13 @@
return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode())
}
+ configExists := false
+ outConfigFilePath := filepath.Join(config.OutDir(), configName)
+ if _, err := os.Stat(outConfigFilePath); err == nil {
+ configExists = true
+ }
+
tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout)
- defer cancel()
fetchStart := time.Now()
cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir(),
"-output_config_name", configName)
@@ -174,33 +213,45 @@
return err
}
- 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 {
+ fetchCfg := func() error {
+ 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()))
+ expConfigFetcher.Filename = proto.String(outConfigFilePath)
+
+ if _, err := os.Stat(outConfigFilePath); err != nil {
+ status := smpb.ExpConfigFetcher_NO_CONFIG
+ expConfigFetcher.Status = &status
+ return err
+ }
status := smpb.ExpConfigFetcher_CONFIG
expConfigFetcher.Status = &status
- } else {
- status := smpb.ExpConfigFetcher_NO_CONFIG
- expConfigFetcher.Status = &status
- }
- return nil
-}
-
-func loadEnvConfig(ctx Context, config *configImpl) error {
- bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
- if bc == "" {
return nil
}
- if err := fetchEnvConfig(ctx, config, bc); err != nil {
- fmt.Fprintf(os.Stderr, "Failed to fetch config file: %v\n", err)
+ // If a config file does not exist, wait for the config file to be fetched. Otherwise
+ // fetch the config file in the background and return immediately.
+ if !configExists {
+ defer cancel()
+ return fetchCfg()
+ }
+
+ go func() {
+ defer cancel()
+ if err := fetchCfg(); err != nil {
+ ctx.Verbosef("Failed to fetch config file %v: %v\n", configName, err)
+ }
+ }()
+ return nil
+}
+
+func loadEnvConfig(ctx Context, config *configImpl, bc string) error {
+ if bc == "" {
+ return nil
}
configDirs := []string{
@@ -233,6 +284,33 @@
return nil
}
+func defaultBazelProdMode(cfg *configImpl) bool {
+ // Environment flag to disable Bazel for users which experience
+ // broken bazel-handled builds, or significant performance regressions.
+ if cfg.IsBazelMixedBuildForceDisabled() {
+ return false
+ }
+ // Darwin-host builds are currently untested with Bazel.
+ if runtime.GOOS == "darwin" {
+ return false
+ }
+ return true
+}
+
+func UploadOnlyConfig(ctx Context, _ ...string) Config {
+ ret := &configImpl{
+ environ: OsEnvironment(),
+ sandboxConfig: &SandboxConfig{},
+ }
+ srcDir := absPath(ctx, ".")
+ bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
+ if err := loadEnvConfig(ctx, ret, bc); err != nil {
+ ctx.Fatalln("Failed to parse env config files: %v", err)
+ }
+ ret.metricsUploader = GetMetricsUploader(srcDir, ret.environ)
+ return Config{ret}
+}
+
func NewConfig(ctx Context, args ...string) Config {
ret := &configImpl{
environ: OsEnvironment(),
@@ -244,9 +322,11 @@
ret.keepGoing = 1
ret.totalRAM = detectTotalRAM(ctx)
-
ret.parseArgs(ctx, args)
+ if ret.ninjaWeightListSource == HINT_FROM_SOONG {
+ ret.environ.Set("SOONG_GENERATES_NINJA_HINT", "true")
+ }
// Make sure OUT_DIR is set appropriately
if outDir, ok := ret.environ.Get("OUT_DIR"); ok {
ret.environ.Set("OUT_DIR", filepath.Clean(outDir))
@@ -264,8 +344,15 @@
// loadEnvConfig needs to know what the OUT_DIR is, so it should
// be called after we determine the appropriate out directory.
- if err := loadEnvConfig(ctx, ret); err != nil {
- ctx.Fatalln("Failed to parse env config files: %v", err)
+ bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
+
+ if bc != "" {
+ if err := fetchEnvConfig(ctx, ret, bc); err != nil {
+ ctx.Verbosef("Failed to fetch config file: %v\n", err)
+ }
+ if err := loadEnvConfig(ctx, ret, bc); err != nil {
+ ctx.Fatalln("Failed to parse env config files: %v", err)
+ }
}
if distDir, ok := ret.environ.Get("DIST_DIR"); ok {
@@ -307,6 +394,7 @@
"CDPATH",
"DISPLAY",
"GREP_OPTIONS",
+ "JAVAC",
"NDK_ROOT",
"POSIXLY_CORRECT",
@@ -386,13 +474,13 @@
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.")
}
- return java11Home
+ if toolchain17, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN"); ok && toolchain17 != "true" {
+ ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN is no longer supported. An OpenJDK 17 toolchain is now the global default.")
+ }
+ return java17Home
}()
absJavaHome := absPath(ctx, javaHome)
@@ -411,6 +499,10 @@
ret.environ.Set("ANDROID_JAVA11_HOME", java11Home)
ret.environ.Set("PATH", strings.Join(newPath, string(filepath.ListSeparator)))
+ if ret.MultitreeBuild() {
+ ret.environ.Set("MULTITREE_BUILD", "true")
+ }
+
outDir := ret.OutDir()
buildDateTimeFile := filepath.Join(outDir, "build_date.txt")
if buildDateTime, ok := ret.environ.Get("BUILD_DATETIME"); ok && buildDateTime != "" {
@@ -427,26 +519,16 @@
}
}
+ if ret.BuildFromTextStub() {
+ // TODO(b/271443071): support hidden api check for from-text stub build
+ ret.environ.Set("UNSAFE_DISABLE_HIDDENAPI_FLAGS", "true")
+ }
+
bpd := ret.BazelMetricsDir()
if err := os.RemoveAll(bpd); err != nil {
ctx.Fatalf("Unable to remove bazel profile directory %q: %v", bpd, err)
}
- ret.useBazel = ret.environ.IsEnvTrue("USE_BAZEL")
-
- if ret.UseBazel() {
- if err := os.MkdirAll(bpd, 0777); err != nil {
- ctx.Fatalf("Failed to create bazel profile directory %q: %v", bpd, err)
- }
- }
-
- if ret.UseBazel() {
- ret.riggedDistDirForBazel = filepath.Join(ret.OutDir(), "dist")
- } else {
- // Not rigged
- ret.riggedDistDirForBazel = ret.distDir
- }
-
c := Config{ret}
storeConfigMetrics(ctx, c)
return c
@@ -474,13 +556,29 @@
ctx.Metrics.SystemResourceInfo(s)
}
+func getNinjaWeightListSourceInMetric(s NinjaWeightListSource) *smpb.BuildConfig_NinjaWeightListSource {
+ switch s {
+ case NINJA_LOG:
+ return smpb.BuildConfig_NINJA_LOG.Enum()
+ case EVENLY_DISTRIBUTED:
+ return smpb.BuildConfig_EVENLY_DISTRIBUTED.Enum()
+ case EXTERNAL_FILE:
+ return smpb.BuildConfig_EXTERNAL_FILE.Enum()
+ case HINT_FROM_SOONG:
+ return smpb.BuildConfig_HINT_FROM_SOONG.Enum()
+ default:
+ return smpb.BuildConfig_NOT_USED.Enum()
+ }
+}
+
func buildConfig(config Config) *smpb.BuildConfig {
c := &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(config.ForceUseGoma()),
- UseGoma: proto.Bool(config.UseGoma()),
- UseRbe: proto.Bool(config.UseRBE()),
- BazelAsNinja: proto.Bool(config.UseBazel()),
- BazelMixedBuild: proto.Bool(config.bazelBuildMode() == mixedBuild),
+ ForceUseGoma: proto.Bool(config.ForceUseGoma()),
+ UseGoma: proto.Bool(config.UseGoma()),
+ UseRbe: proto.Bool(config.UseRBE()),
+ BazelMixedBuild: proto.Bool(config.BazelBuildEnabled()),
+ ForceDisableBazelMixedBuild: proto.Bool(config.IsBazelMixedBuildForceDisabled()),
+ NinjaWeightListSource: getNinjaWeightListSourceInMetric(config.NinjaWeightListSource()),
}
c.Targets = append(c.Targets, config.arguments...)
@@ -729,8 +827,62 @@
c.skipConfig = true
} else if arg == "--skip-soong-tests" {
c.skipSoongTests = true
+ } else if arg == "--skip-metrics-upload" {
+ c.skipMetricsUpload = true
} else if arg == "--mk-metrics" {
c.reportMkMetrics = true
+ } else if arg == "--multitree-build" {
+ c.multitreeBuild = true
+ } else if arg == "--bazel-mode" {
+ c.bazelProdMode = true
+ } else if arg == "--bazel-mode-dev" {
+ c.bazelDevMode = true
+ } else if arg == "--bazel-mode-staging" {
+ c.bazelStagingMode = true
+ } else if arg == "--search-api-dir" {
+ c.searchApiDir = true
+ } else if strings.HasPrefix(arg, "--ninja_weight_source=") {
+ source := strings.TrimPrefix(arg, "--ninja_weight_source=")
+ if source == "ninja_log" {
+ c.ninjaWeightListSource = NINJA_LOG
+ } else if source == "evenly_distributed" {
+ c.ninjaWeightListSource = EVENLY_DISTRIBUTED
+ } else if source == "not_used" {
+ c.ninjaWeightListSource = NOT_USED
+ } else if source == "soong" {
+ c.ninjaWeightListSource = HINT_FROM_SOONG
+ } else if strings.HasPrefix(source, "file,") {
+ c.ninjaWeightListSource = EXTERNAL_FILE
+ filePath := strings.TrimPrefix(source, "file,")
+ err := validateNinjaWeightList(filePath)
+ if err != nil {
+ ctx.Fatalf("Malformed weight list from %s: %s", filePath, err)
+ }
+ _, err = copyFile(filePath, filepath.Join(c.OutDir(), ".ninja_weight_list"))
+ if err != nil {
+ ctx.Fatalf("Error to copy ninja weight list from %s: %s", filePath, err)
+ }
+ } else {
+ ctx.Fatalf("unknown option for ninja_weight_source: %s", source)
+ }
+ } else if arg == "--build-from-text-stub" {
+ c.buildFromTextStub = true
+ } else if strings.HasPrefix(arg, "--build-command=") {
+ buildCmd := strings.TrimPrefix(arg, "--build-command=")
+ // remove quotations
+ buildCmd = strings.TrimPrefix(buildCmd, "\"")
+ buildCmd = strings.TrimSuffix(buildCmd, "\"")
+ ctx.Metrics.SetBuildCommand([]string{buildCmd})
+ } else if strings.HasPrefix(arg, "--bazel-force-enabled-modules=") {
+ c.bazelForceEnabledModules = strings.TrimPrefix(arg, "--bazel-force-enabled-modules=")
+ } else if strings.HasPrefix(arg, "--build-started-time-unix-millis=") {
+ buildTimeStr := strings.TrimPrefix(arg, "--build-started-time-unix-millis=")
+ val, err := strconv.ParseInt(buildTimeStr, 10, 64)
+ if err == nil {
+ c.buildStartedTime = val
+ } else {
+ ctx.Fatalf("Error parsing build-time-started-unix-millis", err)
+ }
} else if len(arg) > 0 && arg[0] == '-' {
parseArgNum := func(def int) int {
if len(arg) > 2 {
@@ -767,6 +919,8 @@
c.jsonModuleGraph = true
} else if arg == "bp2build" {
c.bp2build = true
+ } else if arg == "api_bp2build" {
+ c.apiBp2build = true
} else if arg == "queryview" {
c.queryview = true
} else if arg == "soong_docs" {
@@ -778,6 +932,28 @@
c.arguments = append(c.arguments, arg)
}
}
+ if (!c.bazelProdMode) && (!c.bazelDevMode) && (!c.bazelStagingMode) {
+ c.bazelProdMode = defaultBazelProdMode(c)
+ }
+}
+
+func validateNinjaWeightList(weightListFilePath string) (err error) {
+ data, err := os.ReadFile(weightListFilePath)
+ if err != nil {
+ return
+ }
+ lines := strings.Split(strings.TrimSpace(string(data)), "\n")
+ for _, line := range lines {
+ fields := strings.Split(line, ",")
+ if len(fields) != 2 {
+ return fmt.Errorf("wrong format, each line should have two fields, but '%s'", line)
+ }
+ _, err = strconv.Atoi(fields[1])
+ if err != nil {
+ return
+ }
+ }
+ return
}
func (c *configImpl) configureLocale(ctx Context) {
@@ -844,7 +1020,7 @@
return true
}
- if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() {
+ if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() && !c.ApiBp2build() {
// Command line was empty, the default Ninja target is built
return true
}
@@ -866,11 +1042,7 @@
}
func (c *configImpl) DistDir() string {
- if c.UseBazel() {
- return c.riggedDistDirForBazel
- } else {
- return c.distDir
- }
+ return c.distDir
}
func (c *configImpl) RealDistDir() string {
@@ -888,10 +1060,18 @@
return filepath.Join(c.OutDir(), "bazel")
}
+func (c *configImpl) bazelOutputBase() string {
+ return filepath.Join(c.BazelOutDir(), "output")
+}
+
func (c *configImpl) SoongOutDir() string {
return filepath.Join(c.OutDir(), "soong")
}
+func (c *configImpl) ApiSurfacesOutDir() string {
+ return filepath.Join(c.OutDir(), "api_surfaces")
+}
+
func (c *configImpl) PrebuiltOS() string {
switch runtime.GOOS {
case "linux":
@@ -919,7 +1099,11 @@
return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+tag)
}
-func (c *configImpl) Bp2BuildMarkerFile() string {
+func (c *configImpl) Bp2BuildFilesMarkerFile() string {
+ return shared.JoinPath(c.SoongOutDir(), "bp2build_files_marker")
+}
+
+func (c *configImpl) Bp2BuildWorkspaceMarkerFile() string {
return shared.JoinPath(c.SoongOutDir(), "bp2build_workspace_marker")
}
@@ -931,6 +1115,10 @@
return shared.JoinPath(c.SoongOutDir(), "queryview.marker")
}
+func (c *configImpl) ApiBp2buildMarkerFile() string {
+ return shared.JoinPath(c.SoongOutDir(), "api_bp2build.marker")
+}
+
func (c *configImpl) ModuleGraphFile() string {
return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
}
@@ -972,6 +1160,10 @@
return c.bp2build
}
+func (c *configImpl) ApiBp2build() bool {
+ return c.apiBp2build
+}
+
func (c *configImpl) Queryview() bool {
return c.queryview
}
@@ -984,6 +1176,14 @@
return c.verbose
}
+func (c *configImpl) MultitreeBuild() bool {
+ return c.multitreeBuild
+}
+
+func (c *configImpl) NinjaWeightListSource() NinjaWeightListSource {
+ return c.ninjaWeightListSource
+}
+
func (c *configImpl) SkipKati() bool {
return c.skipKati
}
@@ -1008,6 +1208,10 @@
return c.skipConfig
}
+func (c *configImpl) BuildFromTextStub() bool {
+ return c.buildFromTextStub
+}
+
func (c *configImpl) TargetProduct() string {
if v, ok := c.environ.Get("TARGET_PRODUCT"); ok {
return v
@@ -1038,6 +1242,30 @@
return c.parallel
}
+func (c *configImpl) GetSourceRootDirs() []string {
+ return c.sourceRootDirs
+}
+
+func (c *configImpl) SetSourceRootDirs(i []string) {
+ c.sourceRootDirs = i
+}
+
+func (c *configImpl) GetIncludeTags() []string {
+ return c.includeTags
+}
+
+func (c *configImpl) SetIncludeTags(i []string) {
+ c.includeTags = i
+}
+
+func (c *configImpl) GetLogsPrefix() string {
+ return c.logsPrefix
+}
+
+func (c *configImpl) SetLogsPrefix(prefix string) {
+ c.logsPrefix = prefix
+}
+
func (c *configImpl) HighmemParallel() int {
if i, ok := c.environ.GetInt("NINJA_HIGHMEM_NUM_JOBS"); ok {
return i
@@ -1118,16 +1346,8 @@
return false
}
-func (c *configImpl) UseBazel() bool {
- return c.useBazel
-}
-
-func (c *configImpl) bazelBuildMode() bazelBuildMode {
- if c.Environment().IsEnvTrue("USE_BAZEL_ANALYSIS") {
- return mixedBuild
- } else {
- return noBazel
- }
+func (c *configImpl) BazelBuildEnabled() bool {
+ return c.bazelProdMode || c.bazelDevMode || c.bazelStagingMode
}
func (c *configImpl) StartRBE() bool {
@@ -1144,34 +1364,34 @@
return true
}
-func (c *configImpl) rbeLogDir() string {
- for _, f := range []string{"RBE_log_dir", "FLAG_log_dir"} {
+func (c *configImpl) rbeProxyLogsDir() string {
+ for _, f := range []string{"RBE_proxy_log_dir", "FLAG_output_dir"} {
if v, ok := c.environ.Get(f); ok {
return v
}
}
- if c.Dist() {
- return c.LogsDir()
- }
- return c.OutDir()
+ buildTmpDir := shared.TempDirForOutDir(c.SoongOutDir())
+ return filepath.Join(buildTmpDir, "rbe")
}
-func (c *configImpl) rbeStatsOutputDir() string {
- for _, f := range []string{"RBE_output_dir", "FLAG_output_dir"} {
+func (c *configImpl) rbeCacheDir() string {
+ for _, f := range []string{"RBE_cache_dir", "FLAG_cache_dir"} {
if v, ok := c.environ.Get(f); ok {
return v
}
}
- return c.rbeLogDir()
+ return shared.JoinPath(c.SoongOutDir(), "rbe")
}
-func (c *configImpl) rbeLogPath() string {
- for _, f := range []string{"RBE_log_path", "FLAG_log_path"} {
- if v, ok := c.environ.Get(f); ok {
- return v
+func (c *configImpl) shouldCleanupRBELogsDir() bool {
+ // Perform a log directory cleanup only when the log directory
+ // is auto created by the build rather than user-specified.
+ for _, f := range []string{"RBE_proxy_log_dir", "FLAG_output_dir"} {
+ if _, ok := c.environ.Get(f); ok {
+ return false
}
}
- return fmt.Sprintf("text://%v/reproxy_log.txt", c.rbeLogDir())
+ return true
}
func (c *configImpl) rbeExecRoot() string {
@@ -1223,10 +1443,61 @@
return "RBE_use_application_default_credentials", "true"
}
+func (c *configImpl) rbeSockAddr(dir string) (string, error) {
+ maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
+ base := fmt.Sprintf("reproxy_%v.sock", rbeRandPrefix)
+
+ name := filepath.Join(dir, base)
+ if len(name) < maxNameLen {
+ return name, nil
+ }
+
+ name = filepath.Join("/tmp", base)
+ if len(name) < maxNameLen {
+ return name, nil
+ }
+
+ return "", fmt.Errorf("cannot generate a proxy socket address shorter than the limit of %v", maxNameLen)
+}
+
+// IsGooglerEnvironment returns true if the current build is running
+// on a Google developer machine and false otherwise.
+func (c *configImpl) IsGooglerEnvironment() bool {
+ cf := "ANDROID_BUILD_ENVIRONMENT_CONFIG"
+ if v, ok := c.environ.Get(cf); ok {
+ return v == "googler"
+ }
+ return false
+}
+
+// GoogleProdCredsExist determine whether credentials exist on the
+// Googler machine to use remote execution.
+func (c *configImpl) GoogleProdCredsExist() bool {
+ if googleProdCredsExistCache {
+ return googleProdCredsExistCache
+ }
+ if _, err := exec.Command("/usr/bin/prodcertstatus", "--simple_output", "--nocheck_loas").Output(); err != nil {
+ return false
+ }
+ googleProdCredsExistCache = true
+ return true
+}
+
+// UseRemoteBuild indicates whether to use a remote build acceleration system
+// to speed up the build.
func (c *configImpl) UseRemoteBuild() bool {
return c.UseGoma() || c.UseRBE()
}
+// StubbyExists checks whether the stubby binary exists on the machine running
+// the build.
+func (c *configImpl) StubbyExists() bool {
+ if _, err := exec.LookPath("stubby"); err != nil {
+ return false
+ }
+ return true
+}
+
// RemoteParallel controls how many remote jobs (i.e., commands which contain
// gomacc) are run in parallel. Note the parallelism of all other jobs is
// still limited by Parallel()
@@ -1272,6 +1543,10 @@
return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiPackageSuffix+".ninja")
}
+func (c *configImpl) SoongVarsFile() string {
+ return filepath.Join(c.SoongOutDir(), "soong.variables")
+}
+
func (c *configImpl) SoongNinjaFile() string {
return filepath.Join(c.SoongOutDir(), "build.ninja")
}
@@ -1419,6 +1694,31 @@
return c.emptyNinjaFile
}
+func (c *configImpl) IsBazelMixedBuildForceDisabled() bool {
+ return c.Environment().IsEnvTrue("BUILD_BROKEN_DISABLE_BAZEL")
+}
+
+func (c *configImpl) IsPersistentBazelEnabled() bool {
+ return c.Environment().IsEnvTrue("USE_PERSISTENT_BAZEL")
+}
+
+func (c *configImpl) BazelModulesForceEnabledByFlag() string {
+ return c.bazelForceEnabledModules
+}
+
+func (c *configImpl) SkipMetricsUpload() bool {
+ return c.skipMetricsUpload
+}
+
+// Returns a Time object if one was passed via a command-line flag.
+// Otherwise returns the passed default.
+func (c *configImpl) BuildStartedTimeOrDefault(defaultTime time.Time) time.Time {
+ if c.buildStartedTime == 0 {
+ return defaultTime
+ }
+ return time.UnixMilli(c.buildStartedTime)
+}
+
func GetMetricsUploader(topDir string, env *Environment) string {
if p, ok := env.Get("METRICS_UPLOADER"); ok {
metricsUploader := filepath.Join(topDir, p)
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index e293275..a1eccf0 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -29,6 +29,8 @@
smpb "android/soong/ui/metrics/metrics_proto"
"android/soong/ui/status"
+ "google.golang.org/protobuf/encoding/prototext"
+
"google.golang.org/protobuf/proto"
)
@@ -88,7 +90,9 @@
t.Fatal(err)
})
+ env := Environment([]string{})
c := &configImpl{
+ environ: &env,
parallel: -1,
keepGoing: -1,
}
@@ -899,6 +903,15 @@
tidyOnly: "",
expectedArgs: []string{},
}, {
+ description: "multitree build action executed at root directory",
+ dirsInTrees: []string{},
+ buildFiles: []string{},
+ rootSymlink: false,
+ args: []string{"--multitree-build"},
+ curDir: ".",
+ tidyOnly: "",
+ expectedArgs: []string{"--multitree-build"},
+ }, {
description: "build action executed at root directory in symlink",
dirsInTrees: []string{},
buildFiles: []string{},
@@ -1005,50 +1018,69 @@
environ Environment
arguments []string
useBazel bool
+ bazelDevMode bool
+ bazelProdMode bool
+ bazelStagingMode bool
expectedBuildConfig *smpb.BuildConfig
}{
{
name: "none set",
environ: Environment{},
expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelAsNinja: proto.Bool(false),
- BazelMixedBuild: proto.Bool(false),
+ ForceUseGoma: proto.Bool(false),
+ UseGoma: proto.Bool(false),
+ UseRbe: proto.Bool(false),
+ BazelMixedBuild: proto.Bool(false),
+ ForceDisableBazelMixedBuild: proto.Bool(false),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
},
},
{
name: "force use goma",
environ: Environment{"FORCE_USE_GOMA=1"},
expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(true),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelAsNinja: proto.Bool(false),
- BazelMixedBuild: proto.Bool(false),
+ ForceUseGoma: proto.Bool(true),
+ UseGoma: proto.Bool(false),
+ UseRbe: proto.Bool(false),
+ BazelMixedBuild: proto.Bool(false),
+ ForceDisableBazelMixedBuild: proto.Bool(false),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
},
},
{
name: "use goma",
environ: Environment{"USE_GOMA=1"},
expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(true),
- UseRbe: proto.Bool(false),
- BazelAsNinja: proto.Bool(false),
- BazelMixedBuild: proto.Bool(false),
+ ForceUseGoma: proto.Bool(false),
+ UseGoma: proto.Bool(true),
+ UseRbe: proto.Bool(false),
+ BazelMixedBuild: proto.Bool(false),
+ ForceDisableBazelMixedBuild: proto.Bool(false),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
},
},
{
name: "use rbe",
environ: Environment{"USE_RBE=1"},
expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(true),
- BazelAsNinja: proto.Bool(false),
- BazelMixedBuild: proto.Bool(false),
+ ForceUseGoma: proto.Bool(false),
+ UseGoma: proto.Bool(false),
+ UseRbe: proto.Bool(true),
+ BazelMixedBuild: proto.Bool(false),
+ ForceDisableBazelMixedBuild: proto.Bool(false),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
+ },
+ },
+ {
+ name: "disable mixed builds",
+ environ: Environment{"BUILD_BROKEN_DISABLE_BAZEL=1"},
+ expectedBuildConfig: &smpb.BuildConfig{
+ ForceUseGoma: proto.Bool(false),
+ UseGoma: proto.Bool(false),
+ UseRbe: proto.Bool(false),
+ BazelMixedBuild: proto.Bool(false),
+ ForceDisableBazelMixedBuild: proto.Bool(true),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
},
},
{
@@ -1056,22 +1088,51 @@
environ: Environment{},
useBazel: true,
expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelAsNinja: proto.Bool(true),
- BazelMixedBuild: proto.Bool(false),
+ ForceUseGoma: proto.Bool(false),
+ UseGoma: proto.Bool(false),
+ UseRbe: proto.Bool(false),
+ BazelMixedBuild: proto.Bool(false),
+ ForceDisableBazelMixedBuild: proto.Bool(false),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
},
},
{
- name: "bazel mixed build",
- environ: Environment{"USE_BAZEL_ANALYSIS=1"},
+ name: "bazel mixed build from dev mode",
+ environ: Environment{},
+ bazelDevMode: true,
expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelAsNinja: proto.Bool(false),
- BazelMixedBuild: proto.Bool(true),
+ ForceUseGoma: proto.Bool(false),
+ UseGoma: proto.Bool(false),
+ UseRbe: proto.Bool(false),
+ BazelMixedBuild: proto.Bool(true),
+ ForceDisableBazelMixedBuild: proto.Bool(false),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
+ },
+ },
+ {
+ name: "bazel mixed build from prod mode",
+ environ: Environment{},
+ bazelProdMode: true,
+ expectedBuildConfig: &smpb.BuildConfig{
+ ForceUseGoma: proto.Bool(false),
+ UseGoma: proto.Bool(false),
+ UseRbe: proto.Bool(false),
+ BazelMixedBuild: proto.Bool(true),
+ ForceDisableBazelMixedBuild: proto.Bool(false),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
+ },
+ },
+ {
+ name: "bazel mixed build from staging mode",
+ environ: Environment{},
+ bazelStagingMode: true,
+ expectedBuildConfig: &smpb.BuildConfig{
+ ForceUseGoma: proto.Bool(false),
+ UseGoma: proto.Bool(false),
+ UseRbe: proto.Bool(false),
+ BazelMixedBuild: proto.Bool(true),
+ ForceDisableBazelMixedBuild: proto.Bool(false),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
},
},
{
@@ -1080,12 +1141,13 @@
useBazel: true,
arguments: []string{"droid", "dist"},
expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelAsNinja: proto.Bool(true),
- BazelMixedBuild: proto.Bool(false),
- Targets: []string{"droid", "dist"},
+ ForceUseGoma: proto.Bool(false),
+ UseGoma: proto.Bool(false),
+ UseRbe: proto.Bool(false),
+ BazelMixedBuild: proto.Bool(false),
+ Targets: []string{"droid", "dist"},
+ ForceDisableBazelMixedBuild: proto.Bool(false),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
},
},
{
@@ -1094,30 +1156,38 @@
"FORCE_USE_GOMA=1",
"USE_GOMA=1",
"USE_RBE=1",
- "USE_BAZEL_ANALYSIS=1",
+ "BUILD_BROKEN_DISABLE_BAZEL=1",
},
- useBazel: true,
+ useBazel: true,
+ bazelDevMode: true,
expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(true),
- UseGoma: proto.Bool(true),
- UseRbe: proto.Bool(true),
- BazelAsNinja: proto.Bool(true),
- BazelMixedBuild: proto.Bool(true),
+ ForceUseGoma: proto.Bool(true),
+ UseGoma: proto.Bool(true),
+ UseRbe: proto.Bool(true),
+ BazelMixedBuild: proto.Bool(true),
+ ForceDisableBazelMixedBuild: proto.Bool(true),
+ NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
},
},
}
+ ctx := testContext()
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
c := &configImpl{
- environ: &tc.environ,
- useBazel: tc.useBazel,
- arguments: tc.arguments,
+ environ: &tc.environ,
+ bazelDevMode: tc.bazelDevMode,
+ bazelProdMode: tc.bazelProdMode,
+ bazelStagingMode: tc.bazelStagingMode,
+ arguments: tc.arguments,
}
config := Config{c}
+ checkBazelMode(ctx, config)
actualBuildConfig := buildConfig(config)
if expected := tc.expectedBuildConfig; !proto.Equal(expected, actualBuildConfig) {
- t.Errorf("Expected build config != actual build config: %#v != %#v", *expected, *actualBuildConfig)
+ t.Errorf("Build config mismatch.\n"+
+ "Expected build config: %#v\n"+
+ "Actual build config: %#v", prototext.Format(expected), prototext.Format(actualBuildConfig))
}
})
}
diff --git a/ui/build/context.go b/ui/build/context.go
index 4a4352c..fd20e26 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -40,6 +40,8 @@
Thread tracer.Thread
Tracer tracer.Tracer
+
+ CriticalPath *status.CriticalPath
}
// BeginTrace starts a new Duration Event.
@@ -48,7 +50,7 @@
c.Tracer.Begin(desc, c.Thread)
}
if c.Metrics != nil {
- c.Metrics.EventTracer.Begin(name, desc, c.Thread)
+ c.Metrics.EventTracer.Begin(name, desc)
}
}
@@ -58,7 +60,7 @@
c.Tracer.End(c.Thread)
}
if c.Metrics != nil {
- c.Metrics.SetTimeMetrics(c.Metrics.EventTracer.End(c.Thread))
+ c.Metrics.SetTimeMetrics(c.Metrics.EventTracer.End())
}
}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 45bfe5d..efe7478 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -84,6 +84,14 @@
ctx.BeginTrace(metrics.RunKati, "dumpvars")
defer ctx.EndTrace()
+ tool := ctx.Status.StartTool()
+ if write_soong_vars {
+ // only print this when write_soong_vars is true so that it's not printed when using
+ // the get_build_var command.
+ tool.Status("Running product configuration...")
+ }
+ defer tool.Finish()
+
cmd := Command(ctx, config, "dumpvars",
config.PrebuiltBuildTool("ckati"),
"-f", "build/make/core/config.mk",
@@ -108,7 +116,7 @@
}
cmd.StartOrFatal()
// TODO: error out when Stderr contains any content
- status.KatiReader(ctx.Status.StartTool(), pipe)
+ status.KatiReader(tool, pipe)
cmd.WaitOrFatal()
ret := make(map[string]string, len(vars))
@@ -139,9 +147,10 @@
var BannerVars = []string{
"PLATFORM_VERSION_CODENAME",
"PLATFORM_VERSION",
+ "PRODUCT_INCLUDE_TAGS",
+ "PRODUCT_SOURCE_ROOT_DIRS",
"TARGET_PRODUCT",
"TARGET_BUILD_VARIANT",
- "TARGET_BUILD_TYPE",
"TARGET_BUILD_APPS",
"TARGET_BUILD_UNBUNDLED",
"TARGET_ARCH",
@@ -150,18 +159,11 @@
"TARGET_2ND_ARCH",
"TARGET_2ND_ARCH_VARIANT",
"TARGET_2ND_CPU_VARIANT",
- "HOST_ARCH",
- "HOST_2ND_ARCH",
"HOST_OS",
"HOST_OS_EXTRA",
"HOST_CROSS_OS",
- "HOST_CROSS_ARCH",
- "HOST_CROSS_2ND_ARCH",
- "HOST_BUILD_TYPE",
"BUILD_ID",
"OUT_DIR",
- "AUX_OS_VARIANT_LIST",
- "PRODUCT_SOONG_NAMESPACES",
"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE",
}
@@ -233,6 +235,13 @@
// Not used, but useful to be in the soong.log
"BOARD_VNDK_VERSION",
+ "TARGET_BUILD_TYPE",
+ "HOST_ARCH",
+ "HOST_2ND_ARCH",
+ "HOST_CROSS_ARCH",
+ "HOST_CROSS_2ND_ARCH",
+ "HOST_BUILD_TYPE",
+ "PRODUCT_SOONG_NAMESPACES",
"DEFAULT_WARNING_BUILD_MODULE_TYPES",
"DEFAULT_ERROR_BUILD_MODULE_TYPES",
@@ -242,8 +251,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",
@@ -292,4 +299,6 @@
config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true")
config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(makeVars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
+ config.SetIncludeTags(strings.Fields(makeVars["PRODUCT_INCLUDE_TAGS"]))
+ config.SetSourceRootDirs(strings.Fields(makeVars["PRODUCT_SOURCE_ROOT_DIRS"]))
}
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 262de3d..62079fe 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -63,7 +63,8 @@
// Set up configuration parameters for the Finder cache.
cacheParams := finder.CacheParams{
WorkingDirectory: dir,
- RootDirs: []string{"."},
+ RootDirs: androidBpSearchDirs(config),
+ FollowSymlinks: config.environ.IsEnvTrue("ALLOW_BP_UNDER_SYMLINKS"),
ExcludeDirs: []string{".git", ".repo"},
PruneFiles: pruneFiles,
IncludeFiles: []string{
@@ -86,6 +87,8 @@
"TEST_MAPPING",
// Bazel top-level file to mark a directory as a Bazel workspace.
"WORKSPACE",
+ // METADATA file of packages
+ "METADATA",
},
// Bazel Starlark configuration files and all .mk files for product/board configuration.
IncludeSuffixes: []string{".bzl", ".mk"},
@@ -99,6 +102,15 @@
return f
}
+func androidBpSearchDirs(config Config) []string {
+ dirs := []string{"."} // always search from root of source tree.
+ if config.searchApiDir {
+ // Search in out/api_surfaces
+ dirs = append(dirs, config.ApiSurfacesOutDir())
+ }
+ return dirs
+}
+
// Finds the list of Bazel-related files (BUILD, WORKSPACE and Starlark) in the tree.
func findBazelFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) {
matches := []string{}
@@ -179,6 +191,13 @@
ctx.Fatalf("Could not find OWNERS: %v", err)
}
+ // Recursively look for all METADATA files.
+ metadataFiles := f.FindNamedAt(".", "METADATA")
+ err = dumpListToFile(ctx, config, metadataFiles, filepath.Join(dumpDir, "METADATA.list"))
+ if err != nil {
+ ctx.Fatalf("Could not find METADATA: %v", err)
+ }
+
// Recursively look for all TEST_MAPPING files.
testMappings := f.FindNamedAt(".", "TEST_MAPPING")
err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index dab1a9b..5d56531 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -23,10 +23,60 @@
"strings"
"time"
+ "android/soong/shared"
"android/soong/ui/metrics"
"android/soong/ui/status"
)
+const (
+ // File containing the environment state when ninja is executed
+ ninjaEnvFileName = "ninja.environment"
+ ninjaLogFileName = ".ninja_log"
+ ninjaWeightListFileName = ".ninja_weight_list"
+)
+
+func useNinjaBuildLog(ctx Context, config Config, cmd *Cmd) {
+ ninjaLogFile := filepath.Join(config.OutDir(), ninjaLogFileName)
+ data, err := os.ReadFile(ninjaLogFile)
+ var outputBuilder strings.Builder
+ if err == nil {
+ lines := strings.Split(strings.TrimSpace(string(data)), "\n")
+ // ninja log: <start> <end> <restat> <name> <cmdhash>
+ // ninja weight list: <name>,<end-start+1>
+ for _, line := range lines {
+ if strings.HasPrefix(line, "#") {
+ continue
+ }
+ fields := strings.Split(line, "\t")
+ path := fields[3]
+ start, err := strconv.Atoi(fields[0])
+ if err != nil {
+ continue
+ }
+ end, err := strconv.Atoi(fields[1])
+ if err != nil {
+ continue
+ }
+ outputBuilder.WriteString(path)
+ outputBuilder.WriteString(",")
+ outputBuilder.WriteString(strconv.Itoa(end-start+1) + "\n")
+ }
+ } else {
+ // If there is no ninja log file, just pass empty ninja weight list.
+ // Because it is still efficient with critical path calculation logic even without weight.
+ ctx.Verbosef("There is an error during reading ninja log, so ninja will use empty weight list: %s", err)
+ }
+
+ weightListFile := filepath.Join(config.OutDir(), ninjaWeightListFileName)
+
+ err = os.WriteFile(weightListFile, []byte(outputBuilder.String()), 0644)
+ if err == nil {
+ cmd.Args = append(cmd.Args, "-o", "usesweightlist="+weightListFile)
+ } else {
+ ctx.Panicf("Could not write ninja weight list file %s", err)
+ }
+}
+
// Constructs and runs the Ninja command line with a restricted set of
// environment variables. It's important to restrict the environment Ninja runs
// for hermeticity reasons, and to avoid spurious rebuilds.
@@ -79,6 +129,20 @@
cmd.Environment.AppendFromKati(config.KatiEnvFile())
}
+ switch config.NinjaWeightListSource() {
+ case NINJA_LOG:
+ useNinjaBuildLog(ctx, config, cmd)
+ case EVENLY_DISTRIBUTED:
+ // pass empty weight list means ninja considers every tasks's weight as 1(default value).
+ cmd.Args = append(cmd.Args, "-o", "usesweightlist=/dev/null")
+ case EXTERNAL_FILE:
+ fallthrough
+ case HINT_FROM_SOONG:
+ // The weight list is already copied/generated.
+ ninjaWeightListPath := filepath.Join(config.OutDir(), ninjaWeightListFileName)
+ cmd.Args = append(cmd.Args, "-o", "usesweightlist="+ninjaWeightListPath)
+ }
+
// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
// used in the past to specify extra ninja arguments.
if extra, ok := cmd.Environment.Get("NINJA_ARGS"); ok {
@@ -186,6 +250,21 @@
ctx.Verbosef(" %s", envVar)
}
+ // Write the env vars available during ninja execution to a file
+ ninjaEnvVars := cmd.Environment.AsMap()
+ data, err := shared.EnvFileContents(ninjaEnvVars)
+ if err != nil {
+ ctx.Panicf("Could not parse environment variables for ninja run %s", err)
+ }
+ // Write the file in every single run. This is fine because
+ // 1. It is not a dep of Soong analysis, so will not retrigger Soong analysis.
+ // 2. Is is fairly lightweight (~1Kb)
+ ninjaEnvVarsFile := shared.JoinPath(config.SoongOutDir(), ninjaEnvFileName)
+ err = os.WriteFile(ninjaEnvVarsFile, data, 0666)
+ if err != nil {
+ ctx.Panicf("Could not write ninja environment file %s", err)
+ }
+
// Poll the Ninja log for updates regularly based on the heartbeat
// frequency. If it isn't updated enough, then we want to surface the
// possibility that Ninja is stuck, to the user.
@@ -194,7 +273,7 @@
ticker := time.NewTicker(ninjaHeartbeatDuration)
defer ticker.Stop()
ninjaChecker := &ninjaStucknessChecker{
- logPath: filepath.Join(config.OutDir(), ".ninja_log"),
+ logPath: filepath.Join(config.OutDir(), ninjaLogFileName),
}
go func() {
for {
diff --git a/ui/build/path.go b/ui/build/path.go
index 86e61c0..29128d8 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -109,6 +109,15 @@
prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
+ if value, _ := config.Environment().Get("BUILD_BROKEN_PYTHON_IS_PYTHON2"); value == "true" {
+ py2Path, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86/py2")
+ if info, err := os.Stat(py2Path); err == nil && info.IsDir() {
+ myPath = py2Path + string(os.PathListSeparator) + myPath
+ }
+ } else if value != "" {
+ ctx.Fatalf("BUILD_BROKEN_PYTHON_IS_PYTHON2 can only be set to 'true' or an empty string, but got %s\n", value)
+ }
+
// Set $PATH to be the directories containing the host tool symlinks, and
// the prebuilts directory for the current host OS.
config.Environment().Set("PATH", myPath)
@@ -244,6 +253,15 @@
prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
+ if value, _ := config.Environment().Get("BUILD_BROKEN_PYTHON_IS_PYTHON2"); value == "true" {
+ py2Path, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86/py2")
+ if info, err := os.Stat(py2Path); err == nil && info.IsDir() {
+ myPath = py2Path + string(os.PathListSeparator) + myPath
+ }
+ } else if value != "" {
+ ctx.Fatalf("BUILD_BROKEN_PYTHON_IS_PYTHON2 can only be set to 'true' or an empty string, but got %s\n", value)
+ }
+
// Replace the $PATH variable with the path_interposer symlinks, and
// checked-in prebuilts.
config.Environment().Set("PATH", myPath)
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 831a80f..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,
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 8f9a699..6479925 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -16,13 +16,12 @@
import (
"fmt"
- "math/rand"
"os"
"path/filepath"
"runtime"
- "syscall"
- "time"
+ "strings"
+ "android/soong/remoteexec"
"android/soong/ui/metrics"
)
@@ -54,34 +53,18 @@
return cmdPath
}
-func sockAddr(dir string) (string, error) {
- maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
- rand.Seed(time.Now().UnixNano())
- base := fmt.Sprintf("reproxy_%v.sock", rand.Intn(1000))
-
- name := filepath.Join(dir, base)
- if len(name) < maxNameLen {
- return name, nil
- }
-
- name = filepath.Join("/tmp", base)
- if len(name) < maxNameLen {
- return name, nil
- }
-
- return "", fmt.Errorf("cannot generate a proxy socket address shorter than the limit of %v", maxNameLen)
-}
-
func getRBEVars(ctx Context, config Config) map[string]string {
vars := map[string]string{
- "RBE_log_path": config.rbeLogPath(),
- "RBE_log_dir": config.rbeLogDir(),
- "RBE_re_proxy": config.rbeReproxy(),
- "RBE_exec_root": config.rbeExecRoot(),
- "RBE_output_dir": config.rbeStatsOutputDir(),
+ "RBE_log_dir": config.rbeProxyLogsDir(),
+ "RBE_re_proxy": config.rbeReproxy(),
+ "RBE_exec_root": config.rbeExecRoot(),
+ "RBE_output_dir": config.rbeProxyLogsDir(),
+ "RBE_proxy_log_dir": config.rbeProxyLogsDir(),
+ "RBE_cache_dir": config.rbeCacheDir(),
+ "RBE_platform": "container-image=" + remoteexec.DefaultImage,
}
if config.StartRBE() {
- name, err := sockAddr(absPath(ctx, config.TempDir()))
+ name, err := config.rbeSockAddr(absPath(ctx, config.TempDir()))
if err != nil {
ctx.Fatalf("Error retrieving socket address: %v", err)
return nil
@@ -100,16 +83,37 @@
return vars
}
+func cleanupRBELogsDir(ctx Context, config Config) {
+ if !config.shouldCleanupRBELogsDir() {
+ return
+ }
+
+ rbeTmpDir := config.rbeProxyLogsDir()
+ if err := os.RemoveAll(rbeTmpDir); err != nil {
+ fmt.Fprintln(ctx.Writer, "\033[33mUnable to remove RBE log directory: ", err, "\033[0m")
+ }
+}
+
func startRBE(ctx Context, config Config) {
+ if !config.GoogleProdCredsExist() && prodCredsAuthType(config) {
+ ctx.Fatalf("Unable to start RBE reproxy\nFAILED: Missing LOAS credentials.")
+ }
ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
defer ctx.EndTrace()
+ ctx.Status.Status("Starting rbe...")
+
if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs {
ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs)
}
if n := ulimitOrFatal(ctx, config, "-n"); n < rbeLeastNFiles {
ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles)
}
+ if _, err := os.Stat(config.rbeProxyLogsDir()); os.IsNotExist(err) {
+ if err := os.MkdirAll(config.rbeProxyLogsDir(), 0744); err != nil {
+ ctx.Fatalf("Unable to create logs dir (%v) for RBE: %v", config.rbeProxyLogsDir, err)
+ }
+ }
cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd))
@@ -131,6 +135,34 @@
}
}
+func prodCredsAuthType(config Config) bool {
+ authVar, val := config.rbeAuth()
+ if strings.Contains(authVar, "use_google_prod_creds") && val != "" && val != "false" {
+ return true
+ }
+ return false
+}
+
+// Check whether proper auth exists for RBE builds run within a
+// Google dev environment.
+func CheckProdCreds(ctx Context, config Config) {
+ if !config.IsGooglerEnvironment() {
+ return
+ }
+ if !config.StubbyExists() && prodCredsAuthType(config) {
+ fmt.Fprintln(ctx.Writer, "")
+ fmt.Fprintln(ctx.Writer, fmt.Sprintf("\033[33mWARNING: %q binary not found in $PATH, follow go/build-fast-without-stubby instead for authenticating with RBE.\033[0m", "stubby"))
+ fmt.Fprintln(ctx.Writer, "")
+ return
+ }
+ if config.GoogleProdCredsExist() {
+ return
+ }
+ fmt.Fprintln(ctx.Writer, "")
+ fmt.Fprintln(ctx.Writer, "\033[33mWARNING: Missing LOAS credentials, please run `gcert`. This will result in failing builds in the future, see go/rbe-android-default-announcement.\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
@@ -151,7 +183,9 @@
return
}
- outputDir := config.rbeStatsOutputDir()
+ ctx.Status.Status("Dumping rbe metrics...")
+
+ outputDir := config.rbeProxyLogsDir()
if outputDir == "" {
ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
}
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
index 8ff96bc..266f76b 100644
--- a/ui/build/rbe_test.go
+++ b/ui/build/rbe_test.go
@@ -56,7 +56,8 @@
env := Environment(tt.env)
env.Set("OUT_DIR", tmpDir)
env.Set("RBE_DIR", tmpDir)
- env.Set("RBE_output_dir", t.TempDir())
+ env.Set("RBE_output_dir", tmpDir)
+ env.Set("RBE_proxy_log_dir", tmpDir)
config := Config{&configImpl{
environ: &env,
}}
diff --git a/ui/build/sandbox_config.go b/ui/build/sandbox_config.go
index 1b46459..1d32d86 100644
--- a/ui/build/sandbox_config.go
+++ b/ui/build/sandbox_config.go
@@ -27,6 +27,15 @@
return sc.srcDirIsRO
}
+// Return the mount flag of the source directory in the nsjail command
+func (sc *SandboxConfig) SrcDirMountFlag() string {
+ ret := "-B" // Read-write
+ if sc.SrcDirIsRO() {
+ ret = "-R" // Read-only
+ }
+ return ret
+}
+
func (sc *SandboxConfig) SetSrcDirRWAllowlist(allowlist []string) {
sc.srcDirRWAllowlist = allowlist
}
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index 5b2046e..edb3b66 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -101,7 +101,7 @@
// srcDir is /tmp/.* in integration tests, which is a child dir of /tmp
// nsjail throws an error if a child dir is mounted before its parent
"-B", "/tmp",
- "-B", sandboxConfig.srcDir,
+ c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
"-B", sandboxConfig.outDir,
}
@@ -148,13 +148,6 @@
func (c *Cmd) wrapSandbox() {
wd, _ := os.Getwd()
- var srcDirMountFlag string
- if c.config.sandboxConfig.SrcDirIsRO() {
- srcDirMountFlag = "-R"
- } else {
- srcDirMountFlag = "-B" //Read-Write
- }
-
sandboxArgs := []string{
// The executable to run
"-x", c.Path,
@@ -195,7 +188,7 @@
"-B", "/tmp",
// Mount source
- srcDirMountFlag, sandboxConfig.srcDir,
+ c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
//Mount out dir as read-write
"-B", sandboxConfig.outDir,
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 8992b4f..8ead4c9 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,37 +15,34 @@
package build
import (
- "errors"
"fmt"
- "io/fs"
- "io/ioutil"
"os"
"path/filepath"
"strconv"
+ "strings"
+ "android/soong/bazel"
"android/soong/ui/metrics"
- soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
"android/soong/ui/status"
"android/soong/shared"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
- "github.com/google/blueprint/deptools"
"github.com/google/blueprint/microfactory"
-
- "google.golang.org/protobuf/proto"
)
const (
availableEnvFile = "soong.environment.available"
usedEnvFile = "soong.environment.used"
- soongBuildTag = "build"
- bp2buildTag = "bp2build"
- jsonModuleGraphTag = "modulegraph"
- queryviewTag = "queryview"
- soongDocsTag = "soong_docs"
+ soongBuildTag = "build"
+ bp2buildFilesTag = "bp2build_files"
+ bp2buildWorkspaceTag = "bp2build_workspace"
+ jsonModuleGraphTag = "modulegraph"
+ queryviewTag = "queryview"
+ apiBp2buildTag = "api_bp2build"
+ soongDocsTag = "soong_docs"
// bootstrapEpoch is used to determine if an incremental build is incompatible with the current
// version of bootstrap and needs cleaning before continuing the build. Increment this for
@@ -54,13 +51,13 @@
bootstrapEpoch = 1
)
-func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error {
+func writeEnvironmentFile(_ Context, envFile string, envDeps map[string]string) error {
data, err := shared.EnvFileContents(envDeps)
if err != nil {
return err
}
- return ioutil.WriteFile(envFile, data, 0644)
+ return os.WriteFile(envFile, data, 0644)
}
// This uses Android.bp files and various tools to generate <builddir>/build.ninja.
@@ -139,7 +136,7 @@
if exists, err := fileExists(path); err != nil {
ctx.Fatalf("Failed to check if file '%s' exists: %s", path, err)
} else if !exists {
- err = ioutil.WriteFile(path, nil, 0666)
+ err = os.WriteFile(path, nil, 0666)
if err != nil {
ctx.Fatalf("Failed to create empty file '%s': %s", path, err)
}
@@ -155,25 +152,35 @@
return true, nil
}
-func primaryBuilderInvocation(
- config Config,
- name string,
- output string,
- specificArgs []string,
- description string) bootstrap.PrimaryBuilderInvocation {
+type PrimaryBuilderFactory struct {
+ name string
+ description string
+ config Config
+ output string
+ specificArgs []string
+ debugPort string
+}
+
+func (pb PrimaryBuilderFactory) primaryBuilderInvocation() bootstrap.PrimaryBuilderInvocation {
commonArgs := make([]string, 0, 0)
- if !config.skipSoongTests {
+ if !pb.config.skipSoongTests {
commonArgs = append(commonArgs, "-t")
}
- commonArgs = append(commonArgs, "-l", filepath.Join(config.FileListDir(), "Android.bp.list"))
- invocationEnv := make(map[string]string)
- debugMode := os.Getenv("SOONG_DELVE") != ""
+ if pb.config.multitreeBuild {
+ commonArgs = append(commonArgs, "--multitree-build")
+ }
+ if pb.config.buildFromTextStub {
+ commonArgs = append(commonArgs, "--build-from-text-stub")
+ }
- if debugMode {
- commonArgs = append(commonArgs, "--delve_listen", os.Getenv("SOONG_DELVE"))
- commonArgs = append(commonArgs, "--delve_path", shared.ResolveDelveBinary())
+ commonArgs = append(commonArgs, "-l", filepath.Join(pb.config.FileListDir(), "Android.bp.list"))
+ invocationEnv := make(map[string]string)
+ if pb.debugPort != "" {
+ //debug mode
+ commonArgs = append(commonArgs, "--delve_listen", pb.debugPort,
+ "--delve_path", shared.ResolveDelveBinary())
// GODEBUG=asyncpreemptoff=1 disables the preemption of goroutines. This
// is useful because the preemption happens by sending SIGURG to the OS
// thread hosting the goroutine in question and each signal results in
@@ -186,21 +193,27 @@
invocationEnv["GODEBUG"] = "asyncpreemptoff=1"
}
- allArgs := make([]string, 0, 0)
- allArgs = append(allArgs, specificArgs...)
+ var allArgs []string
+ allArgs = append(allArgs, pb.specificArgs...)
allArgs = append(allArgs,
- "--globListDir", name,
- "--globFile", config.NamedGlobFile(name))
+ "--globListDir", pb.name,
+ "--globFile", pb.config.NamedGlobFile(pb.name))
allArgs = append(allArgs, commonArgs...)
- allArgs = append(allArgs, environmentArgs(config, name)...)
+ allArgs = append(allArgs, environmentArgs(pb.config, pb.name)...)
+ if profileCpu := os.Getenv("SOONG_PROFILE_CPU"); profileCpu != "" {
+ allArgs = append(allArgs, "--cpuprofile", profileCpu+"."+pb.name)
+ }
+ if profileMem := os.Getenv("SOONG_PROFILE_MEM"); profileMem != "" {
+ allArgs = append(allArgs, "--memprofile", profileMem+"."+pb.name)
+ }
allArgs = append(allArgs, "Android.bp")
return bootstrap.PrimaryBuilderInvocation{
Inputs: []string{"Android.bp"},
- Outputs: []string{output},
+ Outputs: []string{pb.output},
Args: allArgs,
- Description: description,
+ Description: pb.description,
// NB: Changing the value of this environment variable will not result in a
// rebuild. The bootstrap Ninja file will change, but apparently Ninja does
// not consider changing the pool specified in a statement a change that's
@@ -235,9 +248,10 @@
func bootstrapGlobFileList(config Config) []string {
return []string{
config.NamedGlobFile(soongBuildTag),
- config.NamedGlobFile(bp2buildTag),
+ config.NamedGlobFile(bp2buildFilesTag),
config.NamedGlobFile(jsonModuleGraphTag),
config.NamedGlobFile(queryviewTag),
+ config.NamedGlobFile(apiBp2buildTag),
config.NamedGlobFile(soongDocsTag),
}
}
@@ -253,71 +267,140 @@
if config.EmptyNinjaFile() {
mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file")
}
-
- mainSoongBuildInvocation := primaryBuilderInvocation(
- config,
- soongBuildTag,
- config.SoongNinjaFile(),
- mainSoongBuildExtraArgs,
- fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()),
- )
-
- if config.bazelBuildMode() == mixedBuild {
- // Mixed builds call Bazel from soong_build and they therefore need the
- // Bazel workspace to be available. Make that so by adding a dependency on
- // the bp2build marker file to the action that invokes soong_build .
- mainSoongBuildInvocation.Inputs = append(mainSoongBuildInvocation.Inputs,
- config.Bp2BuildMarkerFile())
+ if config.bazelProdMode {
+ mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode")
+ }
+ if config.bazelDevMode {
+ mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-dev")
+ }
+ if config.bazelStagingMode {
+ mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-staging")
+ }
+ if config.IsPersistentBazelEnabled() {
+ mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--use-bazel-proxy")
+ }
+ if len(config.bazelForceEnabledModules) > 0 {
+ mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-force-enabled-modules="+config.bazelForceEnabledModules)
+ }
+ if config.MultitreeBuild() {
+ mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--multitree-build")
+ }
+ if config.buildFromTextStub {
+ mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--build-from-text-stub")
}
- bp2buildInvocation := primaryBuilderInvocation(
- config,
- bp2buildTag,
- config.Bp2BuildMarkerFile(),
- []string{
- "--bp2build_marker", config.Bp2BuildMarkerFile(),
- },
- fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
- )
-
- jsonModuleGraphInvocation := primaryBuilderInvocation(
- config,
- jsonModuleGraphTag,
- config.ModuleGraphFile(),
- []string{
- "--module_graph_file", config.ModuleGraphFile(),
- "--module_actions_file", config.ModuleActionsFile(),
- },
- fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()),
- )
-
queryviewDir := filepath.Join(config.SoongOutDir(), "queryview")
- queryviewInvocation := primaryBuilderInvocation(
- config,
- queryviewTag,
- config.QueryviewMarkerFile(),
- []string{
- "--bazel_queryview_dir", queryviewDir,
- },
- fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
- )
+ // The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files)
+ // The final workspace will be generated in out/soong/api_bp2build
+ apiBp2buildDir := filepath.Join(config.SoongOutDir(), ".api_bp2build")
- soongDocsInvocation := primaryBuilderInvocation(
- config,
- soongDocsTag,
- config.SoongDocsHtml(),
- []string{
- "--soong_docs", config.SoongDocsHtml(),
+ pbfs := []PrimaryBuilderFactory{
+ {
+ name: soongBuildTag,
+ description: fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()),
+ config: config,
+ output: config.SoongNinjaFile(),
+ specificArgs: mainSoongBuildExtraArgs,
},
- fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
- )
+ {
+ name: bp2buildFilesTag,
+ description: fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
+ config: config,
+ output: config.Bp2BuildFilesMarkerFile(),
+ specificArgs: []string{"--bp2build_marker", config.Bp2BuildFilesMarkerFile()},
+ },
+ {
+ name: bp2buildWorkspaceTag,
+ description: "Creating Bazel symlink forest",
+ config: config,
+ output: config.Bp2BuildWorkspaceMarkerFile(),
+ specificArgs: []string{"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile()},
+ },
+ {
+ name: jsonModuleGraphTag,
+ description: fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()),
+ config: config,
+ output: config.ModuleGraphFile(),
+ specificArgs: []string{
+ "--module_graph_file", config.ModuleGraphFile(),
+ "--module_actions_file", config.ModuleActionsFile(),
+ },
+ },
+ {
+ name: queryviewTag,
+ description: fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
+ config: config,
+ output: config.QueryviewMarkerFile(),
+ specificArgs: []string{"--bazel_queryview_dir", queryviewDir},
+ },
+ {
+ name: apiBp2buildTag,
+ description: fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
+ config: config,
+ output: config.ApiBp2buildMarkerFile(),
+ specificArgs: []string{"--bazel_api_bp2build_dir", apiBp2buildDir},
+ },
+ {
+ name: soongDocsTag,
+ description: fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
+ config: config,
+ output: config.SoongDocsHtml(),
+ specificArgs: []string{"--soong_docs", config.SoongDocsHtml()},
+ },
+ }
- globFiles := []string{
- config.NamedGlobFile(soongBuildTag),
- config.NamedGlobFile(bp2buildTag),
- config.NamedGlobFile(jsonModuleGraphTag),
- config.NamedGlobFile(queryviewTag),
- config.NamedGlobFile(soongDocsTag),
+ // Figure out which invocations will be run under the debugger:
+ // * SOONG_DELVE if set specifies listening port
+ // * SOONG_DELVE_STEPS if set specifies specific invocations to be debugged, otherwise all are
+ debuggedInvocations := make(map[string]bool)
+ delvePort := os.Getenv("SOONG_DELVE")
+ if delvePort != "" {
+ if steps := os.Getenv("SOONG_DELVE_STEPS"); steps != "" {
+ var validSteps []string
+ for _, pbf := range pbfs {
+ debuggedInvocations[pbf.name] = false
+ validSteps = append(validSteps, pbf.name)
+
+ }
+ for _, step := range strings.Split(steps, ",") {
+ if _, ok := debuggedInvocations[step]; ok {
+ debuggedInvocations[step] = true
+ } else {
+ ctx.Fatalf("SOONG_DELVE_STEPS contains unknown soong_build step %s\n"+
+ "Valid steps are %v", step, validSteps)
+ }
+ }
+ } else {
+ // SOONG_DELVE_STEPS is not set, run all steps in the debugger
+ for _, pbf := range pbfs {
+ debuggedInvocations[pbf.name] = true
+ }
+ }
+ }
+
+ var invocations []bootstrap.PrimaryBuilderInvocation
+ for _, pbf := range pbfs {
+ if debuggedInvocations[pbf.name] {
+ pbf.debugPort = delvePort
+ }
+ pbi := pbf.primaryBuilderInvocation()
+ // Some invocations require adjustment:
+ switch pbf.name {
+ case soongBuildTag:
+ if config.BazelBuildEnabled() {
+ // Mixed builds call Bazel from soong_build and they therefore need the
+ // Bazel workspace to be available. Make that so by adding a dependency on
+ // the bp2build marker file to the action that invokes soong_build .
+ pbi.OrderOnlyInputs = append(pbi.OrderOnlyInputs, config.Bp2BuildWorkspaceMarkerFile())
+ }
+ case bp2buildWorkspaceTag:
+ pbi.Inputs = append(pbi.Inputs,
+ config.Bp2BuildFilesMarkerFile(),
+ filepath.Join(config.FileListDir(), "bazel.list"))
+ case bp2buildFilesTag:
+ pbi.Inputs = append(pbi.Inputs, filepath.Join(config.FileListDir(), "METADATA.list"))
+ }
+ invocations = append(invocations, pbi)
}
// The glob .ninja files are subninja'd. However, they are generated during
@@ -327,13 +410,15 @@
writeEmptyFile(ctx, globFile)
}
- var blueprintArgs bootstrap.Args
-
- blueprintArgs.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
- blueprintArgs.OutFile = shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja")
- blueprintArgs.EmptyNinjaFile = false
+ blueprintArgs := bootstrap.Args{
+ ModuleListFile: filepath.Join(config.FileListDir(), "Android.bp.list"),
+ OutFile: shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja"),
+ EmptyNinjaFile: false,
+ }
blueprintCtx := blueprint.NewContext()
+ blueprintCtx.AddIncludeTags(config.GetIncludeTags()...)
+ blueprintCtx.AddSourceRootDirs(config.GetSourceRootDirs()...)
blueprintCtx.SetIgnoreUnknownModuleTypes(true)
blueprintConfig := BlueprintConfig{
soongOutDir: config.SoongOutDir(),
@@ -341,22 +426,14 @@
outDir: config.OutDir(),
runGoTests: !config.skipSoongTests,
// If we want to debug soong_build, we need to compile it for debugging
- debugCompilation: os.Getenv("SOONG_DELVE") != "",
- subninjas: globFiles,
- primaryBuilderInvocations: []bootstrap.PrimaryBuilderInvocation{
- mainSoongBuildInvocation,
- bp2buildInvocation,
- jsonModuleGraphInvocation,
- queryviewInvocation,
- soongDocsInvocation},
+ debugCompilation: delvePort != "",
+ subninjas: bootstrapGlobFileList(config),
+ primaryBuilderInvocations: invocations,
}
- bootstrapDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
- bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja.d")
- err := deptools.WriteDepFile(bootstrapDepFile, blueprintArgs.OutFile, bootstrapDeps)
- if err != nil {
- ctx.Fatalf("Error writing depfile '%s': %s", bootstrapDepFile, err)
- }
+ // since `bootstrap.ninja` is regenerated unconditionally, we ignore the deps, i.e. little
+ // reason to write a `bootstrap.ninja.d` file
+ _ = bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
}
func checkEnvironmentFile(currentEnv *Environment, envFile string) {
@@ -380,21 +457,21 @@
// unused variables were changed?
envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
- buildMode := config.bazelBuildMode()
- integratedBp2Build := buildMode == mixedBuild
-
// This is done unconditionally, but does not take a measurable amount of time
bootstrapBlueprint(ctx, config)
soongBuildEnv := config.Environment().Copy()
soongBuildEnv.Set("TOP", os.Getenv("TOP"))
// For Bazel mixed builds.
- soongBuildEnv.Set("BAZEL_PATH", "./tools/bazel")
- soongBuildEnv.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome"))
- soongBuildEnv.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
+ soongBuildEnv.Set("BAZEL_PATH", "./build/bazel/bin/bazel")
+ // Bazel's HOME var is set to an output subdirectory which doesn't exist. This
+ // prevents Bazel from file I/O in the actual user HOME directory.
+ soongBuildEnv.Set("BAZEL_HOME", absPath(ctx, filepath.Join(config.BazelOutDir(), "bazelhome")))
+ soongBuildEnv.Set("BAZEL_OUTPUT_BASE", config.bazelOutputBase())
soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
soongBuildEnv.Set("LOG_DIR", config.LogsDir())
+ soongBuildEnv.Set("BAZEL_DEPS_FILE", absPath(ctx, filepath.Join(config.BazelOutDir(), "bazel.list")))
// For Soong bootstrapping tests
if os.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
@@ -412,8 +489,8 @@
checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongBuildTag))
- if integratedBp2Build || config.Bp2Build() {
- checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(bp2buildTag))
+ if config.BazelBuildEnabled() || config.Bp2Build() {
+ checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(bp2buildFilesTag))
}
if config.JsonModuleGraph() {
@@ -424,6 +501,10 @@
checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag))
}
+ if config.ApiBp2build() {
+ checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(apiBp2buildTag))
+ }
+
if config.SoongDocs() {
checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag))
}
@@ -436,6 +517,12 @@
ctx.BeginTrace(metrics.RunSoong, name)
defer ctx.EndTrace()
+ if config.IsPersistentBazelEnabled() {
+ bazelProxy := bazel.NewProxyServer(ctx.Logger, config.OutDir(), filepath.Join(config.SoongOutDir(), "workspace"))
+ bazelProxy.Start()
+ defer bazelProxy.Close()
+ }
+
fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
defer nr.Close()
@@ -453,6 +540,11 @@
"-f", filepath.Join(config.SoongOutDir(), ninjaFile),
}
+ if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok {
+ ctx.Printf(`CAUTION: arguments in $SOONG_UI_NINJA_ARGS=%q, e.g. "-n", can make soong_build FAIL or INCORRECT`, extra)
+ ninjaArgs = append(ninjaArgs, strings.Fields(extra)...)
+ }
+
ninjaArgs = append(ninjaArgs, targets...)
cmd := Command(ctx, config, "soong "+name,
config.PrebuiltBuildTool("ninja"), ninjaArgs...)
@@ -475,13 +567,17 @@
}
if config.Bp2Build() {
- targets = append(targets, config.Bp2BuildMarkerFile())
+ targets = append(targets, config.Bp2BuildWorkspaceMarkerFile())
}
if config.Queryview() {
targets = append(targets, config.QueryviewMarkerFile())
}
+ if config.ApiBp2build() {
+ targets = append(targets, config.ApiBp2buildMarkerFile())
+ }
+
if config.SoongDocs() {
targets = append(targets, config.SoongDocsHtml())
}
@@ -493,17 +589,8 @@
ninja("bootstrap", "bootstrap.ninja", targets...)
- if shouldCollectBuildSoongMetrics(config) {
- soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
- if soongBuildMetrics != nil {
- logSoongBuildMetrics(ctx, soongBuildMetrics)
- if ctx.Metrics != nil {
- ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
- }
- }
- }
-
distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
+ distFile(ctx, config, config.SoongVarsFile(), "soong")
if !config.SkipKati() {
distGzipFile(ctx, config, config.SoongAndroidMk(), "soong")
@@ -532,37 +619,3 @@
ctx.Fatalf("failed to build %s: %s", name, err)
}
}
-
-func shouldCollectBuildSoongMetrics(config Config) bool {
- // Do not collect metrics protobuf if the soong_build binary ran as the
- // bp2build converter or the JSON graph dump.
- return config.SoongBuildInvocationNeeded()
-}
-
-func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {
- 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{}
- err = proto.Unmarshal(buf, soongBuildMetrics)
- if err != nil {
- ctx.Fatalf("Failed to unmarshal %s: %s", soongBuildMetricsFile, err)
- }
- return soongBuildMetrics
-}
-
-func logSoongBuildMetrics(ctx Context, metrics *soong_metrics_proto.SoongBuildMetrics) {
- ctx.Verbosef("soong_build metrics:")
- ctx.Verbosef(" modules: %v", metrics.GetModules())
- ctx.Verbosef(" variants: %v", metrics.GetVariants())
- ctx.Verbosef(" max heap size: %v MB", metrics.GetMaxHeapSize()/1e6)
- ctx.Verbosef(" total allocation count: %v", metrics.GetTotalAllocCount())
- ctx.Verbosef(" total allocation size: %v MB", metrics.GetTotalAllocSize()/1e6)
-
-}
diff --git a/ui/build/staging_snapshot.go b/ui/build/staging_snapshot.go
new file mode 100644
index 0000000..377aa64
--- /dev/null
+++ b/ui/build/staging_snapshot.go
@@ -0,0 +1,246 @@
+// Copyright 2023 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 build
+
+import (
+ "crypto/sha1"
+ "encoding/hex"
+ "encoding/json"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "android/soong/shared"
+ "android/soong/ui/metrics"
+)
+
+// Metadata about a staged file
+type fileEntry struct {
+ Name string `json:"name"`
+ Mode fs.FileMode `json:"mode"`
+ Size int64 `json:"size"`
+ Sha1 string `json:"sha1"`
+}
+
+func fileEntryEqual(a fileEntry, b fileEntry) bool {
+ return a.Name == b.Name && a.Mode == b.Mode && a.Size == b.Size && a.Sha1 == b.Sha1
+}
+
+func sha1_hash(filename string) (string, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+
+ h := sha1.New()
+ if _, err := io.Copy(h, f); err != nil {
+ return "", err
+ }
+
+ return hex.EncodeToString(h.Sum(nil)), nil
+}
+
+// Subdirs of PRODUCT_OUT to scan
+var stagingSubdirs = []string{
+ "apex",
+ "cache",
+ "coverage",
+ "data",
+ "debug_ramdisk",
+ "fake_packages",
+ "installer",
+ "oem",
+ "product",
+ "ramdisk",
+ "recovery",
+ "root",
+ "sysloader",
+ "system",
+ "system_dlkm",
+ "system_ext",
+ "system_other",
+ "testcases",
+ "test_harness_ramdisk",
+ "vendor",
+ "vendor_debug_ramdisk",
+ "vendor_kernel_ramdisk",
+ "vendor_ramdisk",
+}
+
+// Return an array of stagedFileEntrys, one for each file in the staging directories inside
+// productOut
+func takeStagingSnapshot(ctx Context, productOut string, subdirs []string) ([]fileEntry, error) {
+ var outer_err error
+ if !strings.HasSuffix(productOut, "/") {
+ productOut += "/"
+ }
+ result := []fileEntry{}
+ for _, subdir := range subdirs {
+ filepath.WalkDir(productOut+subdir,
+ func(filename string, dirent fs.DirEntry, err error) error {
+ // Ignore errors. The most common one is that one of the subdirectories
+ // hasn't been built, in which case we just report it as empty.
+ if err != nil {
+ ctx.Verbosef("scanModifiedStagingOutputs error: %s", err)
+ return nil
+ }
+ if dirent.Type().IsRegular() {
+ fileInfo, _ := dirent.Info()
+ relative := strings.TrimPrefix(filename, productOut)
+ sha, err := sha1_hash(filename)
+ if err != nil {
+ outer_err = err
+ }
+ result = append(result, fileEntry{
+ Name: relative,
+ Mode: fileInfo.Mode(),
+ Size: fileInfo.Size(),
+ Sha1: sha,
+ })
+ }
+ return nil
+ })
+ }
+
+ sort.Slice(result, func(l, r int) bool { return result[l].Name < result[r].Name })
+
+ return result, outer_err
+}
+
+// Read json into an array of fileEntry. On error return empty array.
+func readJson(filename string) ([]fileEntry, error) {
+ buf, err := os.ReadFile(filename)
+ if err != nil {
+ // Not an error, just missing, which is empty.
+ return []fileEntry{}, nil
+ }
+
+ var result []fileEntry
+ err = json.Unmarshal(buf, &result)
+ if err != nil {
+ // Bad formatting. This is an error
+ return []fileEntry{}, err
+ }
+
+ return result, nil
+}
+
+// Write obj to filename.
+func writeJson(filename string, obj interface{}) error {
+ buf, err := json.MarshalIndent(obj, "", " ")
+ if err != nil {
+ return err
+ }
+
+ return os.WriteFile(filename, buf, 0660)
+}
+
+type snapshotDiff struct {
+ Added []string `json:"added"`
+ Changed []string `json:"changed"`
+ Removed []string `json:"removed"`
+}
+
+// Diff the two snapshots, returning a snapshotDiff.
+func diffSnapshots(previous []fileEntry, current []fileEntry) snapshotDiff {
+ result := snapshotDiff{
+ Added: []string{},
+ Changed: []string{},
+ Removed: []string{},
+ }
+
+ found := make(map[string]bool)
+
+ prev := make(map[string]fileEntry)
+ for _, pre := range previous {
+ prev[pre.Name] = pre
+ }
+
+ for _, cur := range current {
+ pre, ok := prev[cur.Name]
+ found[cur.Name] = true
+ // Added
+ if !ok {
+ result.Added = append(result.Added, cur.Name)
+ continue
+ }
+ // Changed
+ if !fileEntryEqual(pre, cur) {
+ result.Changed = append(result.Changed, cur.Name)
+ }
+ }
+
+ // Removed
+ for _, pre := range previous {
+ if !found[pre.Name] {
+ result.Removed = append(result.Removed, pre.Name)
+ }
+ }
+
+ // Sort the results
+ sort.Strings(result.Added)
+ sort.Strings(result.Changed)
+ sort.Strings(result.Removed)
+
+ return result
+}
+
+// Write a json files to dist:
+// - A list of which files have changed in this build.
+//
+// And record in out/soong:
+// - A list of all files in the staging directories, including their hashes.
+func runStagingSnapshot(ctx Context, config Config) {
+ ctx.BeginTrace(metrics.RunSoong, "runStagingSnapshot")
+ defer ctx.EndTrace()
+
+ snapshotFilename := shared.JoinPath(config.SoongOutDir(), "staged_files.json")
+
+ // Read the existing snapshot file. If it doesn't exist, this is a full
+ // build, so all files will be treated as new.
+ previous, err := readJson(snapshotFilename)
+ if err != nil {
+ ctx.Fatal(err)
+ return
+ }
+
+ // Take a snapshot of the current out directory
+ current, err := takeStagingSnapshot(ctx, config.ProductOut(), stagingSubdirs)
+ if err != nil {
+ ctx.Fatal(err)
+ return
+ }
+
+ // Diff the snapshots
+ diff := diffSnapshots(previous, current)
+
+ // Write the diff (use RealDistDir, not one that might have been faked for bazel)
+ err = writeJson(shared.JoinPath(config.RealDistDir(), "modified_files.json"), diff)
+ if err != nil {
+ ctx.Fatal(err)
+ return
+ }
+
+ // Update the snapshot
+ err = writeJson(snapshotFilename, current)
+ if err != nil {
+ ctx.Fatal(err)
+ return
+ }
+}
diff --git a/ui/build/staging_snapshot_test.go b/ui/build/staging_snapshot_test.go
new file mode 100644
index 0000000..7ac5443
--- /dev/null
+++ b/ui/build/staging_snapshot_test.go
@@ -0,0 +1,188 @@
+// Copyright 2023 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 build
+
+import (
+ "os"
+ "path/filepath"
+ "reflect"
+ "testing"
+)
+
+func assertDeepEqual(t *testing.T, expected interface{}, actual interface{}) {
+ if !reflect.DeepEqual(actual, expected) {
+ t.Fatalf("expected:\n %#v\n actual:\n %#v", expected, actual)
+ }
+}
+
+// Make a temp directory containing the supplied contents
+func makeTempDir(files []string, directories []string, symlinks []string) string {
+ temp, _ := os.MkdirTemp("", "soon_staging_snapshot_test_")
+
+ for _, file := range files {
+ os.MkdirAll(temp+"/"+filepath.Dir(file), 0700)
+ os.WriteFile(temp+"/"+file, []byte(file), 0600)
+ }
+
+ for _, dir := range directories {
+ os.MkdirAll(temp+"/"+dir, 0770)
+ }
+
+ for _, symlink := range symlinks {
+ os.MkdirAll(temp+"/"+filepath.Dir(symlink), 0770)
+ os.Symlink(temp, temp+"/"+symlink)
+ }
+
+ return temp
+}
+
+// If this is a clean build, we won't have any preexisting files, make sure we get back an empty
+// list and not errors.
+func TestEmptyOut(t *testing.T) {
+ ctx := testContext()
+
+ temp := makeTempDir(nil, nil, nil)
+ defer os.RemoveAll(temp)
+
+ actual, _ := takeStagingSnapshot(ctx, temp, []string{"a", "e", "g"})
+
+ expected := []fileEntry{}
+
+ assertDeepEqual(t, expected, actual)
+}
+
+// Make sure only the listed directories are picked up, and only regular files
+func TestNoExtraSubdirs(t *testing.T) {
+ ctx := testContext()
+
+ temp := makeTempDir([]string{"a/b", "a/c", "d", "e/f"}, []string{"g/h"}, []string{"e/symlink"})
+ defer os.RemoveAll(temp)
+
+ actual, _ := takeStagingSnapshot(ctx, temp, []string{"a", "e", "g"})
+
+ expected := []fileEntry{
+ {"a/b", 0600, 3, "3ec69c85a4ff96830024afeef2d4e512181c8f7b"},
+ {"a/c", 0600, 3, "592d70e4e03ee6f6780c71b0bf3b9608dbf1e201"},
+ {"e/f", 0600, 3, "9e164bef74aceede0974b857170100409efe67f1"},
+ }
+
+ assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles empty lists
+func TestDiffEmpty(t *testing.T) {
+ actual := diffSnapshots(nil, []fileEntry{})
+
+ expected := snapshotDiff{
+ Added: []string{},
+ Changed: []string{},
+ Removed: []string{},
+ }
+
+ assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles adding
+func TestDiffAdd(t *testing.T) {
+ actual := diffSnapshots([]fileEntry{
+ {"a", 0600, 1, "1234"},
+ }, []fileEntry{
+ {"a", 0600, 1, "1234"},
+ {"b", 0700, 2, "5678"},
+ })
+
+ expected := snapshotDiff{
+ Added: []string{"b"},
+ Changed: []string{},
+ Removed: []string{},
+ }
+
+ assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles changing mode
+func TestDiffChangeMode(t *testing.T) {
+ actual := diffSnapshots([]fileEntry{
+ {"a", 0600, 1, "1234"},
+ {"b", 0700, 2, "5678"},
+ }, []fileEntry{
+ {"a", 0600, 1, "1234"},
+ {"b", 0600, 2, "5678"},
+ })
+
+ expected := snapshotDiff{
+ Added: []string{},
+ Changed: []string{"b"},
+ Removed: []string{},
+ }
+
+ assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles changing size
+func TestDiffChangeSize(t *testing.T) {
+ actual := diffSnapshots([]fileEntry{
+ {"a", 0600, 1, "1234"},
+ {"b", 0700, 2, "5678"},
+ }, []fileEntry{
+ {"a", 0600, 1, "1234"},
+ {"b", 0700, 3, "5678"},
+ })
+
+ expected := snapshotDiff{
+ Added: []string{},
+ Changed: []string{"b"},
+ Removed: []string{},
+ }
+
+ assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles changing contents
+func TestDiffChangeContents(t *testing.T) {
+ actual := diffSnapshots([]fileEntry{
+ {"a", 0600, 1, "1234"},
+ {"b", 0700, 2, "5678"},
+ }, []fileEntry{
+ {"a", 0600, 1, "1234"},
+ {"b", 0700, 2, "aaaa"},
+ })
+
+ expected := snapshotDiff{
+ Added: []string{},
+ Changed: []string{"b"},
+ Removed: []string{},
+ }
+
+ assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles removing
+func TestDiffRemove(t *testing.T) {
+ actual := diffSnapshots([]fileEntry{
+ {"a", 0600, 1, "1234"},
+ {"b", 0700, 2, "5678"},
+ }, []fileEntry{
+ {"a", 0600, 1, "1234"},
+ })
+
+ expected := snapshotDiff{
+ Added: []string{},
+ Changed: []string{},
+ Removed: []string{"b"},
+ }
+
+ assertDeepEqual(t, expected, actual)
+}
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 86c8568..2efc732 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -18,14 +18,44 @@
"bufio"
"fmt"
"path/filepath"
+ "regexp"
"runtime"
"sort"
"strings"
+ "sync"
"android/soong/ui/metrics"
"android/soong/ui/status"
)
+var (
+ // bazel output paths are in __main__/bazel-out/<config-specific-path>/bin
+ bazelOutputPathRegexOnce sync.Once
+ bazelOutputPathRegexp *regexp.Regexp
+)
+
+func bazelOutputPathPattern(config Config) *regexp.Regexp {
+ bazelOutputPathRegexOnce.Do(func() {
+ // Bazel output files are in <Bazel output base>/execroot/__main__/bazel-out/<config>/bin
+ bazelOutRoot := filepath.Join(regexp.QuoteMeta(config.bazelOutputBase()), "execroot", "__main__", "bazel-out")
+ bazelOutputPathRegexp = regexp.MustCompile(bazelOutRoot + "/[^/]+/bin")
+ })
+ return bazelOutputPathRegexp
+}
+
+func ignoreBazelPath(config Config, path string) bool {
+ bazelRoot := filepath.Join(config.bazelOutputBase(), "execroot")
+ // Don't check bazel output regexp unless it is Bazel path
+ if strings.HasPrefix(path, bazelRoot) {
+ bazelOutputRegexp := bazelOutputPathPattern(config)
+ // if the file is a bazel path that is _not_ a Bazel generated file output, we rely on Bazel to
+ // ensure the paths to exist. If it _is_ a Bazel output path, we expect that it should be built
+ // by Ninja.
+ return !bazelOutputRegexp.MatchString(path)
+ }
+ return false
+}
+
// Checks for files in the out directory that have a rule that depends on them but no rule to
// create them. This catches a common set of build failures where a rule to generate a file is
// deleted (either by deleting a module in an Android.mk file, or by modifying the build system
@@ -97,6 +127,10 @@
// full build rules in the primary build.ninja file.
continue
}
+
+ if ignoreBazelPath(config, line) {
+ continue
+ }
danglingRules[line] = true
}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 687f519..9959e6f 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -18,15 +18,21 @@
// another.
import (
+ "bufio"
+ "fmt"
"io/ioutil"
"os"
"path/filepath"
+ "strconv"
+ "strings"
"time"
+ "android/soong/shared"
"android/soong/ui/metrics"
"google.golang.org/protobuf/proto"
+ bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
upload_proto "android/soong/ui/metrics/upload_proto"
)
@@ -56,7 +62,9 @@
}
if fi.IsDir() {
- if l, err := ioutil.ReadDir(p); err == nil {
+ if l, err := ioutil.ReadDir(p); err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Failed to find files under %s\n", p)
+ } else {
files := make([]string, 0, len(l))
for _, fi := range l {
files = append(files, filepath.Join(p, fi.Name()))
@@ -70,12 +78,113 @@
return metricsFiles
}
+func parseTimingToNanos(str string) int64 {
+ millisString := removeDecimalPoint(str)
+ timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
+ return timingMillis * 1000000
+}
+
+func parsePercentageToTenThousandths(str string) int32 {
+ percentageString := removeDecimalPoint(str)
+ //remove the % at the end of the string
+ percentage := strings.ReplaceAll(percentageString, "%", "")
+ percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
+ return int32(percentagePortion)
+}
+
+func removeDecimalPoint(numString string) string {
+ // The format is always 0.425 or 10.425
+ return strings.ReplaceAll(numString, ".", "")
+}
+
+func parseTotal(line string) int64 {
+ words := strings.Fields(line)
+ timing := words[3]
+ return parseTimingToNanos(timing)
+}
+
+func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
+ words := strings.Fields(line)
+ getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
+ // Sample lines include:
+ // Total launch phase time 0.011 s 2.59%
+ // Total target pattern evaluation phase time 0.011 s 2.59%
+ var beginning int
+ var end int
+ for ind, word := range words {
+ if word == "Total" {
+ beginning = ind + 1
+ } else if beginning > 0 && word == "phase" {
+ end = ind
+ break
+ }
+ }
+ phaseName := strings.Join(words[beginning:end], " ")
+
+ // end is now "phase" - advance by 2 for timing and 4 for percentage
+ percentageString := words[end+4]
+ timingString := words[end+2]
+ timing := parseTimingToNanos(timingString)
+ percentagePortion := parsePercentageToTenThousandths(percentageString)
+ return phaseName, timing, percentagePortion
+ }
+
+ phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
+ phaseTiming := bazel_metrics_proto.PhaseTiming{}
+ phaseTiming.DurationNanos = &timing
+ phaseTiming.PortionOfBuildTime = &portion
+
+ phaseTiming.PhaseName = &phaseName
+ return phaseTiming
+}
+
+func processBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context) {
+ if bazelProfileFile == "" {
+ return
+ }
+
+ readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
+ //serialize the proto, write it
+ bazelMetrics := bazel_metrics_proto.BazelMetrics{}
+
+ file, err := os.ReadFile(filepath)
+ if err != nil {
+ ctx.Fatalln("Error reading metrics file\n", err)
+ }
+
+ scanner := bufio.NewScanner(strings.NewReader(string(file)))
+ scanner.Split(bufio.ScanLines)
+
+ var phaseTimings []*bazel_metrics_proto.PhaseTiming
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "Total run time") {
+ total := parseTotal(line)
+ bazelMetrics.Total = &total
+ } else if strings.HasPrefix(line, "Total") {
+ phaseTiming := parsePhaseTiming(line)
+ phaseTimings = append(phaseTimings, &phaseTiming)
+ }
+ }
+ bazelMetrics.PhaseTimings = phaseTimings
+
+ return bazelMetrics
+ }
+
+ if _, err := os.Stat(bazelProfileFile); err != nil {
+ // We can assume bazel didn't run if the profile doesn't exist
+ return
+ }
+ bazelProto := readBazelProto(bazelProfileFile)
+ shared.Save(&bazelProto, bazelMetricsFile)
+}
+
// UploadMetrics uploads a set of metrics files to a server for analysis.
// The metrics files are first copied to a temporary directory
// and the uploader is then executed in the background to allow the user/system
// to continue working. Soong communicates to the uploader through the
// upload_proto raw protobuf file.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, paths ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, bazelProfileFile string, bazelMetricsFile string, paths ...string) {
ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
defer ctx.EndTrace()
@@ -85,6 +194,7 @@
return
}
+ processBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx)
// Several of the files might be directories.
metricsFiles := pruneMetricsFiles(paths)
if len(metricsFiles) == 0 {
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index 764a1e1..58d9237 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -29,6 +29,30 @@
"android/soong/ui/logger"
)
+func writeBazelProfileFile(dir string) error {
+ contents := `
+
+=== PHASE SUMMARY INFORMATION ===
+
+Total launch phase time 1.193 s 15.77%
+Total init phase time 1.092 s 14.44%
+Total target pattern evaluation phase time 0.580 s 7.67%
+Total interleaved loading-and-analysis phase time 3.646 s 48.21%
+Total preparation phase time 0.022 s 0.30%
+Total execution phase time 0.993 s 13.13%
+Total finish phase time 0.036 s 0.48%
+---------------------------------------------------------------------
+Total run time 7.563 s 100.00%
+
+Critical path (178 ms):
+ Time Percentage Description
+ 178 ms 100.00% action 'BazelWorkspaceStatusAction stable-status.txt'
+
+`
+ file := filepath.Join(dir, "bazel_metrics.txt")
+ return os.WriteFile(file, []byte(contents), 0666)
+}
+
func TestPruneMetricsFiles(t *testing.T) {
rootDir := t.TempDir()
@@ -84,12 +108,12 @@
}, {
description: "non-existent metrics files no upload",
uploader: "echo",
- files: []string{"metrics_file_1", "metrics_file_2", "metrics_file_3"},
+ files: []string{"metrics_file_1", "metrics_file_2", "metrics_file_3, bazel_metrics.pb"},
}, {
description: "trigger upload",
uploader: "echo",
createFiles: true,
- files: []string{"metrics_file_1", "metrics_file_2"},
+ files: []string{"metrics_file_1", "metrics_file_2, bazel_metrics.pb"},
}}
for _, tt := range tests {
@@ -130,6 +154,9 @@
}
}
}
+ if err := writeBazelProfileFile(outDir); err != nil {
+ t.Fatalf("failed to create bazel profile file in dir: %v", outDir)
+ }
config := Config{&configImpl{
environ: &Environment{
@@ -139,7 +166,7 @@
metricsUploader: tt.uploader,
}}
- UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
+ UploadMetrics(ctx, config, false, time.Now(), "out/bazel_metrics.txt", "out/bazel_metrics.pb", metricsFiles...)
})
}
}
@@ -194,8 +221,79 @@
metricsUploader: "echo",
}}
- UploadMetrics(ctx, config, true, time.Now(), metricsFile)
+ UploadMetrics(ctx, config, true, time.Now(), "", "", metricsFile)
t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
})
}
}
+
+func TestParsePercentageToTenThousandths(t *testing.T) {
+ // 2.59% should be returned as 259 - representing 259/10000 of the build
+ percentage := parsePercentageToTenThousandths("2.59%")
+ if percentage != 259 {
+ t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 259, have %d\n", percentage)
+ }
+
+ // Test without a leading digit
+ percentage = parsePercentageToTenThousandths(".52%")
+ if percentage != 52 {
+ t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 52, have %d\n", percentage)
+ }
+}
+
+func TestParseTimingToNanos(t *testing.T) {
+ // This parses from seconds (with millis precision) and returns nanos
+ timingNanos := parseTimingToNanos("0.111")
+ if timingNanos != 111000000 {
+ t.Errorf("Error parsing timing. Expected 111000, have %d\n", timingNanos)
+ }
+
+ // Test without a leading digit
+ timingNanos = parseTimingToNanos(".112")
+ if timingNanos != 112000000 {
+ t.Errorf("Error parsing timing. Expected 112000, have %d\n", timingNanos)
+ }
+}
+
+func TestParsePhaseTiming(t *testing.T) {
+ // Sample lines include:
+ // Total launch phase time 0.011 s 2.59%
+ // Total target pattern evaluation phase time 0.012 s 4.59%
+
+ line1 := "Total launch phase time 0.011 s 2.59%"
+ timing := parsePhaseTiming(line1)
+
+ if timing.GetPhaseName() != "launch" {
+ t.Errorf("Failed to parse phase name. Expected launch, have %s\n", timing.GetPhaseName())
+ } else if timing.GetDurationNanos() != 11000000 {
+ t.Errorf("Failed to parse duration nanos. Expected 11000000, have %d\n", timing.GetDurationNanos())
+ } else if timing.GetPortionOfBuildTime() != 259 {
+ t.Errorf("Failed to parse portion of build time. Expected 259, have %d\n", timing.GetPortionOfBuildTime())
+ }
+
+ // Test with a multiword phase name
+ line2 := "Total target pattern evaluation phase time 0.012 s 4.59%"
+
+ timing = parsePhaseTiming(line2)
+ if timing.GetPhaseName() != "target pattern evaluation" {
+ t.Errorf("Failed to parse phase name. Expected target pattern evaluation, have %s\n", timing.GetPhaseName())
+ } else if timing.GetDurationNanos() != 12000000 {
+ t.Errorf("Failed to parse duration nanos. Expected 12000000, have %d\n", timing.GetDurationNanos())
+ } else if timing.GetPortionOfBuildTime() != 459 {
+ t.Errorf("Failed to parse portion of build time. Expected 459, have %d\n", timing.GetPortionOfBuildTime())
+ }
+}
+
+func TestParseTotal(t *testing.T) {
+ // Total line is in the form of:
+ // Total run time 7.563 s 100.00%
+
+ line := "Total run time 7.563 s 100.00%"
+
+ total := parseTotal(line)
+
+ // Only the seconds field is parsed, as nanos
+ if total != 7563000000 {
+ t.Errorf("Failed to parse total build time. Expected 7563000000, have %d\n", total)
+ }
+}
diff --git a/ui/logger/Android.bp b/ui/logger/Android.bp
index 269a5a0..7883ea6 100644
--- a/ui/logger/Android.bp
+++ b/ui/logger/Android.bp
@@ -25,4 +25,7 @@
testSrcs: [
"logger_test.go",
],
+ deps: [
+ "soong-ui-metrics",
+ ],
}
diff --git a/ui/logger/logger.go b/ui/logger/logger.go
index 9b26ae8..1185298 100644
--- a/ui/logger/logger.go
+++ b/ui/logger/logger.go
@@ -29,6 +29,7 @@
package logger
import (
+ "android/soong/ui/metrics"
"errors"
"fmt"
"io"
@@ -72,8 +73,8 @@
Output(calldepth int, str string) error
}
-// fatalLog is the type used when Fatal[f|ln]
-type fatalLog struct {
+// fatalError is the type used when Fatal[f|ln]
+type fatalError struct {
error
}
@@ -127,7 +128,7 @@
if p == nil {
return
- } else if log, ok := p.(fatalLog); ok {
+ } else if log, ok := p.(fatalError); ok {
fn(error(log))
} else {
panic(p)
@@ -141,6 +142,7 @@
fileLogger *log.Logger
mutex sync.Mutex
file *os.File
+ metrics *metrics.Metrics
}
var _ Logger = &stdLogger{}
@@ -149,9 +151,14 @@
// os.Stderr, but it may be a buffer for tests, or a separate log file if
// the user doesn't need to see the output.
func New(out io.Writer) *stdLogger {
+ return NewWithMetrics(out, nil)
+}
+
+func NewWithMetrics(out io.Writer, m *metrics.Metrics) *stdLogger {
return &stdLogger{
stderr: log.New(out, "", log.Ltime),
fileLogger: log.New(ioutil.Discard, "", log.Ldate|log.Lmicroseconds|log.Llongfile),
+ metrics: m,
}
}
@@ -201,7 +208,7 @@
fatal := false
p := recover()
- if _, ok := p.(fatalLog); ok {
+ if _, ok := p.(fatalError); ok {
fatal = true
p = nil
} else if p != nil {
@@ -217,40 +224,56 @@
}
}
+type verbosityLevel int
+
+const (
+ verboseLog verbosityLevel = iota
+ infoLog
+ fatalLog
+ panicLog
+)
+
// Output writes string to both stderr and the file log.
func (s *stdLogger) Output(calldepth int, str string) error {
- s.stderr.Output(calldepth+1, str)
+ return s.output(calldepth, str, infoLog)
+}
+
+// output writes string to stderr, the file log, and if fatal or panic, to metrics.
+func (s *stdLogger) output(calldepth int, str string, level verbosityLevel) error {
+ if level != verboseLog || s.verbose {
+ s.stderr.Output(calldepth+1, str)
+ }
+ if level >= fatalLog {
+ s.metrics.SetFatalOrPanicMessage(str)
+ }
return s.fileLogger.Output(calldepth+1, str)
}
// VerboseOutput is equivalent to Output, but only goes to the file log
// unless SetVerbose(true) has been called.
func (s *stdLogger) VerboseOutput(calldepth int, str string) error {
- if s.verbose {
- s.stderr.Output(calldepth+1, str)
- }
- return s.fileLogger.Output(calldepth+1, str)
+ return s.output(calldepth, str, verboseLog)
}
// Print prints to both stderr and the file log.
// Arguments are handled in the manner of fmt.Print.
func (s *stdLogger) Print(v ...interface{}) {
output := fmt.Sprint(v...)
- s.Output(2, output)
+ s.output(2, output, infoLog)
}
// Printf prints to both stderr and the file log.
// Arguments are handled in the manner of fmt.Printf.
func (s *stdLogger) Printf(format string, v ...interface{}) {
output := fmt.Sprintf(format, v...)
- s.Output(2, output)
+ s.output(2, output, infoLog)
}
// Println prints to both stderr and the file log.
// Arguments are handled in the manner of fmt.Println.
func (s *stdLogger) Println(v ...interface{}) {
output := fmt.Sprintln(v...)
- s.Output(2, output)
+ s.output(2, output, infoLog)
}
// Verbose is equivalent to Print, but only goes to the file log unless
@@ -278,43 +301,43 @@
// Cleanup will convert to a os.Exit(1).
func (s *stdLogger) Fatal(v ...interface{}) {
output := fmt.Sprint(v...)
- s.Output(2, output)
- panic(fatalLog{errors.New(output)})
+ s.output(2, output, fatalLog)
+ panic(fatalError{errors.New(output)})
}
// Fatalf is equivalent to Printf() followed by a call to panic() that
// Cleanup will convert to a os.Exit(1).
func (s *stdLogger) Fatalf(format string, v ...interface{}) {
output := fmt.Sprintf(format, v...)
- s.Output(2, output)
- panic(fatalLog{errors.New(output)})
+ s.output(2, output, fatalLog)
+ panic(fatalError{errors.New(output)})
}
// Fatalln is equivalent to Println() followed by a call to panic() that
// Cleanup will convert to a os.Exit(1).
func (s *stdLogger) Fatalln(v ...interface{}) {
output := fmt.Sprintln(v...)
- s.Output(2, output)
- panic(fatalLog{errors.New(output)})
+ s.output(2, output, fatalLog)
+ panic(fatalError{errors.New(output)})
}
// Panic is equivalent to Print() followed by a call to panic().
func (s *stdLogger) Panic(v ...interface{}) {
output := fmt.Sprint(v...)
- s.Output(2, output)
+ s.output(2, output, panicLog)
panic(output)
}
// Panicf is equivalent to Printf() followed by a call to panic().
func (s *stdLogger) Panicf(format string, v ...interface{}) {
output := fmt.Sprintf(format, v...)
- s.Output(2, output)
+ s.output(2, output, panicLog)
panic(output)
}
// Panicln is equivalent to Println() followed by a call to panic().
func (s *stdLogger) Panicln(v ...interface{}) {
output := fmt.Sprintln(v...)
- s.Output(2, output)
+ s.output(2, output, panicLog)
panic(output)
}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 05db1d7..bd1517c 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -22,10 +22,10 @@
deps: [
"golang-protobuf-proto",
"soong-ui-bp2build_metrics_proto",
+ "soong-ui-bazel_metrics_proto",
"soong-ui-metrics_upload_proto",
"soong-ui-metrics_proto",
"soong-ui-mk_metrics_proto",
- "soong-ui-tracer",
"soong-shared",
],
srcs: [
@@ -74,6 +74,18 @@
}
bootstrap_go_package {
+ name: "soong-ui-bazel_metrics_proto",
+ pkgPath: "android/soong/ui/metrics/bazel_metrics_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "bazel_metrics_proto/bazel_metrics.pb.go",
+ ],
+}
+
+bootstrap_go_package {
name: "soong-ui-mk_metrics_proto",
pkgPath: "android/soong/ui/metrics/mk_metrics_proto",
deps: [
diff --git a/ui/metrics/BUILD.bazel b/ui/metrics/BUILD.bazel
new file mode 100644
index 0000000..15ebb88
--- /dev/null
+++ b/ui/metrics/BUILD.bazel
@@ -0,0 +1,30 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load("//build/bazel/rules/python:py_proto.bzl", "py_proto_library")
+
+py_proto_library(
+ name = "metrics-py-proto",
+ visibility = ["//build/bazel/scripts/incremental_build:__pkg__"],
+ deps = [":metrics-proto"],
+)
+
+proto_library(
+ name = "metrics-proto",
+ srcs = [
+ "bp2build_metrics_proto/bp2build_metrics.proto",
+ "metrics_proto/metrics.proto",
+ ],
+ strip_import_prefix = "",
+)
diff --git a/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go b/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go
new file mode 100644
index 0000000..f8b8fd6
--- /dev/null
+++ b/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go
@@ -0,0 +1,268 @@
+// 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.28.0
+// protoc v3.21.7
+// source: bazel_metrics.proto
+
+package bazel_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)
+)
+
+type BazelMetrics struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ PhaseTimings []*PhaseTiming `protobuf:"bytes,1,rep,name=phase_timings,json=phaseTimings,proto3" json:"phase_timings,omitempty"`
+ Total *int64 `protobuf:"varint,2,opt,name=total,proto3,oneof" json:"total,omitempty"`
+}
+
+func (x *BazelMetrics) Reset() {
+ *x = BazelMetrics{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_bazel_metrics_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BazelMetrics) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BazelMetrics) ProtoMessage() {}
+
+func (x *BazelMetrics) ProtoReflect() protoreflect.Message {
+ mi := &file_bazel_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 BazelMetrics.ProtoReflect.Descriptor instead.
+func (*BazelMetrics) Descriptor() ([]byte, []int) {
+ return file_bazel_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *BazelMetrics) GetPhaseTimings() []*PhaseTiming {
+ if x != nil {
+ return x.PhaseTimings
+ }
+ return nil
+}
+
+func (x *BazelMetrics) GetTotal() int64 {
+ if x != nil && x.Total != nil {
+ return *x.Total
+ }
+ return 0
+}
+
+type PhaseTiming struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // E.g. "execution", "analysis", "launch"
+ PhaseName *string `protobuf:"bytes,1,opt,name=phase_name,json=phaseName,proto3,oneof" json:"phase_name,omitempty"`
+ DurationNanos *int64 `protobuf:"varint,2,opt,name=duration_nanos,json=durationNanos,proto3,oneof" json:"duration_nanos,omitempty"`
+ // What portion of the build time this phase took, with ten-thousandths precision.
+ // E.g., 1111 = 11.11%, 111 = 1.11%
+ PortionOfBuildTime *int32 `protobuf:"varint,3,opt,name=portion_of_build_time,json=portionOfBuildTime,proto3,oneof" json:"portion_of_build_time,omitempty"`
+}
+
+func (x *PhaseTiming) Reset() {
+ *x = PhaseTiming{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_bazel_metrics_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PhaseTiming) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PhaseTiming) ProtoMessage() {}
+
+func (x *PhaseTiming) ProtoReflect() protoreflect.Message {
+ mi := &file_bazel_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 PhaseTiming.ProtoReflect.Descriptor instead.
+func (*PhaseTiming) Descriptor() ([]byte, []int) {
+ return file_bazel_metrics_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *PhaseTiming) GetPhaseName() string {
+ if x != nil && x.PhaseName != nil {
+ return *x.PhaseName
+ }
+ return ""
+}
+
+func (x *PhaseTiming) GetDurationNanos() int64 {
+ if x != nil && x.DurationNanos != nil {
+ return *x.DurationNanos
+ }
+ return 0
+}
+
+func (x *PhaseTiming) GetPortionOfBuildTime() int32 {
+ if x != nil && x.PortionOfBuildTime != nil {
+ return *x.PortionOfBuildTime
+ }
+ return 0
+}
+
+var File_bazel_metrics_proto protoreflect.FileDescriptor
+
+var file_bazel_metrics_proto_rawDesc = []byte{
+ 0x0a, 0x13, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+ 0x6c, 0x64, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+ 0x22, 0x80, 0x01, 0x0a, 0x0c, 0x42, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+ 0x73, 0x12, 0x4b, 0x0a, 0x0d, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x69, 0x6e,
+ 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+ 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67,
+ 0x52, 0x0c, 0x70, 0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19,
+ 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52,
+ 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, 0x6f,
+ 0x74, 0x61, 0x6c, 0x22, 0xd1, 0x01, 0x0a, 0x0b, 0x50, 0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d,
+ 0x69, 0x6e, 0x67, 0x12, 0x22, 0x0a, 0x0a, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x6e, 0x61, 0x6d,
+ 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x70, 0x68, 0x61, 0x73, 0x65,
+ 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x0e, 0x64, 0x75, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48,
+ 0x01, 0x52, 0x0d, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73,
+ 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x15, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f,
+ 0x66, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x05, 0x48, 0x02, 0x52, 0x12, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x66, 0x42,
+ 0x75, 0x69, 0x6c, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f,
+ 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x64,
+ 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x42, 0x18, 0x0a,
+ 0x16, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x66, 0x5f, 0x62, 0x75, 0x69,
+ 0x6c, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x2e, 0x5a, 0x2c, 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, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+ 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_bazel_metrics_proto_rawDescOnce sync.Once
+ file_bazel_metrics_proto_rawDescData = file_bazel_metrics_proto_rawDesc
+)
+
+func file_bazel_metrics_proto_rawDescGZIP() []byte {
+ file_bazel_metrics_proto_rawDescOnce.Do(func() {
+ file_bazel_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_bazel_metrics_proto_rawDescData)
+ })
+ return file_bazel_metrics_proto_rawDescData
+}
+
+var file_bazel_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_bazel_metrics_proto_goTypes = []interface{}{
+ (*BazelMetrics)(nil), // 0: soong_build_bazel_metrics.BazelMetrics
+ (*PhaseTiming)(nil), // 1: soong_build_bazel_metrics.PhaseTiming
+}
+var file_bazel_metrics_proto_depIdxs = []int32{
+ 1, // 0: soong_build_bazel_metrics.BazelMetrics.phase_timings:type_name -> soong_build_bazel_metrics.PhaseTiming
+ 1, // [1:1] is the sub-list for method output_type
+ 1, // [1:1] is the sub-list for method input_type
+ 1, // [1:1] is the sub-list for extension type_name
+ 1, // [1:1] is the sub-list for extension extendee
+ 0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_bazel_metrics_proto_init() }
+func file_bazel_metrics_proto_init() {
+ if File_bazel_metrics_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_bazel_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*BazelMetrics); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_bazel_metrics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PhaseTiming); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ file_bazel_metrics_proto_msgTypes[0].OneofWrappers = []interface{}{}
+ file_bazel_metrics_proto_msgTypes[1].OneofWrappers = []interface{}{}
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_bazel_metrics_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_bazel_metrics_proto_goTypes,
+ DependencyIndexes: file_bazel_metrics_proto_depIdxs,
+ MessageInfos: file_bazel_metrics_proto_msgTypes,
+ }.Build()
+ File_bazel_metrics_proto = out.File
+ file_bazel_metrics_proto_rawDesc = nil
+ file_bazel_metrics_proto_goTypes = nil
+ file_bazel_metrics_proto_depIdxs = nil
+}
diff --git a/ui/metrics/bazel_metrics_proto/bazel_metrics.proto b/ui/metrics/bazel_metrics_proto/bazel_metrics.proto
new file mode 100644
index 0000000..57eed4c
--- /dev/null
+++ b/ui/metrics/bazel_metrics_proto/bazel_metrics.proto
@@ -0,0 +1,32 @@
+// 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_bazel_metrics;
+option go_package = "android/soong/ui/metrics/bazel_metrics_proto";
+
+message BazelMetrics {
+ repeated PhaseTiming phase_timings = 1;
+ optional int64 total = 2;
+}
+
+message PhaseTiming {
+ // E.g. "execution", "analysis", "launch"
+ optional string phase_name = 1;
+ optional int64 duration_nanos = 2;
+ // What portion of the build time this phase took, with ten-thousandths precision.
+ // E.g., 1111 = 11.11%, 111 = 1.11%
+ optional int32 portion_of_build_time = 3;
+}
diff --git a/ui/metrics/bazel_metrics_proto/regen.sh b/ui/metrics/bazel_metrics_proto/regen.sh
new file mode 100755
index 0000000..2cf2bf6
--- /dev/null
+++ b/ui/metrics/bazel_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 bp2build_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:. bazel_metrics.proto; then
+ die "build failed. ${error_msg}"
+fi
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
index 93f3471..4821043 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
@@ -14,8 +14,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.27.1
-// protoc v3.9.1
+// protoc-gen-go v1.28.0
+// protoc v3.21.7
// source: bp2build_metrics.proto
package bp2build_metrics_proto
@@ -45,6 +45,10 @@
HandCraftedModuleCount uint64 `protobuf:"varint,2,opt,name=handCraftedModuleCount,proto3" json:"handCraftedModuleCount,omitempty"`
// Total number of unconverted Soong modules
UnconvertedModuleCount uint64 `protobuf:"varint,3,opt,name=unconvertedModuleCount,proto3" json:"unconvertedModuleCount,omitempty"`
+ // Counts of symlinks in synthetic bazel workspace
+ WorkspaceSymlinkCount uint64 `protobuf:"varint,9,opt,name=workspaceSymlinkCount,proto3" json:"workspaceSymlinkCount,omitempty"`
+ // Counts of mkdir calls during creation of synthetic bazel workspace
+ WorkspaceMkDirCount uint64 `protobuf:"varint,10,opt,name=workspaceMkDirCount,proto3" json:"workspaceMkDirCount,omitempty"`
// Counts of generated Bazel targets per Bazel rule class
RuleClassCount map[string]uint64 `protobuf:"bytes,4,rep,name=ruleClassCount,proto3" json:"ruleClassCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
// List of converted modules
@@ -111,6 +115,20 @@
return 0
}
+func (x *Bp2BuildMetrics) GetWorkspaceSymlinkCount() uint64 {
+ if x != nil {
+ return x.WorkspaceSymlinkCount
+ }
+ return 0
+}
+
+func (x *Bp2BuildMetrics) GetWorkspaceMkDirCount() uint64 {
+ if x != nil {
+ return x.WorkspaceMkDirCount
+ }
+ return 0
+}
+
func (x *Bp2BuildMetrics) GetRuleClassCount() map[string]uint64 {
if x != nil {
return x.RuleClassCount
@@ -221,7 +239,7 @@
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, 0xe9, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
+ 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xd1, 0x07, 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,
@@ -232,60 +250,66 @@
0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76,
0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
- 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69,
- 0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74,
- 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 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, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65,
- 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43,
- 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43,
- 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e,
- 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20,
- 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
- 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
- 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75,
- 0x6e, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 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, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64,
- 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 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, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64,
- 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
- 0x7b, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
- 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 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, 0x42, 0x70, 0x32,
- 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, 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, 0x4b, 0x0a, 0x1d,
+ 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34,
+ 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69,
+ 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x77,
+ 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x43,
+ 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x4d, 0x6b, 0x44, 0x69, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28,
+ 0x04, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x6b, 0x44, 0x69,
+ 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69, 0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41,
+ 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, 0x42, 0x70,
+ 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75,
+ 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e,
+ 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e,
+ 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01,
+ 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x4b, 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,
+ 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
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,
+ 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x63,
+ 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
+ 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x7b, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+ 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+ 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 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, 0x42, 0x70, 0x32, 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, 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, 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 (
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
index 19a7827..9ff4ae2 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
@@ -27,6 +27,12 @@
// Total number of unconverted Soong modules
uint64 unconvertedModuleCount = 3;
+ // Counts of symlinks in synthetic bazel workspace
+ uint64 workspaceSymlinkCount= 9;
+
+ // Counts of mkdir calls during creation of synthetic bazel workspace
+ uint64 workspaceMkDirCount= 10;
+
// Counts of generated Bazel targets per Bazel rule class
map<string, uint64> ruleClassCount = 4;
diff --git a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
new file mode 100644
index 0000000..f6c6df8
--- /dev/null
+++ b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
@@ -0,0 +1,27 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load("//build/bazel/rules/python:py_proto.bzl", "py_proto_library")
+
+proto_library(
+ name = "bp2build_proto",
+ srcs = ["bp2build.proto"],
+ strip_import_prefix = "",
+)
+
+py_proto_library(
+ name = "bp2build_py_proto",
+ visibility = ["//build/bazel/scripts/bp2build_progress:__pkg__"],
+ deps = [":bp2build_proto"],
+)
diff --git a/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto b/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto
new file mode 100644
index 0000000..4aee88b
--- /dev/null
+++ b/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto
@@ -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.
+ */
+
+syntax = "proto3";
+
+package bp2build_proto;
+
+
+// Conversion progress report for root_modules .
+message Bp2buildConversionProgress {
+
+ // Soong module identifying information.
+ message Module {
+ // Name of the Soong module.
+ string name = 1;
+
+ // Directory that the Soong module is in.
+ string directory = 2;
+
+ // Module type of this module.
+ string type = 3;
+
+ // All unconverted transitive dependencies.
+ repeated string unconverted_deps = 4;
+
+ // Total number of transitive dependencies.
+ int32 num_deps = 5;
+ }
+
+ // Modules that the transitive dependencies were identified for.
+ repeated string root_modules = 1;
+
+ // Names of all dependencies of the root_modules.
+ int32 num_deps = 2;
+
+ // Module with all its unconverted transitive dependencies.
+ repeated Module unconverted = 3;
+}
diff --git a/ui/metrics/event.go b/ui/metrics/event.go
index ebe664f..cbdeb27 100644
--- a/ui/metrics/event.go
+++ b/ui/metrics/event.go
@@ -31,7 +31,6 @@
"time"
soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
- "android/soong/ui/tracer"
"google.golang.org/protobuf/proto"
)
@@ -50,6 +49,10 @@
// for metrics analysis).
desc string
+ nonZeroExitCode bool
+
+ errorMsg *string
+
// The time that the event started to occur.
start time.Time
@@ -68,13 +71,18 @@
func (e event) perfInfo() soong_metrics_proto.PerfInfo {
realTime := uint64(_now().Sub(e.start).Nanoseconds())
- return soong_metrics_proto.PerfInfo{
+ perfInfo := soong_metrics_proto.PerfInfo{
Description: proto.String(e.desc),
Name: proto.String(e.name),
StartTime: proto.Uint64(uint64(e.start.UnixNano())),
RealTime: proto.Uint64(realTime),
ProcessesResourceInfo: e.procResInfo,
+ NonZeroExit: proto.Bool(e.nonZeroExitCode),
}
+ if m := e.errorMsg; m != nil {
+ perfInfo.ErrorMessage = proto.String(*m)
+ }
+ return perfInfo
}
// EventTracer is an array of events that provides functionality to trace a
@@ -94,6 +102,9 @@
// peek returns the active build event.
func (t *EventTracer) peek() *event {
+ if t.empty() {
+ return nil
+ }
return (*t)[t.lastIndex()]
}
@@ -124,8 +135,8 @@
e := t.peek()
e.procResInfo = append(e.procResInfo, &soong_metrics_proto.ProcessResourceInfo{
Name: proto.String(name),
- UserTimeMicros: proto.Uint64(uint64(rusage.Utime.Usec)),
- SystemTimeMicros: proto.Uint64(uint64(rusage.Stime.Usec)),
+ UserTimeMicros: proto.Uint64(uint64(state.UserTime().Microseconds())),
+ SystemTimeMicros: proto.Uint64(uint64(state.SystemTime().Microseconds())),
MinorPageFaults: proto.Uint64(uint64(rusage.Minflt)),
MajorPageFaults: proto.Uint64(uint64(rusage.Majflt)),
// ru_inblock and ru_oublock are measured in blocks of 512 bytes.
@@ -137,12 +148,12 @@
}
// Begin starts tracing the event.
-func (t *EventTracer) Begin(name, desc string, _ tracer.Thread) {
+func (t *EventTracer) Begin(name, desc string) {
t.push(newEvent(name, desc))
}
// End performs post calculations such as duration of the event, aggregates
// the collected performance information into PerfInfo protobuf message.
-func (t *EventTracer) End(tracer.Thread) soong_metrics_proto.PerfInfo {
+func (t *EventTracer) End() soong_metrics_proto.PerfInfo {
return t.pop().perfInfo()
}
diff --git a/ui/metrics/event_test.go b/ui/metrics/event_test.go
index 043450b..a80e7cf 100644
--- a/ui/metrics/event_test.go
+++ b/ui/metrics/event_test.go
@@ -17,8 +17,6 @@
import (
"testing"
"time"
-
- "android/soong/ui/tracer"
)
func TestEnd(t *testing.T) {
@@ -35,8 +33,31 @@
start: startTime,
})
- perf := et.End(tracer.Thread(0))
+ perf := et.End()
if perf.GetRealTime() != uint64(dur.Nanoseconds()) {
t.Errorf("got %d, want %d nanoseconds for event duration", perf.GetRealTime(), dur.Nanoseconds())
}
}
+
+func TestEndWithError(t *testing.T) {
+ startTime := time.Date(2020, time.July, 13, 13, 0, 0, 0, time.UTC)
+ dur := time.Nanosecond * 10
+ initialNow := _now
+ _now = func() time.Time { return startTime.Add(dur) }
+ defer func() { _now = initialNow }()
+
+ err := "foobar"
+ et := &EventTracer{}
+ et.push(&event{
+ desc: "test",
+ name: "test",
+ start: startTime,
+ nonZeroExitCode: true,
+ errorMsg: &err,
+ })
+
+ perf := et.End()
+ if msg := perf.GetErrorMessage(); msg != err {
+ t.Errorf("got %q, want %q for even error message", msg, err)
+ }
+}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 0c62865..82d11ed 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -32,6 +32,7 @@
// of what an event is and how the metrics system is a stack based system.
import (
+ "fmt"
"os"
"runtime"
"strings"
@@ -125,6 +126,25 @@
}
}
+func (m *Metrics) SetCriticalPathInfo(criticalPathInfo soong_metrics_proto.CriticalPathInfo) {
+ m.metrics.CriticalPathInfo = &criticalPathInfo
+}
+
+// SetFatalOrPanicMessage stores a non-zero exit and the relevant message in the latest event if
+// available or the metrics base.
+func (m *Metrics) SetFatalOrPanicMessage(errMsg string) {
+ if m == nil {
+ return
+ }
+ if event := m.EventTracer.peek(); event != nil {
+ event.nonZeroExitCode = true
+ event.errorMsg = &errMsg
+ } else {
+ m.metrics.ErrorMessage = proto.String(errMsg)
+ }
+ m.metrics.NonZeroExit = proto.Bool(true)
+}
+
// BuildConfig stores information about the build configuration.
func (m *Metrics) BuildConfig(b *soong_metrics_proto.BuildConfig) {
m.metrics.BuildConfig = b
@@ -208,6 +228,17 @@
m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
}
+func (m *Metrics) UpdateTotalRealTime(data []byte) error {
+ if err := proto.Unmarshal(data, &m.metrics); err != nil {
+ return fmt.Errorf("Failed to unmarshal proto", err)
+ }
+ startTime := *m.metrics.Total.StartTime
+ endTime := uint64(time.Now().UnixNano())
+
+ *m.metrics.Total.RealTime = *proto.Uint64(endTime - startTime)
+ return nil
+}
+
// SetBuildCommand adds the build command specified by the user to the
// list of collected metrics.
func (m *Metrics) SetBuildCommand(cmd []string) {
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 69f5689..32d4dc0 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -14,8 +14,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.27.1
-// protoc v3.9.1
+// protoc-gen-go v1.30.0
+// protoc v3.21.7
// source: metrics.proto
package metrics_proto
@@ -158,6 +158,71 @@
return file_metrics_proto_rawDescGZIP(), []int{0, 1}
}
+type BuildConfig_NinjaWeightListSource int32
+
+const (
+ BuildConfig_NOT_USED BuildConfig_NinjaWeightListSource = 0
+ BuildConfig_NINJA_LOG BuildConfig_NinjaWeightListSource = 1
+ BuildConfig_EVENLY_DISTRIBUTED BuildConfig_NinjaWeightListSource = 2
+ BuildConfig_EXTERNAL_FILE BuildConfig_NinjaWeightListSource = 3
+ BuildConfig_HINT_FROM_SOONG BuildConfig_NinjaWeightListSource = 4
+)
+
+// Enum value maps for BuildConfig_NinjaWeightListSource.
+var (
+ BuildConfig_NinjaWeightListSource_name = map[int32]string{
+ 0: "NOT_USED",
+ 1: "NINJA_LOG",
+ 2: "EVENLY_DISTRIBUTED",
+ 3: "EXTERNAL_FILE",
+ 4: "HINT_FROM_SOONG",
+ }
+ BuildConfig_NinjaWeightListSource_value = map[string]int32{
+ "NOT_USED": 0,
+ "NINJA_LOG": 1,
+ "EVENLY_DISTRIBUTED": 2,
+ "EXTERNAL_FILE": 3,
+ "HINT_FROM_SOONG": 4,
+ }
+)
+
+func (x BuildConfig_NinjaWeightListSource) Enum() *BuildConfig_NinjaWeightListSource {
+ p := new(BuildConfig_NinjaWeightListSource)
+ *p = x
+ return p
+}
+
+func (x BuildConfig_NinjaWeightListSource) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (BuildConfig_NinjaWeightListSource) Descriptor() protoreflect.EnumDescriptor {
+ return file_metrics_proto_enumTypes[2].Descriptor()
+}
+
+func (BuildConfig_NinjaWeightListSource) Type() protoreflect.EnumType {
+ return &file_metrics_proto_enumTypes[2]
+}
+
+func (x BuildConfig_NinjaWeightListSource) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *BuildConfig_NinjaWeightListSource) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = BuildConfig_NinjaWeightListSource(num)
+ return nil
+}
+
+// Deprecated: Use BuildConfig_NinjaWeightListSource.Descriptor instead.
+func (BuildConfig_NinjaWeightListSource) EnumDescriptor() ([]byte, []int) {
+ return file_metrics_proto_rawDescGZIP(), []int{1, 0}
+}
+
type ModuleTypeInfo_BuildSystem int32
const (
@@ -191,11 +256,11 @@
}
func (ModuleTypeInfo_BuildSystem) Descriptor() protoreflect.EnumDescriptor {
- return file_metrics_proto_enumTypes[2].Descriptor()
+ return file_metrics_proto_enumTypes[3].Descriptor()
}
func (ModuleTypeInfo_BuildSystem) Type() protoreflect.EnumType {
- return &file_metrics_proto_enumTypes[2]
+ return &file_metrics_proto_enumTypes[3]
}
func (x ModuleTypeInfo_BuildSystem) Number() protoreflect.EnumNumber {
@@ -220,9 +285,10 @@
type ExpConfigFetcher_ConfigStatus int32
const (
- ExpConfigFetcher_NO_CONFIG ExpConfigFetcher_ConfigStatus = 0
- ExpConfigFetcher_CONFIG ExpConfigFetcher_ConfigStatus = 1
- ExpConfigFetcher_ERROR ExpConfigFetcher_ConfigStatus = 2
+ ExpConfigFetcher_NO_CONFIG ExpConfigFetcher_ConfigStatus = 0
+ ExpConfigFetcher_CONFIG ExpConfigFetcher_ConfigStatus = 1
+ ExpConfigFetcher_ERROR ExpConfigFetcher_ConfigStatus = 2
+ ExpConfigFetcher_MISSING_GCERT ExpConfigFetcher_ConfigStatus = 3
)
// Enum value maps for ExpConfigFetcher_ConfigStatus.
@@ -231,11 +297,13 @@
0: "NO_CONFIG",
1: "CONFIG",
2: "ERROR",
+ 3: "MISSING_GCERT",
}
ExpConfigFetcher_ConfigStatus_value = map[string]int32{
- "NO_CONFIG": 0,
- "CONFIG": 1,
- "ERROR": 2,
+ "NO_CONFIG": 0,
+ "CONFIG": 1,
+ "ERROR": 2,
+ "MISSING_GCERT": 3,
}
)
@@ -250,11 +318,11 @@
}
func (ExpConfigFetcher_ConfigStatus) Descriptor() protoreflect.EnumDescriptor {
- return file_metrics_proto_enumTypes[3].Descriptor()
+ return file_metrics_proto_enumTypes[4].Descriptor()
}
func (ExpConfigFetcher_ConfigStatus) Type() protoreflect.EnumType {
- return &file_metrics_proto_enumTypes[3]
+ return &file_metrics_proto_enumTypes[4]
}
func (x ExpConfigFetcher_ConfigStatus) Number() protoreflect.EnumNumber {
@@ -322,7 +390,11 @@
// The metrics for calling Ninja.
NinjaRuns []*PerfInfo `protobuf:"bytes,20,rep,name=ninja_runs,json=ninjaRuns" json:"ninja_runs,omitempty"`
// The metrics for the whole build
- Total *PerfInfo `protobuf:"bytes,21,opt,name=total" json:"total,omitempty"`
+ Total *PerfInfo `protobuf:"bytes,21,opt,name=total" json:"total,omitempty"`
+ // Deprecated because instead of embedding in a MetricsBase, we keep
+ // SoongBuildMetrics in its own file
+ //
+ // Deprecated: Marked as deprecated in metrics.proto.
SoongBuildMetrics *SoongBuildMetrics `protobuf:"bytes,22,opt,name=soong_build_metrics,json=soongBuildMetrics" json:"soong_build_metrics,omitempty"`
BuildConfig *BuildConfig `protobuf:"bytes,23,opt,name=build_config,json=buildConfig" json:"build_config,omitempty"`
// The hostname of the machine.
@@ -335,6 +407,19 @@
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"`
+ // Whether the build exited with a panic or non-zero exit code, includes both
+ // non-zero exits of recorded phases and non-recorded phases of the build.
+ NonZeroExit *bool `protobuf:"varint,29,opt,name=non_zero_exit,json=nonZeroExit" json:"non_zero_exit,omitempty"`
+ // The error message due to a non-zero exit _only_ if it did not occur in a
+ // recorded phase of the build.
+ ErrorMessage *string `protobuf:"bytes,30,opt,name=error_message,json=errorMessage" json:"error_message,omitempty"`
+ // The Git Manifest for the user's branch.
+ ManifestUrl *string `protobuf:"bytes,31,opt,name=manifest_url,json=manifestUrl" json:"manifest_url,omitempty"`
+ // The branch on which the build occurred.
+ // Example: refs/heads/master
+ Branch *string `protobuf:"bytes,32,opt,name=branch" json:"branch,omitempty"`
+ // The metric of critical path in build
+ CriticalPathInfo *CriticalPathInfo `protobuf:"bytes,33,opt,name=critical_path_info,json=criticalPathInfo" json:"critical_path_info,omitempty"`
}
// Default values for MetricsBase fields.
@@ -524,6 +609,7 @@
return nil
}
+// Deprecated: Marked as deprecated in metrics.proto.
func (x *MetricsBase) GetSoongBuildMetrics() *SoongBuildMetrics {
if x != nil {
return x.SoongBuildMetrics
@@ -573,6 +659,41 @@
return nil
}
+func (x *MetricsBase) GetNonZeroExit() bool {
+ if x != nil && x.NonZeroExit != nil {
+ return *x.NonZeroExit
+ }
+ return false
+}
+
+func (x *MetricsBase) GetErrorMessage() string {
+ if x != nil && x.ErrorMessage != nil {
+ return *x.ErrorMessage
+ }
+ return ""
+}
+
+func (x *MetricsBase) GetManifestUrl() string {
+ if x != nil && x.ManifestUrl != nil {
+ return *x.ManifestUrl
+ }
+ return ""
+}
+
+func (x *MetricsBase) GetBranch() string {
+ if x != nil && x.Branch != nil {
+ return *x.Branch
+ }
+ return ""
+}
+
+func (x *MetricsBase) GetCriticalPathInfo() *CriticalPathInfo {
+ if x != nil {
+ return x.CriticalPathInfo
+ }
+ return nil
+}
+
type BuildConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -589,8 +710,21 @@
// These are the targets soong passes to ninja, these targets include special
// targets such as droid as well as the regular build targets.
Targets []string `protobuf:"bytes,6,rep,name=targets" json:"targets,omitempty"`
+ // Whether the user explicitly disabled bazel mixed builds for this build.
+ ForceDisableBazelMixedBuild *bool `protobuf:"varint,7,opt,name=force_disable_bazel_mixed_build,json=forceDisableBazelMixedBuild" json:"force_disable_bazel_mixed_build,omitempty"`
+ // NOT_USED - ninja doesn't use weight list.
+ // NINJA_LOG - ninja uses weight list based on previous builds by ninja log
+ // EVENLY_DISTRIBUTED - ninja thinks every task has the same weight.
+ // EXTERNAL_FILE - ninja uses an external custom weight list
+ // HINT_FROM_SOONG - ninja uses a prioritized module list from Soong
+ NinjaWeightListSource *BuildConfig_NinjaWeightListSource `protobuf:"varint,8,opt,name=ninja_weight_list_source,json=ninjaWeightListSource,enum=soong_build_metrics.BuildConfig_NinjaWeightListSource,def=0" json:"ninja_weight_list_source,omitempty"`
}
+// Default values for BuildConfig fields.
+const (
+ Default_BuildConfig_NinjaWeightListSource = BuildConfig_NOT_USED
+)
+
func (x *BuildConfig) Reset() {
*x = BuildConfig{}
if protoimpl.UnsafeEnabled {
@@ -665,6 +799,20 @@
return nil
}
+func (x *BuildConfig) GetForceDisableBazelMixedBuild() bool {
+ if x != nil && x.ForceDisableBazelMixedBuild != nil {
+ return *x.ForceDisableBazelMixedBuild
+ }
+ return false
+}
+
+func (x *BuildConfig) GetNinjaWeightListSource() BuildConfig_NinjaWeightListSource {
+ if x != nil && x.NinjaWeightListSource != nil {
+ return *x.NinjaWeightListSource
+ }
+ return Default_BuildConfig_NinjaWeightListSource
+}
+
type SystemResourceInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -739,10 +887,15 @@
RealTime *uint64 `protobuf:"varint,4,opt,name=real_time,json=realTime" json:"real_time,omitempty"`
// The number of MB for memory use (deprecated as it is too generic).
//
- // Deprecated: Do not use.
+ // Deprecated: Marked as deprecated in metrics.proto.
MemoryUse *uint64 `protobuf:"varint,5,opt,name=memory_use,json=memoryUse" json:"memory_use,omitempty"`
// The resource information of each executed process.
ProcessesResourceInfo []*ProcessResourceInfo `protobuf:"bytes,6,rep,name=processes_resource_info,json=processesResourceInfo" json:"processes_resource_info,omitempty"`
+ // Whether the phase of tool running exited with a panic or non-zero exit
+ // code.
+ NonZeroExit *bool `protobuf:"varint,7,opt,name=non_zero_exit,json=nonZeroExit" json:"non_zero_exit,omitempty"`
+ // The error message, if any, due to a non-zero exit.
+ ErrorMessage *string `protobuf:"bytes,8,opt,name=error_message,json=errorMessage" json:"error_message,omitempty"`
}
func (x *PerfInfo) Reset() {
@@ -805,7 +958,7 @@
return 0
}
-// Deprecated: Do not use.
+// Deprecated: Marked as deprecated in metrics.proto.
func (x *PerfInfo) GetMemoryUse() uint64 {
if x != nil && x.MemoryUse != nil {
return *x.MemoryUse
@@ -820,6 +973,20 @@
return nil
}
+func (x *PerfInfo) GetNonZeroExit() bool {
+ if x != nil && x.NonZeroExit != nil {
+ return *x.NonZeroExit
+ }
+ return false
+}
+
+func (x *PerfInfo) GetErrorMessage() string {
+ if x != nil && x.ErrorMessage != nil {
+ return *x.ErrorMessage
+ }
+ return ""
+}
+
type ProcessResourceInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -954,9 +1121,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"`
@@ -1142,6 +1309,8 @@
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() {
@@ -1218,6 +1387,13 @@
return nil
}
+func (x *SoongBuildMetrics) GetMixedBuildsInfo() *MixedBuildsInfo {
+ if x != nil {
+ return x.MixedBuildsInfo
+ }
+ return nil
+}
+
type ExpConfigFetcher struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1287,12 +1463,203 @@
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
+}
+
+// CriticalPathInfo contains critical path nodes's information.
+// A critical path is a path determining the minimum time needed for the whole build given perfect parallelism.
+type CriticalPathInfo struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Real time which the build system spent in microseconds
+ ElapsedTimeMicros *uint64 `protobuf:"varint,1,opt,name=elapsed_time_micros,json=elapsedTimeMicros" json:"elapsed_time_micros,omitempty"`
+ // The sum of execution time of the longest path from leave to the root in microseconds
+ CriticalPathTimeMicros *uint64 `protobuf:"varint,2,opt,name=critical_path_time_micros,json=criticalPathTimeMicros" json:"critical_path_time_micros,omitempty"`
+ // Detailed job information in a critical path.
+ CriticalPath []*JobInfo `protobuf:"bytes,4,rep,name=critical_path,json=criticalPath" json:"critical_path,omitempty"`
+ // Detailed job information for long running jobs (>30 seconds). These may or may not also be on a critical path.
+ LongRunningJobs []*JobInfo `protobuf:"bytes,5,rep,name=long_running_jobs,json=longRunningJobs" json:"long_running_jobs,omitempty"`
+}
+
+func (x *CriticalPathInfo) Reset() {
+ *x = CriticalPathInfo{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_metrics_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriticalPathInfo) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriticalPathInfo) ProtoMessage() {}
+
+func (x *CriticalPathInfo) ProtoReflect() protoreflect.Message {
+ mi := &file_metrics_proto_msgTypes[11]
+ 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 CriticalPathInfo.ProtoReflect.Descriptor instead.
+func (*CriticalPathInfo) Descriptor() ([]byte, []int) {
+ return file_metrics_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *CriticalPathInfo) GetElapsedTimeMicros() uint64 {
+ if x != nil && x.ElapsedTimeMicros != nil {
+ return *x.ElapsedTimeMicros
+ }
+ return 0
+}
+
+func (x *CriticalPathInfo) GetCriticalPathTimeMicros() uint64 {
+ if x != nil && x.CriticalPathTimeMicros != nil {
+ return *x.CriticalPathTimeMicros
+ }
+ return 0
+}
+
+func (x *CriticalPathInfo) GetCriticalPath() []*JobInfo {
+ if x != nil {
+ return x.CriticalPath
+ }
+ return nil
+}
+
+func (x *CriticalPathInfo) GetLongRunningJobs() []*JobInfo {
+ if x != nil {
+ return x.LongRunningJobs
+ }
+ return nil
+}
+
+type JobInfo struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Real time which a job spent in microseconds
+ ElapsedTimeMicros *uint64 `protobuf:"varint,1,opt,name=elapsed_time_micros,json=elapsedTimeMicros" json:"elapsed_time_micros,omitempty"`
+ // Description of a job
+ JobDescription *string `protobuf:"bytes,2,opt,name=job_description,json=jobDescription" json:"job_description,omitempty"`
+}
+
+func (x *JobInfo) Reset() {
+ *x = JobInfo{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_metrics_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *JobInfo) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JobInfo) ProtoMessage() {}
+
+func (x *JobInfo) ProtoReflect() protoreflect.Message {
+ mi := &file_metrics_proto_msgTypes[12]
+ 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 JobInfo.ProtoReflect.Descriptor instead.
+func (*JobInfo) Descriptor() ([]byte, []int) {
+ return file_metrics_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *JobInfo) GetElapsedTimeMicros() uint64 {
+ if x != nil && x.ElapsedTimeMicros != nil {
+ return *x.ElapsedTimeMicros
+ }
+ return 0
+}
+
+func (x *JobInfo) GetJobDescription() string {
+ if x != nil && x.JobDescription != nil {
+ return *x.JobDescription
+ }
+ return ""
+}
+
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, 0xad, 0x0d, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+ 0x72, 0x69, 0x63, 0x73, 0x22, 0x8a, 0x0f, 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,
@@ -1363,166 +1730,242 @@
0x75, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x15, 0x20, 0x01,
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, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x56, 0x0a, 0x13, 0x73, 0x6f, 0x6f, 0x6e,
+ 0x6f, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x5a, 0x0a, 0x13, 0x73, 0x6f, 0x6f, 0x6e,
0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18,
0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53, 0x6f, 0x6f, 0x6e,
- 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x11, 0x73,
- 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
- 0x12, 0x43, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
- 0x18, 0x17, 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, 0x42, 0x75, 0x69,
- 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43,
- 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
- 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
- 0x65, 0x12, 0x59, 0x0a, 0x14, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x65, 0x73, 0x6f,
- 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x27, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
- 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f,
- 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
- 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x23, 0x0a, 0x0d,
- 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x1a, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
- 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, 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,
+ 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x02, 0x18,
+ 0x01, 0x52, 0x11, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x18, 0x17, 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, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x62, 0x75,
+ 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73,
+ 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73,
+ 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x59, 0x0a, 0x14, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f,
+ 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x19, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c,
+ 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+ 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x12, 0x73, 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, 0xfa, 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, 0x12, 0x35, 0x0a, 0x06, 0x65,
- 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f,
+ 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
+ 0x64, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f,
+ 0x6d, 0x6d, 0x61, 0x6e, 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, 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, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x6e, 0x5f,
+ 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x0b, 0x6e, 0x6f, 0x6e, 0x5a, 0x65, 0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d,
+ 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x1e, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
+ 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x5f, 0x75, 0x72,
+ 0x6c, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
+ 0x74, 0x55, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x20,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x53, 0x0a, 0x12,
+ 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x69, 0x6e,
+ 0x66, 0x6f, 0x18, 0x21, 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, 0x43,
+ 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52,
+ 0x10, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66,
+ 0x6f, 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, 0x8a, 0x04, 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, 0x12, 0x44, 0x0a, 0x1f, 0x66, 0x6f, 0x72, 0x63, 0x65,
+ 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d,
+ 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x1b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61,
+ 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x79, 0x0a,
+ 0x18, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6c, 0x69,
+ 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32,
+ 0x36, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+ 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x2e, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73,
+ 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45,
+ 0x44, 0x52, 0x15, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69,
+ 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x74, 0x0a, 0x15, 0x4e, 0x69, 0x6e, 0x6a,
+ 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x10, 0x00, 0x12,
+ 0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x4e, 0x4a, 0x41, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x01, 0x12, 0x16,
+ 0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x4c, 0x59, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42,
+ 0x55, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4e,
+ 0x41, 0x4c, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x48, 0x49, 0x4e,
+ 0x54, 0x5f, 0x46, 0x52, 0x4f, 0x4d, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x04, 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,
+ 0xca, 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, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x6e, 0x5f, 0x7a, 0x65, 0x72, 0x6f, 0x5f,
+ 0x65, 0x78, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x6e, 0x5a,
+ 0x65, 0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72,
+ 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
+ 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 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, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e,
- 0x74, 0x73, 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, 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, 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,
+ 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, 0xdb, 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, 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, 0x47, 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, 0x12, 0x11, 0x0a, 0x0d,
+ 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45, 0x52, 0x54, 0x10, 0x03, 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, 0x22, 0x8a, 0x02, 0x0a, 0x10, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
+ 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70,
+ 0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69,
+ 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, 0x69, 0x74,
+ 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d,
+ 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x63, 0x72, 0x69,
+ 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63,
+ 0x72, 0x6f, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f,
+ 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f,
+ 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+ 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63,
+ 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x48, 0x0a, 0x11, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x72,
+ 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+ 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52,
+ 0x0f, 0x6c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x4a, 0x6f, 0x62, 0x73,
+ 0x22, 0x62, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65,
+ 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72,
+ 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65,
+ 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6a,
+ 0x6f, 0x62, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 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 (
@@ -1537,50 +1980,59 @@
return file_metrics_proto_rawDescData
}
-var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
-var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
+var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
+var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
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
- (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
+ (MetricsBase_BuildVariant)(0), // 0: soong_build_metrics.MetricsBase.BuildVariant
+ (MetricsBase_Arch)(0), // 1: soong_build_metrics.MetricsBase.Arch
+ (BuildConfig_NinjaWeightListSource)(0), // 2: soong_build_metrics.BuildConfig.NinjaWeightListSource
+ (ModuleTypeInfo_BuildSystem)(0), // 3: soong_build_metrics.ModuleTypeInfo.BuildSystem
+ (ExpConfigFetcher_ConfigStatus)(0), // 4: soong_build_metrics.ExpConfigFetcher.ConfigStatus
+ (*MetricsBase)(nil), // 5: soong_build_metrics.MetricsBase
+ (*BuildConfig)(nil), // 6: soong_build_metrics.BuildConfig
+ (*SystemResourceInfo)(nil), // 7: soong_build_metrics.SystemResourceInfo
+ (*PerfInfo)(nil), // 8: soong_build_metrics.PerfInfo
+ (*ProcessResourceInfo)(nil), // 9: soong_build_metrics.ProcessResourceInfo
+ (*ModuleTypeInfo)(nil), // 10: soong_build_metrics.ModuleTypeInfo
+ (*CriticalUserJourneyMetrics)(nil), // 11: soong_build_metrics.CriticalUserJourneyMetrics
+ (*CriticalUserJourneysMetrics)(nil), // 12: soong_build_metrics.CriticalUserJourneysMetrics
+ (*SoongBuildMetrics)(nil), // 13: soong_build_metrics.SoongBuildMetrics
+ (*ExpConfigFetcher)(nil), // 14: soong_build_metrics.ExpConfigFetcher
+ (*MixedBuildsInfo)(nil), // 15: soong_build_metrics.MixedBuildsInfo
+ (*CriticalPathInfo)(nil), // 16: soong_build_metrics.CriticalPathInfo
+ (*JobInfo)(nil), // 17: soong_build_metrics.JobInfo
}
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
- 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
- 3, // 19: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
- 20, // [20:20] is the sub-list for method output_type
- 20, // [20:20] is the sub-list for method input_type
- 20, // [20:20] is the sub-list for extension type_name
- 20, // [20:20] is the sub-list for extension extendee
- 0, // [0:20] is the sub-list for field type_name
+ 8, // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
+ 8, // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
+ 8, // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
+ 8, // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
+ 8, // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
+ 13, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
+ 6, // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
+ 7, // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
+ 8, // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
+ 14, // 13: soong_build_metrics.MetricsBase.exp_config_fetcher:type_name -> soong_build_metrics.ExpConfigFetcher
+ 16, // 14: soong_build_metrics.MetricsBase.critical_path_info:type_name -> soong_build_metrics.CriticalPathInfo
+ 2, // 15: soong_build_metrics.BuildConfig.ninja_weight_list_source:type_name -> soong_build_metrics.BuildConfig.NinjaWeightListSource
+ 9, // 16: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
+ 3, // 17: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
+ 5, // 18: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
+ 11, // 19: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
+ 8, // 20: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
+ 15, // 21: soong_build_metrics.SoongBuildMetrics.mixed_builds_info:type_name -> soong_build_metrics.MixedBuildsInfo
+ 4, // 22: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
+ 17, // 23: soong_build_metrics.CriticalPathInfo.critical_path:type_name -> soong_build_metrics.JobInfo
+ 17, // 24: soong_build_metrics.CriticalPathInfo.long_running_jobs:type_name -> soong_build_metrics.JobInfo
+ 25, // [25:25] is the sub-list for method output_type
+ 25, // [25:25] is the sub-list for method input_type
+ 25, // [25:25] is the sub-list for extension type_name
+ 25, // [25:25] is the sub-list for extension extendee
+ 0, // [0:25] is the sub-list for field type_name
}
func init() { file_metrics_proto_init() }
@@ -1709,14 +2161,50 @@
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
+ }
+ }
+ file_metrics_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriticalPathInfo); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_metrics_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*JobInfo); 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: 4,
- NumMessages: 10,
+ NumEnums: 5,
+ NumMessages: 13,
NumExtensions: 0,
NumServices: 0,
},
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 814eb67..6db83e9 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -93,7 +93,9 @@
// The metrics for the whole build
optional PerfInfo total = 21;
- optional SoongBuildMetrics soong_build_metrics = 22;
+ // Deprecated because instead of embedding in a MetricsBase, we keep
+ // SoongBuildMetrics in its own file
+ optional SoongBuildMetrics soong_build_metrics = 22 [deprecated=true];
optional BuildConfig build_config = 23;
@@ -111,9 +113,35 @@
// The metrics of the experiment config fetcher
optional ExpConfigFetcher exp_config_fetcher = 28;
+
+ // Whether the build exited with a panic or non-zero exit code, includes both
+ // non-zero exits of recorded phases and non-recorded phases of the build.
+ optional bool non_zero_exit = 29;
+
+ // The error message due to a non-zero exit _only_ if it did not occur in a
+ // recorded phase of the build.
+ optional string error_message = 30;
+
+ // The Git Manifest for the user's branch.
+ optional string manifest_url = 31;
+
+ // The branch on which the build occurred.
+ // Example: refs/heads/master
+ optional string branch = 32;
+
+ // The metric of critical path in build
+ optional CriticalPathInfo critical_path_info = 33;
}
message BuildConfig {
+ enum NinjaWeightListSource {
+ NOT_USED = 0;
+ NINJA_LOG = 1;
+ EVENLY_DISTRIBUTED = 2;
+ EXTERNAL_FILE = 3;
+ HINT_FROM_SOONG = 4;
+ }
+
optional bool use_goma = 1;
optional bool use_rbe = 2;
@@ -130,6 +158,16 @@
// These are the targets soong passes to ninja, these targets include special
// targets such as droid as well as the regular build targets.
repeated string targets = 6;
+
+ // Whether the user explicitly disabled bazel mixed builds for this build.
+ optional bool force_disable_bazel_mixed_build = 7;
+
+ // NOT_USED - ninja doesn't use weight list.
+ // NINJA_LOG - ninja uses weight list based on previous builds by ninja log
+ // EVENLY_DISTRIBUTED - ninja thinks every task has the same weight.
+ // EXTERNAL_FILE - ninja uses an external custom weight list
+ // HINT_FROM_SOONG - ninja uses a prioritized module list from Soong
+ optional NinjaWeightListSource ninja_weight_list_source = 8 [default = NOT_USED];
}
message SystemResourceInfo {
@@ -160,6 +198,13 @@
// The resource information of each executed process.
repeated ProcessResourceInfo processes_resource_info = 6;
+
+ // Whether the phase of tool running exited with a panic or non-zero exit
+ // code.
+ optional bool non_zero_exit = 7;
+
+ // The error message, if any, due to a non-zero exit.
+ optional string error_message = 8;
}
message ProcessResourceInfo {
@@ -200,10 +245,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.
@@ -241,6 +286,9 @@
// Runtime metrics for soong_build execution.
repeated PerfInfo events = 6;
+
+ // Mixed Builds information
+ optional MixedBuildsInfo mixed_builds_info = 7;
}
message ExpConfigFetcher {
@@ -248,6 +296,7 @@
NO_CONFIG = 0;
CONFIG = 1;
ERROR = 2;
+ MISSING_GCERT = 3;
}
// The result of the call to expconfigfetcher
// NO_CONFIG - Not part of experiment
@@ -261,3 +310,45 @@
// 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;
+}
+
+// CriticalPathInfo contains critical path nodes's information.
+// A critical path is a path determining the minimum time needed for the whole build given perfect parallelism.
+message CriticalPathInfo {
+ // Real time which the build system spent in microseconds
+ optional uint64 elapsed_time_micros = 1;
+ // The sum of execution time of the longest path from leave to the root in microseconds
+ optional uint64 critical_path_time_micros = 2;
+ // Detailed job information in a critical path.
+ repeated JobInfo critical_path = 4;
+ // Detailed job information for long running jobs (>30 seconds). These may or may not also be on a critical path.
+ repeated JobInfo long_running_jobs = 5;
+}
+
+message JobInfo {
+ // Real time which a job spent in microseconds
+ optional uint64 elapsed_time_micros = 1;
+ // Description of a job
+ optional string job_description = 2;
+}
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
index a46a007..b724bc9 100644
--- a/ui/status/Android.bp
+++ b/ui/status/Android.bp
@@ -28,6 +28,7 @@
],
srcs: [
"critical_path.go",
+ "critical_path_logger.go",
"kati.go",
"log.go",
"ninja.go",
diff --git a/ui/status/critical_path.go b/ui/status/critical_path.go
index 8065c60..bf32c58 100644
--- a/ui/status/critical_path.go
+++ b/ui/status/critical_path.go
@@ -15,23 +15,23 @@
package status
import (
+ "android/soong/ui/metrics"
+
+ soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
"time"
- "android/soong/ui/logger"
+ "google.golang.org/protobuf/proto"
)
-func NewCriticalPath(log logger.Logger) StatusOutput {
- return &criticalPath{
- log: log,
+func NewCriticalPath() *CriticalPath {
+ return &CriticalPath{
running: make(map[*Action]time.Time),
nodes: make(map[string]*node),
clock: osClock{},
}
}
-type criticalPath struct {
- log logger.Logger
-
+type CriticalPath struct {
nodes map[string]*node
running map[*Action]time.Time
@@ -57,7 +57,7 @@
input *node
}
-func (cp *criticalPath) StartAction(action *Action, counts Counts) {
+func (cp *CriticalPath) StartAction(action *Action) {
start := cp.clock.Now()
if cp.start.IsZero() {
cp.start = start
@@ -65,13 +65,13 @@
cp.running[action] = start
}
-func (cp *criticalPath) FinishAction(result ActionResult, counts Counts) {
- if start, ok := cp.running[result.Action]; ok {
- delete(cp.running, result.Action)
+func (cp *CriticalPath) FinishAction(action *Action) {
+ if start, ok := cp.running[action]; ok {
+ delete(cp.running, action)
// Determine the input to this edge with the longest cumulative duration
var criticalPathInput *node
- for _, input := range result.Action.Inputs {
+ for _, input := range action.Inputs {
if x := cp.nodes[input]; x != nil {
if criticalPathInput == nil || x.cumulativeDuration > criticalPathInput.cumulativeDuration {
criticalPathInput = x
@@ -88,13 +88,13 @@
}
node := &node{
- action: result.Action,
+ action: action,
cumulativeDuration: cumulativeDuration,
duration: duration,
input: criticalPathInput,
}
- for _, output := range result.Action.Outputs {
+ for _, output := range action.Outputs {
cp.nodes[output] = node
}
@@ -102,37 +102,7 @@
}
}
-func (cp *criticalPath) Flush() {
- criticalPath := cp.criticalPath()
-
- if len(criticalPath) > 0 {
- // Log the critical path to the verbose log
- criticalTime := criticalPath[0].cumulativeDuration.Round(time.Second)
- cp.log.Verbosef("critical path took %s", criticalTime.String())
- if !cp.start.IsZero() {
- elapsedTime := cp.end.Sub(cp.start).Round(time.Second)
- cp.log.Verbosef("elapsed time %s", elapsedTime.String())
- if elapsedTime > 0 {
- cp.log.Verbosef("perfect parallelism ratio %d%%",
- int(float64(criticalTime)/float64(elapsedTime)*100))
- }
- }
- cp.log.Verbose("critical path:")
- for i := len(criticalPath) - 1; i >= 0; i-- {
- duration := criticalPath[i].duration
- duration = duration.Round(time.Second)
- seconds := int(duration.Seconds())
- cp.log.Verbosef(" %2d:%02d %s",
- seconds/60, seconds%60, criticalPath[i].action.Description)
- }
- }
-}
-
-func (cp *criticalPath) Message(level MsgLevel, msg string) {}
-
-func (cp *criticalPath) Write(p []byte) (n int, err error) { return len(p), nil }
-
-func (cp *criticalPath) criticalPath() []*node {
+func (cp *CriticalPath) criticalPath() (path []*node, elapsedTime time.Duration, criticalTime time.Duration) {
var max *node
// Find the node with the longest critical path
@@ -142,13 +112,46 @@
}
}
- // Follow the critical path back to the leaf node
- var criticalPath []*node
node := max
for node != nil {
- criticalPath = append(criticalPath, node)
+ path = append(path, node)
node = node.input
}
+ if len(path) > 0 {
+ // Log the critical path to the verbose log
+ criticalTime = path[0].cumulativeDuration
+ if !cp.start.IsZero() {
+ elapsedTime = cp.end.Sub(cp.start)
+ }
+ }
+ return
+}
- return criticalPath
+func (cp *CriticalPath) longRunningJobs() (nodes []*node) {
+ threshold := time.Second * 30
+ for _, node := range cp.nodes {
+ if node != nil && node.duration > threshold {
+ nodes = append(nodes, node)
+ }
+ }
+ return
+}
+
+func addJobInfos(jobInfos *[]*soong_metrics_proto.JobInfo, sources []*node) {
+ for _, job := range sources {
+ jobInfo := soong_metrics_proto.JobInfo{}
+ jobInfo.ElapsedTimeMicros = proto.Uint64(uint64(job.duration.Microseconds()))
+ jobInfo.JobDescription = &job.action.Description
+ *jobInfos = append(*jobInfos, &jobInfo)
+ }
+}
+
+func (cp *CriticalPath) WriteToMetrics(met *metrics.Metrics) {
+ criticalPathInfo := soong_metrics_proto.CriticalPathInfo{}
+ path, elapsedTime, criticalTime := cp.criticalPath()
+ criticalPathInfo.ElapsedTimeMicros = proto.Uint64(uint64(elapsedTime.Microseconds()))
+ criticalPathInfo.CriticalPathTimeMicros = proto.Uint64(uint64(criticalTime.Microseconds()))
+ addJobInfos(&criticalPathInfo.LongRunningJobs, cp.longRunningJobs())
+ addJobInfos(&criticalPathInfo.CriticalPath, path)
+ met.SetCriticalPathInfo(criticalPathInfo)
}
diff --git a/ui/status/critical_path_logger.go b/ui/status/critical_path_logger.go
new file mode 100644
index 0000000..f8b49d1
--- /dev/null
+++ b/ui/status/critical_path_logger.go
@@ -0,0 +1,73 @@
+// Copyright 2023 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 status
+
+import (
+ "time"
+
+ "android/soong/ui/logger"
+)
+
+// Create a new CriticalPathLogger. if criticalPath is nil, it creates a new criticalPath,
+// if not, it uses that.(its purpose is using a critical path outside logger)
+func NewCriticalPathLogger(log logger.Logger, criticalPath *CriticalPath) StatusOutput {
+ if criticalPath == nil {
+ criticalPath = NewCriticalPath()
+ }
+ return &criticalPathLogger{
+ log: log,
+ criticalPath: criticalPath,
+ }
+}
+
+type criticalPathLogger struct {
+ log logger.Logger
+ criticalPath *CriticalPath
+}
+
+func (cp *criticalPathLogger) StartAction(action *Action, counts Counts) {
+ cp.criticalPath.StartAction(action)
+}
+
+func (cp *criticalPathLogger) FinishAction(result ActionResult, counts Counts) {
+ cp.criticalPath.FinishAction(result.Action)
+}
+
+func (cp *criticalPathLogger) Flush() {
+ criticalPath, elapsedTime, criticalTime := cp.criticalPath.criticalPath()
+
+ if len(criticalPath) > 0 {
+ cp.log.Verbosef("critical path took %s", criticalTime.String())
+ if !cp.criticalPath.start.IsZero() {
+ cp.log.Verbosef("elapsed time %s", elapsedTime.String())
+ if elapsedTime > 0 {
+ cp.log.Verbosef("perfect parallelism ratio %d%%",
+ int(float64(criticalTime)/float64(elapsedTime)*100))
+ }
+ }
+ cp.log.Verbose("critical path:")
+ for i := len(criticalPath) - 1; i >= 0; i-- {
+ duration := criticalPath[i].duration
+ duration = duration.Round(time.Second)
+ seconds := int(duration.Seconds())
+ cp.log.Verbosef(" %2d:%02d %s",
+ seconds/60, seconds%60, criticalPath[i].action.Description)
+ }
+ }
+}
+
+func (cp *criticalPathLogger) Message(level MsgLevel, msg string) {}
+
+func (cp *criticalPathLogger) Write(p []byte) (n int, err error) { return len(p), nil }
diff --git a/ui/status/critical_path_test.go b/ui/status/critical_path_test.go
index 965e0ad..369e805 100644
--- a/ui/status/critical_path_test.go
+++ b/ui/status/critical_path_test.go
@@ -21,7 +21,7 @@
)
type testCriticalPath struct {
- *criticalPath
+ *CriticalPath
Counts
actions map[int]*Action
@@ -40,14 +40,12 @@
}
t.actions[id] = action
- t.StartAction(action, t.Counts)
+ t.StartAction(action)
}
func (t *testCriticalPath) finish(id int, endTime time.Duration) {
t.clock = testClock(time.Unix(0, 0).Add(endTime))
- t.FinishAction(ActionResult{
- Action: t.actions[id],
- }, t.Counts)
+ t.FinishAction(t.actions[id])
}
func TestCriticalPath(t *testing.T) {
@@ -137,13 +135,13 @@
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cp := &testCriticalPath{
- criticalPath: NewCriticalPath(nil).(*criticalPath),
+ CriticalPath: NewCriticalPath(),
actions: make(map[int]*Action),
}
tt.msgs(cp)
- criticalPath := cp.criticalPath.criticalPath()
+ criticalPath, _, _ := cp.CriticalPath.criticalPath()
var descs []string
for _, x := range criticalPath {
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index 4d99621..fc0e21a 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -35,8 +35,7 @@
func NewNinjaReader(ctx logger.Logger, status ToolStatus, fifo string) *NinjaReader {
os.Remove(fifo)
- err := syscall.Mkfifo(fifo, 0666)
- if err != nil {
+ if err := syscall.Mkfifo(fifo, 0666); err != nil {
ctx.Fatalf("Failed to mkfifo(%q): %v", fifo, err)
}
diff --git a/ui/terminal/simple_status.go b/ui/terminal/simple_status.go
index 3157813..9e9ffc0 100644
--- a/ui/terminal/simple_status.go
+++ b/ui/terminal/simple_status.go
@@ -22,41 +22,36 @@
)
type simpleStatusOutput struct {
- writer io.Writer
- formatter formatter
- keepANSI bool
- outputLevel status.MsgLevel
+ writer io.Writer
+ formatter formatter
+ keepANSI bool
}
// NewSimpleStatusOutput returns a StatusOutput that represents the
// current build status similarly to Ninja's built-in terminal
// output.
-func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool, quietBuild bool) status.StatusOutput {
- level := status.StatusLvl
- if quietBuild {
- level = status.PrintLvl
- }
+func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool) status.StatusOutput {
return &simpleStatusOutput{
- writer: w,
- formatter: formatter,
- keepANSI: keepANSI,
- outputLevel: level,
+ writer: w,
+ formatter: formatter,
+ keepANSI: keepANSI,
}
}
func (s *simpleStatusOutput) Message(level status.MsgLevel, message string) {
- if level >= s.outputLevel {
- fmt.Fprintln(s.writer, s.formatter.message(level, message))
+ if level >= status.StatusLvl {
+ output := s.formatter.message(level, message)
+ if !s.keepANSI {
+ output = string(stripAnsiEscapes([]byte(output)))
+ }
+ fmt.Fprintln(s.writer, output)
}
}
-func (s *simpleStatusOutput) StartAction(_ *status.Action, _ status.Counts) {
+func (s *simpleStatusOutput) StartAction(action *status.Action, counts status.Counts) {
}
func (s *simpleStatusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
- if s.outputLevel > status.StatusLvl {
- return
- }
str := result.Description
if str == "" {
str = result.Command
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index ff0af47..2ad174f 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -29,9 +29,9 @@
func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
formatter := newFormatter(statusFormat, quietBuild)
- if forceSimpleOutput || quietBuild || !isSmartTerminal(w) {
- return NewSimpleStatusOutput(w, formatter, forceKeepANSI, quietBuild)
- } else {
+ if !forceSimpleOutput && isSmartTerminal(w) {
return NewSmartStatusOutput(w, formatter)
+ } else {
+ return NewSimpleStatusOutput(w, formatter, forceKeepANSI)
}
}
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
index 810e31d..b9057d2 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -81,9 +81,9 @@
},
{
name: "action with output with ansi codes",
- calls: actionWithOuptutWithAnsiCodes,
- smart: "\r\x1b[1m[ 0% 0/1] action1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] action1\x1b[0m\x1b[K\n\x1b[31mcolor\x1b[0m\n",
- simple: "[100% 1/1] action1\ncolor\n",
+ calls: actionWithOutputWithAnsiCodes,
+ smart: "\r\x1b[1m[ 0% 0/1] action1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] action1\x1b[0m\x1b[K\n\x1b[31mcolor\x1b[0m\n\x1b[31mcolor message\x1b[0m\n",
+ simple: "[100% 1/1] action1\ncolor\ncolor message\n",
},
}
@@ -257,12 +257,14 @@
runner.finishAction(result1)
}
-func actionWithOuptutWithAnsiCodes(stat status.StatusOutput) {
+func actionWithOutputWithAnsiCodes(stat status.StatusOutput) {
result1WithOutputWithAnsiCodes := status.ActionResult{Action: action1, Output: "\x1b[31mcolor\x1b[0m"}
runner := newRunner(stat, 1)
runner.startAction(action1)
runner.finishAction(result1WithOutputWithAnsiCodes)
+
+ stat.Message(status.PrintLvl, "\x1b[31mcolor message\x1b[0m")
}
func TestSmartStatusOutputWidthChange(t *testing.T) {
diff --git a/xml/Android.bp b/xml/Android.bp
index 1542930..d4753de 100644
--- a/xml/Android.bp
+++ b/xml/Android.bp
@@ -9,6 +9,7 @@
"blueprint",
"blueprint-pathtools",
"soong",
+ "soong-bp2build",
"soong-android",
"soong-etc",
],
@@ -18,6 +19,7 @@
],
testSrcs: [
"xml_test.go",
+ "xml_conversion_test.go",
],
pluginFor: ["soong_build"],
}
diff --git a/xml/xml.go b/xml/xml.go
index c281078..8c0c072 100644
--- a/xml/xml.go
+++ b/xml/xml.go
@@ -16,6 +16,7 @@
import (
"android/soong/android"
+ "android/soong/bazel"
"android/soong/etc"
"github.com/google/blueprint"
@@ -67,6 +68,8 @@
}
type prebuiltEtcXml struct {
+ android.BazelModuleBase
+
etc.PrebuiltEtc
properties prebuiltEtcXmlProperties
@@ -129,5 +132,40 @@
etc.InitPrebuiltEtcModule(&module.PrebuiltEtc, "etc")
// This module is device-only
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitBazelModule(module)
return module
}
+
+type bazelPrebuiltEtcXmlAttributes struct {
+ Src bazel.LabelAttribute
+ Filename bazel.LabelAttribute
+ Dir string
+ Installable bazel.BoolAttribute
+ Filename_from_src bazel.BoolAttribute
+ Schema *string
+}
+
+func (p *prebuiltEtcXml) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ baseAttrs := p.PrebuiltEtc.Bp2buildHelper(ctx)
+
+ var schema *string
+ if p.properties.Schema != nil {
+ schema = p.properties.Schema
+ }
+
+ attrs := &bazelPrebuiltEtcXmlAttributes{
+ Src: baseAttrs.Src,
+ Filename: baseAttrs.Filename,
+ Dir: baseAttrs.Dir,
+ Installable: baseAttrs.Installable,
+ Filename_from_src: baseAttrs.Filename_from_src,
+ Schema: schema,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "prebuilt_xml",
+ Bzl_load_location: "//build/bazel/rules/prebuilt_xml.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: p.Name()}, attrs)
+}
diff --git a/xml/xml_conversion_test.go b/xml/xml_conversion_test.go
new file mode 100644
index 0000000..6606ddc
--- /dev/null
+++ b/xml/xml_conversion_test.go
@@ -0,0 +1,129 @@
+// 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 xml
+
+import (
+ "android/soong/android"
+ "android/soong/bp2build"
+
+ "testing"
+)
+
+func runXmlPrebuiltEtcTestCase(t *testing.T, tc bp2build.Bp2buildTestCase) {
+ t.Helper()
+ (&tc).ModuleTypeUnderTest = "prebuilt_etc_xml"
+ (&tc).ModuleTypeUnderTestFactory = PrebuiltEtcXmlFactory
+ bp2build.RunBp2BuildTestCase(t, registerXmlModuleTypes, tc)
+}
+
+func registerXmlModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestXmlPrebuiltEtcSimple(t *testing.T) {
+ runXmlPrebuiltEtcTestCase(t, bp2build.Bp2buildTestCase{
+ Description: "prebuilt_etc_xml - simple example",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc_xml {
+ name: "foo",
+ src: "fooSrc",
+ filename: "fooFileName",
+ sub_dir: "fooDir",
+ schema: "foo.dtd",
+}
+`,
+ ExpectedBazelTargets: []string{
+ bp2build.MakeBazelTarget("prebuilt_xml", "foo", bp2build.AttrNameToString{
+ "src": `"fooSrc"`,
+ "filename": `"fooFileName"`,
+ "dir": `"etc/fooDir"`,
+ "schema": `"foo.dtd"`,
+ })}})
+}
+
+func TestXmlPrebuiltEtcFilenameFromSrc(t *testing.T) {
+ runXmlPrebuiltEtcTestCase(t, bp2build.Bp2buildTestCase{
+ Description: "prebuilt_etc_xml - filenameFromSrc True ",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc_xml {
+ name: "foo",
+ src: "fooSrc",
+ filename_from_src: true,
+ sub_dir: "fooDir",
+ schema: "foo.dtd",
+}
+`,
+ ExpectedBazelTargets: []string{
+ bp2build.MakeBazelTarget("prebuilt_xml", "foo", bp2build.AttrNameToString{
+ "src": `"fooSrc"`,
+ "filename": `"fooSrc"`,
+ "dir": `"etc/fooDir"`,
+ "schema": `"foo.dtd"`,
+ })}})
+}
+
+func TestXmlPrebuiltEtcFilenameAndFilenameFromSrc(t *testing.T) {
+ runXmlPrebuiltEtcTestCase(t, bp2build.Bp2buildTestCase{
+ Description: "prebuilt_etc_xml - filename provided and filenameFromSrc True ",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc_xml {
+ name: "foo",
+ src: "fooSrc",
+ filename: "fooFileName",
+ filename_from_src: true,
+ sub_dir: "fooDir",
+ schema: "foo.dtd",
+}
+`,
+ ExpectedBazelTargets: []string{
+ bp2build.MakeBazelTarget("prebuilt_xml", "foo", bp2build.AttrNameToString{
+ "src": `"fooSrc"`,
+ "filename": `"fooFileName"`,
+ "dir": `"etc/fooDir"`,
+ "schema": `"foo.dtd"`,
+ })}})
+}
+
+func TestXmlPrebuiltEtcFileNameFromSrcMultipleSrcs(t *testing.T) {
+ runXmlPrebuiltEtcTestCase(t, bp2build.Bp2buildTestCase{
+ Description: "prebuilt_etc - filename_from_src is true but there are multiple srcs",
+ Filesystem: map[string]string{},
+ Blueprint: `
+prebuilt_etc_xml {
+ name: "foo",
+ filename_from_src: true,
+ arch: {
+ arm: {
+ src: "barSrc",
+ },
+ arm64: {
+ src: "bazSrc",
+ },
+ }
+}
+`,
+ ExpectedBazelTargets: []string{
+ bp2build.MakeBazelTarget("prebuilt_xml", "foo", bp2build.AttrNameToString{
+ "filename_from_src": `True`,
+ "dir": `"etc"`,
+ "src": `select({
+ "//build/bazel/platforms/arch:arm": "barSrc",
+ "//build/bazel/platforms/arch:arm64": "bazSrc",
+ "//conditions:default": None,
+ })`,
+ })}})
+}
diff --git a/zip/cmd/BUILD.bazel b/zip/cmd/BUILD.bazel
new file mode 100644
index 0000000..e04a1e1
--- /dev/null
+++ b/zip/cmd/BUILD.bazel
@@ -0,0 +1,20 @@
+# 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.
+
+# TODO(b/194644518): Switch to the source version when Bazel can build go
+# binaries.
+alias(
+ name = "soong_zip",
+ actual = "//prebuilts/build-tools:linux-x86/bin/soong_zip",
+)
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index cbc73ed..def76aa 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -163,6 +163,7 @@
parallelJobs := flags.Int("parallel", runtime.NumCPU(), "number of parallel threads to use")
cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
traceFile := flags.String("trace", "", "write trace to file")
+ sha256Checksum := flags.Bool("sha256", false, "add a zip header to each file containing its SHA256 digest")
flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
flags.Var(&listFiles{}, "l", "file containing list of files to zip")
@@ -224,6 +225,7 @@
WriteIfChanged: *writeIfChanged,
StoreSymlinks: *symlinks,
IgnoreMissingFiles: *ignoreMissingFiles,
+ Sha256Checksum: *sha256Checksum,
})
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err.Error())
diff --git a/zip/zip.go b/zip/zip.go
index ae379f5..6f1a8ad 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -17,8 +17,11 @@
import (
"bytes"
"compress/flate"
+ "crypto/sha256"
+ "encoding/binary"
"errors"
"fmt"
+ "hash"
"hash/crc32"
"io"
"io/ioutil"
@@ -38,6 +41,14 @@
"android/soong/third_party/zip"
)
+// Sha256HeaderID is a custom Header ID for the `extra` field in
+// the file header to store the SHA checksum.
+const Sha256HeaderID = 0x4967
+
+// Sha256HeaderSignature is the signature to verify that the extra
+// data block is used to store the SHA checksum.
+const Sha256HeaderSignature = 0x9514
+
// Block size used during parallel compression of a single file.
const parallelBlockSize = 1 * 1024 * 1024 // 1MB
@@ -201,6 +212,16 @@
return fmt.Sprintf("path %q is outside relative root %q", x.Path, x.RelativeRoot)
}
+type ConflictingFileError struct {
+ Dest string
+ Prev string
+ Src string
+}
+
+func (x ConflictingFileError) Error() string {
+ return fmt.Sprintf("destination %q has two files %q and %q", x.Dest, x.Prev, x.Src)
+}
+
type ZipWriter struct {
time time.Time
createdFiles map[string]string
@@ -221,6 +242,8 @@
stderr io.Writer
fs pathtools.FileSystem
+
+ sha256Checksum bool
}
type zipEntry struct {
@@ -247,6 +270,7 @@
WriteIfChanged bool
StoreSymlinks bool
IgnoreMissingFiles bool
+ Sha256Checksum bool
Stderr io.Writer
Filesystem pathtools.FileSystem
@@ -270,6 +294,7 @@
ignoreMissingFiles: args.IgnoreMissingFiles,
stderr: args.Stderr,
fs: args.Filesystem,
+ sha256Checksum: args.Sha256Checksum,
}
if z.fs == nil {
@@ -605,13 +630,24 @@
if prev, exists := z.createdDirs[dest]; exists {
return fmt.Errorf("destination %q is both a directory %q and a file %q", dest, prev, src)
}
+
+ return nil
+ }
+
+ checkDuplicateFiles := func(dest, src string) (bool, error) {
if prev, exists := z.createdFiles[dest]; exists {
- return fmt.Errorf("destination %q has two files %q and %q", dest, prev, src)
+ if prev != src {
+ return true, ConflictingFileError{
+ Dest: dest,
+ Prev: prev,
+ Src: src,
+ }
+ }
+ return true, nil
}
z.createdFiles[dest] = src
-
- return nil
+ return false, nil
}
if s.IsDir() {
@@ -625,6 +661,14 @@
return err
}
+ duplicate, err := checkDuplicateFiles(dest, src)
+ if err != nil {
+ return err
+ }
+ if duplicate {
+ return nil
+ }
+
return z.writeSymlink(dest, src)
} else if s.Mode().IsRegular() {
r, err := z.fs.Open(src)
@@ -667,6 +711,14 @@
return err
}
+ duplicate, err := checkDuplicateFiles(dest, src)
+ if err != nil {
+ return err
+ }
+ if duplicate {
+ return nil
+ }
+
return z.writeFileContents(header, r)
} else {
return fmt.Errorf("%s is not a file, directory, or symlink", src)
@@ -678,7 +730,14 @@
return fmt.Errorf("destination %q is both a directory %q and a file %q", dest, prev, src)
}
if prev, exists := z.createdFiles[dest]; exists {
- return fmt.Errorf("destination %q has two files %q and %q", dest, prev, src)
+ if prev != src {
+ return ConflictingFileError{
+ Dest: dest,
+ Prev: prev,
+ Src: src,
+ }
+ }
+ return nil
}
if err := z.writeDirectory(filepath.Dir(dest), src, true); err != nil {
@@ -738,15 +797,17 @@
// this based on actual buffer sizes in RateLimit.
ze.futureReaders = make(chan chan io.Reader, (fileSize/parallelBlockSize)+1)
- // Calculate the CRC in the background, since reading the entire
- // file could take a while.
+ // Calculate the CRC and SHA256 in the background, since reading
+ // the entire file could take a while.
//
// We could split this up into chunks as well, but it's faster
// than the compression. Due to the Go Zip API, we also need to
// know the result before we can begin writing the compressed
// data out to the zipfile.
+ //
+ // We calculate SHA256 only if `-sha256` is set.
wg.Add(1)
- go z.crcFile(r, ze, compressChan, wg)
+ go z.checksumFileAsync(r, ze, compressChan, wg)
for start := int64(0); start < fileSize; start += parallelBlockSize {
sr := io.NewSectionReader(r, start, parallelBlockSize)
@@ -785,20 +846,53 @@
return nil
}
-func (z *ZipWriter) crcFile(r io.Reader, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
+func (z *ZipWriter) checksumFileAsync(r io.ReadSeeker, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
defer wg.Done()
defer z.cpuRateLimiter.Finish()
+ z.checksumFile(r, ze)
+
+ resultChan <- ze
+ close(resultChan)
+}
+
+func (z *ZipWriter) checksumFile(r io.ReadSeeker, ze *zipEntry) {
crc := crc32.NewIEEE()
- _, err := io.Copy(crc, r)
+ writers := []io.Writer{crc}
+
+ var shaHasher hash.Hash
+ if z.sha256Checksum && !ze.fh.Mode().IsDir() {
+ shaHasher = sha256.New()
+ writers = append(writers, shaHasher)
+ }
+
+ w := io.MultiWriter(writers...)
+
+ _, err := io.Copy(w, r)
if err != nil {
z.errors <- err
return
}
ze.fh.CRC32 = crc.Sum32()
- resultChan <- ze
- close(resultChan)
+ if shaHasher != nil {
+ z.appendSHAToExtra(ze, shaHasher.Sum(nil))
+ }
+}
+
+func (z *ZipWriter) appendSHAToExtra(ze *zipEntry, checksum []byte) {
+ // The block of SHA256 checksum consist of:
+ // - Header ID, equals to Sha256HeaderID (2 bytes)
+ // - Data size (2 bytes)
+ // - Data block:
+ // - Signature, equals to Sha256HeaderSignature (2 bytes)
+ // - Data, SHA checksum value
+ var buf []byte
+ buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderID)
+ buf = binary.LittleEndian.AppendUint16(buf, uint16(len(checksum)+2))
+ buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderSignature)
+ buf = append(buf, checksum...)
+ ze.fh.Extra = append(ze.fh.Extra, buf...)
}
func (z *ZipWriter) compressPartialFile(r io.Reader, dict []byte, last bool, resultChan chan io.Reader, wg *sync.WaitGroup) {
@@ -850,17 +944,9 @@
}
func (z *ZipWriter) compressWholeFile(ze *zipEntry, r io.ReadSeeker, compressChan chan *zipEntry) {
+ z.checksumFile(r, ze)
- crc := crc32.NewIEEE()
- _, err := io.Copy(crc, r)
- if err != nil {
- z.errors <- err
- return
- }
-
- ze.fh.CRC32 = crc.Sum32()
-
- _, err = r.Seek(0, 0)
+ _, err := r.Seek(0, 0)
if err != nil {
z.errors <- err
return
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 79cc0b4..e7fdea8 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -16,6 +16,7 @@
import (
"bytes"
+ "encoding/hex"
"hash/crc32"
"io"
"os"
@@ -35,6 +36,10 @@
fileEmpty = []byte("")
fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n")
+ sha256FileA = "d53eda7a637c99cc7fb566d96e9fa109bf15c478410a3f5eb4d4c4e26cd081f6"
+ sha256FileB = "430c56c5818e62bcb6d478901ef86284e97714c138f3c86aa14fd6a84b7ce5d3"
+ sha256FileC = "31c5ab6111f1d6aa13c2c4e92bb3c0f7c76b61b42d141af1e846eb7f6586a51c"
+
fileCustomManifest = []byte("Custom manifest: true\n")
customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n")
)
@@ -46,6 +51,7 @@
"dangling -> missing": nil,
"a/a/d -> b": nil,
"c": fileC,
+ "d/a/a": nil,
"l_nl": []byte("a/a/a\na/a/b\nc\n\\[\n"),
"l_sp": []byte("a/a/a a/a/b c \\["),
"l2": []byte("missing\n"),
@@ -66,6 +72,20 @@
}
}
+func fhWithSHA256(name string, contents []byte, method uint16, sha256 string) zip.FileHeader {
+ h := fh(name, contents, method)
+ // The extra field contains 38 bytes, including 2 bytes of header ID, 2 bytes
+ // of size, 2 bytes of signature, and 32 bytes of checksum data block.
+ var extra [38]byte
+ // The first 6 bytes contains Sha256HeaderID (0x4967), size (unit(34)) and
+ // Sha256HeaderSignature (0x9514)
+ copy(extra[0:], []byte{103, 73, 34, 0, 20, 149})
+ sha256Bytes, _ := hex.DecodeString(sha256)
+ copy(extra[6:], sha256Bytes)
+ h.Extra = append(h.Extra, extra[:]...)
+ return h
+}
+
func fhManifest(contents []byte) zip.FileHeader {
return zip.FileHeader{
Name: "META-INF/MANIFEST.MF",
@@ -86,13 +106,18 @@
}
}
-func fhDir(name string) zip.FileHeader {
+type fhDirOptions struct {
+ extra []byte
+}
+
+func fhDir(name string, opts fhDirOptions) zip.FileHeader {
return zip.FileHeader{
Name: name,
Method: zip.Store,
CRC32: crc32.ChecksumIEEE(nil),
UncompressedSize64: 0,
ExternalAttrs: (syscall.S_IFDIR|0755)<<16 | 0x10,
+ Extra: opts.extra,
}
}
@@ -113,6 +138,7 @@
manifest string
storeSymlinks bool
ignoreMissingFiles bool
+ sha256Checksum bool
files []zip.FileHeader
err error
@@ -319,10 +345,10 @@
emulateJar: true,
files: []zip.FileHeader{
- fhDir("META-INF/"),
+ fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
fhManifest(fileManifest),
- fhDir("a/"),
- fhDir("a/a/"),
+ fhDir("a/", fhDirOptions{}),
+ fhDir("a/a/", fhDirOptions{}),
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
},
@@ -337,10 +363,10 @@
manifest: "manifest.txt",
files: []zip.FileHeader{
- fhDir("META-INF/"),
+ fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
fhManifest(customManifestAfter),
- fhDir("a/"),
- fhDir("a/a/"),
+ fhDir("a/", fhDirOptions{}),
+ fhDir("a/a/", fhDirOptions{}),
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
},
@@ -354,8 +380,8 @@
dirEntries: true,
files: []zip.FileHeader{
- fhDir("a/"),
- fhDir("a/a/"),
+ fhDir("a/", fhDirOptions{}),
+ fhDir("a/a/", fhDirOptions{}),
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
},
@@ -400,6 +426,34 @@
fh("a/a/b", fileB, zip.Deflate),
},
},
+ {
+ name: "duplicate sources",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/a"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ },
+ },
+ {
+ name: "generate SHA256 checksum",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("a/a/c").
+ File("c"),
+ compressionLevel: 9,
+ sha256Checksum: true,
+
+ files: []zip.FileHeader{
+ fhWithSHA256("a/a/a", fileA, zip.Deflate, sha256FileA),
+ fhWithSHA256("a/a/b", fileB, zip.Deflate, sha256FileB),
+ fhWithSHA256("a/a/c", fileC, zip.Deflate, sha256FileC),
+ fhWithSHA256("c", fileC, zip.Deflate, sha256FileC),
+ },
+ },
// errors
{
@@ -427,6 +481,15 @@
File("a/a/a"),
err: IncorrectRelativeRootError{},
},
+ {
+ name: "error conflicting file",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("a").
+ File("a/a/a").
+ SourcePrefixToStrip("d").
+ File("d/a/a"),
+ err: ConflictingFileError{},
+ },
}
for _, test := range testCases {
@@ -444,6 +507,7 @@
args.ManifestSourcePath = test.manifest
args.StoreSymlinks = test.storeSymlinks
args.IgnoreMissingFiles = test.ignoreMissingFiles
+ args.Sha256Checksum = test.sha256Checksum
args.Filesystem = mockFs
args.Stderr = &bytes.Buffer{}
@@ -454,13 +518,17 @@
t.Fatalf("want error %v, got %v", test.err, err)
} else if test.err != nil {
if os.IsNotExist(test.err) {
- if !os.IsNotExist(test.err) {
+ if !os.IsNotExist(err) {
t.Fatalf("want error %v, got %v", test.err, err)
}
} else if _, wantRelativeRootErr := test.err.(IncorrectRelativeRootError); wantRelativeRootErr {
if _, gotRelativeRootErr := err.(IncorrectRelativeRootError); !gotRelativeRootErr {
t.Fatalf("want error %v, got %v", test.err, err)
}
+ } else if _, wantConflictingFileError := test.err.(ConflictingFileError); wantConflictingFileError {
+ if _, gotConflictingFileError := err.(ConflictingFileError); !gotConflictingFileError {
+ t.Fatalf("want error %v, got %v", test.err, err)
+ }
} else {
t.Fatalf("want error %v, got %v", test.err, err)
}
@@ -530,6 +598,11 @@
t.Errorf("incorrect file %s method want %v got %v", want.Name,
want.Method, got.Method)
}
+
+ if !bytes.Equal(want.Extra, got.Extra) {
+ t.Errorf("incorrect file %s extra want %v got %v", want.Name,
+ want.Extra, got.Extra)
+ }
}
})
}