Merge "Improve error message for dupe srcs in genrule"
diff --git a/Android.bp b/Android.bp
index d6260b4..7c50047 100644
--- a/Android.bp
+++ b/Android.bp
@@ -46,24 +46,6 @@
// C static libraries extracted from the gcc toolchain
//
-toolchain_library {
- name: "libwinpthread",
- host_supported: true,
- enabled: false,
- target: {
- windows: {
- enabled: true,
- },
- windows_x86: {
- src: "prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib32/libwinpthread.a",
- },
- windows_x86_64: {
- src: "prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib/libwinpthread.a",
- },
- },
- notice: ":mingw-libwinpthread-notice",
-}
-
kernel_headers {
name: "device_kernel_headers",
vendor: true,
@@ -126,3 +108,7 @@
srcs: ["cc/config/global.go"],
out: ["clang-prebuilts-version.txt"],
}
+
+dexpreopt_systemserver_check {
+ name: "dexpreopt_systemserver_check",
+}
diff --git a/OWNERS b/OWNERS
index f0ccd82..937a243 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,12 +7,15 @@
asmundak@google.com
ccross@android.com
cparsons@google.com
+delmerico@google.com
dwillemsen@google.com
eakammer@google.com
jobredeaux@google.com
joeo@google.com
lamontjones@google.com
spandandas@google.com
+tradical@google.com
+usta@google.com
weiwli@google.com
yudiliu@google.com
yuntaoxu@google.com
diff --git a/README.md b/README.md
index a67c393..caffd3d 100644
--- a/README.md
+++ b/README.md
@@ -188,7 +188,7 @@
#### Namespaces
-A presense of the `soong_namespace {..}` in an Android.bp file defines a
+The presence of the `soong_namespace {..}` in an Android.bp file defines a
**namespace**. For instance, having
```
@@ -452,10 +452,9 @@
The values of the variables can be set from a product's `BoardConfig.mk` file:
```
-$(call add_soong_config_namespace, acme)
-$(call add_soong_config_var_value, acme, board, soc_a)
-$(call add_soong_config_var_value, acme, feature, true)
-$(call add_soong_config_var_value, acme, width, 200)
+$(call soong_config_set,acme,board,soc_a)
+$(call soong_config_set,acme,feature,true)
+$(call soong_config_set,acme,width,200)
```
The `acme_cc_defaults` module type can be used anywhere after the definition in
@@ -551,6 +550,26 @@
and produces build rules. The build rules are collected by blueprint and
written to a [ninja](http://ninja-build.org) build file.
+## Environment Variables Config File
+
+Soong can optionally load environment variables from a pre-specified
+configuration file during startup. These environment variables can be used
+to control the behavior of the build. For example, these variables can determine
+whether remote-execution should be used for the build or not.
+
+The `ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR` environment variable specifies the
+directory in which the config file should be searched for. The
+`ANDROID_BUILD_ENVIRONMENT_CONFIG` variable determines the name of the config
+file to be searched for within the config directory. For example, the following
+build comand will load `ENV_VAR_1` and `ENV_VAR_2` environment variables from
+the `example_config.json` file inside the `build/soong` directory.
+
+```
+ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR=build/soong \
+ ANDROID_BUILD_ENVIRONMENT_CONFIG=example_config \
+ build/soong/soong_ui.bash
+```
+
## Other documentation
* [Best Practices](docs/best_practices.md)
@@ -561,27 +580,41 @@
## Developing for Soong
-To load Soong code in a Go-aware IDE, create a directory outside your android tree and then:
-```bash
-apt install bindfs
-export GOPATH=<path to the directory you created>
-build/soong/scripts/setup_go_workspace_for_soong.sh
-```
+To load the code of Soong in IntelliJ:
-This will bind mount the Soong source directories into the directory in the layout expected by
-the IDE.
-
+* File -> Open, open the `build/soong` directory. It will be opened as a new
+ project.
+* File -> Settings, then Languages & Frameworks -> Go -> GOROOT, then set it to
+ `prebuilts/go/linux-x86`
+* File -> Project Structure, then, Project Settings -> Modules, then Add
+ 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
-To run the soong_build process in a debugger, install `dlv` and then start the build with
-`SOONG_DELVE=<listen addr>` in the environment.
+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=:1234 m nothing
+SOONG_DELVE=:5006 m nothing
```
-and then in another terminal:
+
+To make `soong_ui` wait for a debugger connection, use the `SOONG_UI_DELVE`
+variable:
+
```
-dlv connect :1234
+SOONG_UI_DELVE=:5006 m nothing
+```
+
+
+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.
+
+To test the debugger connection, run this command:
+
+```
+dlv connect :5006
```
If you see an error:
@@ -596,6 +629,21 @@
sudo sysctl -w kernel.yama.ptrace_scope=0
```
+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
+
+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.
+Anecdotally, it _feels_ like waiting a minute after the start of `soong_build`
+helps.
+
## Contact
Email android-building@googlegroups.com (external) for any questions, or see
diff --git a/android/Android.bp b/android/Android.bp
index 6450a06..d3540b2 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -16,9 +16,14 @@
"soong-remoteexec",
"soong-response",
"soong-shared",
+ "soong-starlark-format",
"soong-ui-metrics_proto",
+
"golang-protobuf-proto",
"golang-protobuf-encoding-prototext",
+
+ // Only used for tests.
+ "androidmk-parser",
],
srcs: [
"androidmk.go",
@@ -44,6 +49,7 @@
"image.go",
"license.go",
"license_kind.go",
+ "license_metadata.go",
"license_sdk_member.go",
"licenses.go",
"makefile_goal.go",
@@ -110,6 +116,7 @@
"paths_test.go",
"prebuilt_test.go",
"rule_builder_test.go",
+ "sdk_version_test.go",
"sdk_test.go",
"singleton_module_test.go",
"soong_config_modules_test.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 967c550..72b6584 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -25,15 +25,16 @@
"bytes"
"fmt"
"io"
- "io/ioutil"
"os"
"path/filepath"
"reflect"
+ "runtime"
"sort"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
+ "github.com/google/blueprint/pathtools"
)
func init() {
@@ -113,7 +114,7 @@
// If true, the module is skipped and does not appear on the final Android-<product name>.mk
// file. Useful when a module needs to be skipped conditionally.
Disabled bool
- // The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_prebuilt.mk
+ // The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk
// If not set, $(BUILD_SYSTEM)/prebuilt.mk is used.
Include string
// Required modules that need to be built and included in the final build output when building
@@ -473,12 +474,14 @@
ModuleDir(module blueprint.Module) string
Config() Config
ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
+ ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
}
func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
a.EntryMap = make(map[string][]string)
- amod := mod.(Module).base()
- name := amod.BaseModuleName()
+ amod := mod.(Module)
+ base := amod.base()
+ name := base.BaseModuleName()
if a.OverrideName != "" {
name = a.OverrideName
}
@@ -486,9 +489,9 @@
if a.Include == "" {
a.Include = "$(BUILD_PREBUILT)"
}
- a.Required = append(a.Required, mod.(Module).RequiredModuleNames()...)
- a.Host_required = append(a.Host_required, mod.(Module).HostRequiredModuleNames()...)
- a.Target_required = append(a.Target_required, mod.(Module).TargetRequiredModuleNames()...)
+ a.Required = append(a.Required, amod.RequiredModuleNames()...)
+ a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
+ a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
for _, distString := range a.GetDistForGoals(mod) {
fmt.Fprintf(&a.header, distString)
@@ -499,14 +502,14 @@
// Collect make variable assignment entries.
a.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
a.SetString("LOCAL_MODULE", name+a.SubName)
- a.AddStrings("LOCAL_LICENSE_KINDS", amod.commonProperties.Effective_license_kinds...)
- a.AddStrings("LOCAL_LICENSE_CONDITIONS", amod.commonProperties.Effective_license_conditions...)
- a.AddStrings("LOCAL_NOTICE_FILE", amod.commonProperties.Effective_license_text.Strings()...)
+ a.AddStrings("LOCAL_LICENSE_KINDS", base.commonProperties.Effective_license_kinds...)
+ a.AddStrings("LOCAL_LICENSE_CONDITIONS", base.commonProperties.Effective_license_conditions...)
+ a.AddStrings("LOCAL_NOTICE_FILE", base.commonProperties.Effective_license_text.Strings()...)
// TODO(b/151177513): Does this code need to set LOCAL_MODULE_IS_CONTAINER ?
- if amod.commonProperties.Effective_package_name != nil {
- a.SetString("LOCAL_LICENSE_PACKAGE_NAME", *amod.commonProperties.Effective_package_name)
- } else if len(amod.commonProperties.Effective_licenses) > 0 {
- a.SetString("LOCAL_LICENSE_PACKAGE_NAME", strings.Join(amod.commonProperties.Effective_licenses, " "))
+ if base.commonProperties.Effective_package_name != nil {
+ a.SetString("LOCAL_LICENSE_PACKAGE_NAME", *base.commonProperties.Effective_package_name)
+ } else if len(base.commonProperties.Effective_licenses) > 0 {
+ a.SetString("LOCAL_LICENSE_PACKAGE_NAME", strings.Join(base.commonProperties.Effective_licenses, " "))
}
a.SetString("LOCAL_MODULE_CLASS", a.Class)
a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
@@ -514,31 +517,41 @@
a.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
a.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
+ // If the install rule was generated by Soong tell Make about it.
+ if len(base.katiInstalls) > 0 {
+ // Assume the primary install file is last since it probably needs to depend on any other
+ // installed files. If that is not the case we can add a method to specify the primary
+ // installed file.
+ a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", base.katiInstalls[len(base.katiInstalls)-1].to)
+ a.SetString("LOCAL_SOONG_INSTALL_PAIRS", base.katiInstalls.BuiltInstalled())
+ a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", base.katiSymlinks.InstallPaths().Paths())
+ }
+
if am, ok := mod.(ApexModule); ok {
a.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
}
- archStr := amod.Arch().ArchType.String()
+ archStr := base.Arch().ArchType.String()
host := false
- switch amod.Os().Class {
+ switch base.Os().Class {
case Host:
- if amod.Target().HostCross {
+ if base.Target().HostCross {
// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
- if amod.Arch().ArchType != Common {
+ if base.Arch().ArchType != Common {
a.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
}
} else {
// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
- if amod.Arch().ArchType != Common {
+ if base.Arch().ArchType != Common {
a.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
}
}
host = true
case Device:
// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
- if amod.Arch().ArchType != Common {
- if amod.Target().NativeBridge {
- hostArchStr := amod.Target().NativeBridgeHostArchName
+ if base.Arch().ArchType != Common {
+ if base.Target().NativeBridge {
+ hostArchStr := base.Target().NativeBridgeHostArchName
if hostArchStr != "" {
a.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
}
@@ -547,31 +560,31 @@
}
}
- if !amod.InRamdisk() && !amod.InVendorRamdisk() {
- a.AddPaths("LOCAL_FULL_INIT_RC", amod.initRcPaths)
+ if !base.InRamdisk() && !base.InVendorRamdisk() {
+ a.AddPaths("LOCAL_FULL_INIT_RC", base.initRcPaths)
}
- if len(amod.vintfFragmentsPaths) > 0 {
- a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", amod.vintfFragmentsPaths)
+ if len(base.vintfFragmentsPaths) > 0 {
+ a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", base.vintfFragmentsPaths)
}
- a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(amod.commonProperties.Proprietary))
- if Bool(amod.commonProperties.Vendor) || Bool(amod.commonProperties.Soc_specific) {
+ a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary))
+ if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) {
a.SetString("LOCAL_VENDOR_MODULE", "true")
}
- a.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(amod.commonProperties.Device_specific))
- a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(amod.commonProperties.Product_specific))
- a.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(amod.commonProperties.System_ext_specific))
- if amod.commonProperties.Owner != nil {
- a.SetString("LOCAL_MODULE_OWNER", *amod.commonProperties.Owner)
+ a.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(base.commonProperties.Device_specific))
+ a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(base.commonProperties.Product_specific))
+ a.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(base.commonProperties.System_ext_specific))
+ if base.commonProperties.Owner != nil {
+ a.SetString("LOCAL_MODULE_OWNER", *base.commonProperties.Owner)
}
}
- if len(amod.noticeFiles) > 0 {
- a.SetString("LOCAL_NOTICE_FILE", strings.Join(amod.noticeFiles.Strings(), " "))
+ if len(base.noticeFiles) > 0 {
+ a.SetString("LOCAL_NOTICE_FILE", strings.Join(base.noticeFiles.Strings(), " "))
}
if host {
- makeOs := amod.Os().String()
- if amod.Os() == Linux || amod.Os() == LinuxBionic || amod.Os() == LinuxMusl {
+ makeOs := base.Os().String()
+ if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
makeOs = "linux"
}
a.SetString("LOCAL_MODULE_HOST_OS", makeOs)
@@ -579,10 +592,10 @@
}
prefix := ""
- if amod.ArchSpecific() {
- switch amod.Os().Class {
+ if base.ArchSpecific() {
+ switch base.Os().Class {
case Host:
- if amod.Target().HostCross {
+ if base.Target().HostCross {
prefix = "HOST_CROSS_"
} else {
prefix = "HOST_"
@@ -592,11 +605,16 @@
}
- if amod.Arch().ArchType != ctx.Config().Targets[amod.Os()][0].Arch.ArchType {
+ if base.Arch().ArchType != ctx.Config().Targets[base.Os()][0].Arch.ArchType {
prefix = "2ND_" + prefix
}
}
+ if ctx.ModuleHasProvider(mod, LicenseMetadataProvider) {
+ licenseMetadata := ctx.ModuleProvider(mod, LicenseMetadataProvider).(*LicenseMetadataInfo)
+ a.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
+ }
+
extraCtx := &androidMkExtraEntriesContext{
ctx: ctx,
mod: mod,
@@ -678,7 +696,7 @@
})
}
-func translateAndroidMk(ctx SingletonContext, mkFile string, mods []blueprint.Module) error {
+func translateAndroidMk(ctx SingletonContext, absMkFile string, mods []blueprint.Module) error {
buf := &bytes.Buffer{}
fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
@@ -687,7 +705,7 @@
for _, mod := range mods {
err := translateAndroidMkModule(ctx, buf, mod)
if err != nil {
- os.Remove(mkFile)
+ os.Remove(absMkFile)
return err
}
@@ -707,27 +725,7 @@
fmt.Fprintf(buf, "STATS.SOONG_MODULE_TYPE.%s := %d\n", mod_type, typeStats[mod_type])
}
- // Don't write to the file if it hasn't changed
- if _, err := os.Stat(absolutePath(mkFile)); !os.IsNotExist(err) {
- if data, err := ioutil.ReadFile(absolutePath(mkFile)); err == nil {
- matches := buf.Len() == len(data)
-
- if matches {
- for i, value := range buf.Bytes() {
- if value != data[i] {
- matches = false
- break
- }
- }
- }
-
- if matches {
- return nil
- }
- }
- }
-
- return ioutil.WriteFile(absolutePath(mkFile), buf.Bytes(), 0666)
+ return pathtools.WriteFileIfChanged(absMkFile, buf.Bytes(), 0666)
}
func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
@@ -836,6 +834,7 @@
case "*aidl.aidlApi": // writes non-custom before adding .phony
case "*aidl.aidlMapping": // writes non-custom before adding .phony
case "*android.customModule": // appears in tests only
+ case "*android_sdk.sdkRepoHost": // doesn't go through base_rules
case "*apex.apexBundle": // license properties written
case "*bpf.bpf": // license properties written (both for module and objs)
case "*genrule.Module": // writes non-custom before adding .phony
@@ -894,6 +893,10 @@
return nil
}
+func ShouldSkipAndroidMkProcessing(module Module) bool {
+ return shouldSkipAndroidMkProcessing(module.base())
+}
+
func shouldSkipAndroidMkProcessing(module *ModuleBase) bool {
if !module.commonProperties.NamespaceExportedToMake {
// TODO(jeffrygaston) do we want to validate that there are no modules being
@@ -901,6 +904,18 @@
return true
}
+ // On Mac, only expose host darwin modules to Make, as that's all we claim to support.
+ // In reality, some of them depend on device-built (Java) modules, so we can't disable all
+ // device modules in Soong, but we can hide them from Make (and thus the build user interface)
+ if runtime.GOOS == "darwin" && module.Os() != Darwin {
+ return true
+ }
+
+ // Only expose the primary Darwin target, as Make does not understand Darwin+Arm64
+ if module.Os() == Darwin && module.Target().HostCross {
+ return true
+ }
+
return !module.Enabled() ||
module.commonProperties.HideFromMake ||
// Make does not understand LinuxBionic
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index 8eda9b2..ecfb008 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -18,6 +18,7 @@
"fmt"
"io"
"reflect"
+ "runtime"
"strings"
"testing"
@@ -155,6 +156,11 @@
}
func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
+ if runtime.GOOS == "darwin" {
+ // Device modules are not exported on Mac, so this test doesn't work.
+ t.SkipNow()
+ }
+
bp := `
custom {
name: "foo",
diff --git a/android/apex.go b/android/apex.go
index b9efe4e..cf1bcfe 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -854,7 +854,6 @@
}
return list
}(map[string]int{
- "adbd": 30,
"android.net.ipsec.ike": 30,
"androidx.annotation_annotation-nodeps": 29,
"androidx.arch.core_core-common-nodeps": 29,
@@ -879,24 +878,8 @@
"kotlinx-coroutines-android-nodeps": 30,
"kotlinx-coroutines-core": 28,
"kotlinx-coroutines-core-nodeps": 30,
- "libadb_crypto": 30,
- "libadb_pairing_auth": 30,
- "libadb_pairing_connection": 30,
- "libadb_pairing_server": 30,
- "libadb_protos": 30,
- "libadb_tls_connection": 30,
- "libadbconnection_client": 30,
- "libadbconnection_server": 30,
- "libadbd_core": 30,
- "libadbd_services": 30,
- "libadbd": 30,
- "libapp_processes_protos_lite": 30,
- "libasyncio": 30,
"libbrotli": 30,
- "libbuildversion": 30,
"libcrypto_static": 30,
- "libcrypto_utils": 30,
- "libdiagnose_usb": 30,
"libeigen": 30,
"liblz4": 30,
"libmdnssd": 30,
@@ -906,7 +889,6 @@
"libprocpartition": 30,
"libprotobuf-java-lite": 30,
"libprotoutil": 30,
- "libsync": 30,
"libtextclassifier_hash_headers": 30,
"libtextclassifier_hash_static": 30,
"libtflite_kernel_utils": 30,
@@ -926,16 +908,18 @@
//
// Return true if the `to` module should be visited, false otherwise.
type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
+type WalkPayloadDepsFunc func(ctx ModuleContext, do PayloadDepsCallback)
-// UpdatableModule represents updatable APEX/APK
-type UpdatableModule interface {
+// ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks
+type ModuleWithMinSdkVersionCheck interface {
Module
- WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback)
+ MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+ CheckMinSdkVersion(ctx ModuleContext)
}
// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version
// accordingly
-func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) {
+func CheckMinSdkVersion(ctx ModuleContext, minSdkVersion ApiLevel, walk WalkPayloadDepsFunc) {
// do not enforce min_sdk_version for host
if ctx.Host() {
return
@@ -951,7 +935,7 @@
return
}
- m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
+ walk(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
if externalDep {
// external deps are outside the payload boundary, which is "stable"
// interface. We don't have to check min_sdk_version for external
@@ -961,6 +945,14 @@
if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
return false
}
+ if m, ok := to.(ModuleWithMinSdkVersionCheck); ok {
+ // This dependency performs its own min_sdk_version check, just make sure it sets min_sdk_version
+ // to trigger the check.
+ if !m.MinSdkVersion(ctx).Specified() {
+ ctx.OtherModuleErrorf(m, "must set min_sdk_version")
+ }
+ return false
+ }
if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
toName := ctx.OtherModuleName(to)
if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
diff --git a/android/api_levels.go b/android/api_levels.go
index 84ab27c..926d297 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -177,6 +177,10 @@
// libandroid_support.
var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21)
+// LastWithoutModuleLibCoreSystemModules is the last API level where prebuilts/sdk does not contain
+// 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.
@@ -188,8 +192,8 @@
// * "30" -> "30"
// * "R" -> "30"
// * "S" -> "S"
-func ReplaceFinalizedCodenames(ctx PathContext, raw string) string {
- num, ok := getFinalCodenamesMap(ctx.Config())[raw]
+func ReplaceFinalizedCodenames(config Config, raw string) string {
+ num, ok := getFinalCodenamesMap(config)[raw]
if !ok {
return raw
}
@@ -197,7 +201,7 @@
return strconv.Itoa(num)
}
-// Converts the given string `raw` to an ApiLevel, possibly returning an error.
+// 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.
//
@@ -212,6 +216,12 @@
// Inputs that are not "current", known previews, or convertible to an integer
// will return an error.
func ApiLevelFromUser(ctx PathContext, raw string) (ApiLevel, error) {
+ return ApiLevelFromUserWithConfig(ctx.Config(), raw)
+}
+
+// ApiLevelFromUserWithConfig implements ApiLevelFromUser, see comments for
+// ApiLevelFromUser for more details.
+func ApiLevelFromUserWithConfig(config Config, raw string) (ApiLevel, error) {
if raw == "" {
panic("API level string must be non-empty")
}
@@ -220,13 +230,13 @@
return FutureApiLevel, nil
}
- for _, preview := range ctx.Config().PreviewApiLevels() {
+ for _, preview := range config.PreviewApiLevels() {
if raw == preview.String() {
return preview, nil
}
}
- canonical := ReplaceFinalizedCodenames(ctx, raw)
+ 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)
@@ -236,6 +246,27 @@
return apiLevel, nil
}
+// ApiLevelForTest returns an ApiLevel constructed from the supplied raw string.
+//
+// This only supports "current" and numeric levels, code names are not supported.
+func ApiLevelForTest(raw string) ApiLevel {
+ if raw == "" {
+ panic("API level string must be non-empty")
+ }
+
+ if raw == "current" {
+ return FutureApiLevel
+ }
+
+ asInt, err := strconv.Atoi(raw)
+ if err != nil {
+ panic(fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", raw))
+ }
+
+ apiLevel := uncheckedFinalApiLevel(asInt)
+ return apiLevel
+}
+
// Converts an API level string `raw` into an ApiLevel in the same method as
// `ApiLevelFromUser`, but the input is assumed to have no errors and any errors
// will panic instead of returning an error.
@@ -289,6 +320,7 @@
"P": 28,
"Q": 29,
"R": 30,
+ "S": 31,
}
// TODO: Differentiate "current" and "future".
@@ -312,7 +344,7 @@
var apiLevelsMapKey = NewOnceKey("ApiLevelsMap")
-func getApiLevelsMap(config Config) map[string]int {
+func GetApiLevelsMap(config Config) map[string]int {
return config.Once(apiLevelsMapKey, func() interface{} {
apiLevelsMap := map[string]int{
"G": 9,
@@ -331,6 +363,7 @@
"P": 28,
"Q": 29,
"R": 30,
+ "S": 31,
}
for i, codename := range config.PlatformVersionActiveCodenames() {
apiLevelsMap[codename] = previewAPILevelBase + i
@@ -341,7 +374,7 @@
}
func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {
- apiLevelsMap := getApiLevelsMap(ctx.Config())
+ apiLevelsMap := GetApiLevelsMap(ctx.Config())
apiLevelsJson := GetApiLevelsJson(ctx)
createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap)
}
diff --git a/android/arch.go b/android/arch.go
index 54242e5..a719cf3 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -168,7 +168,7 @@
return archType
}
-// ArchTypeList returns the a slice copy of the 4 supported ArchTypes for arm,
+// ArchTypeList returns a slice copy of the 4 supported ArchTypes for arm,
// arm64, x86 and x86_64.
func ArchTypeList() []ArchType {
return append([]ArchType(nil), archTypeList...)
@@ -308,7 +308,7 @@
// LinuxMusl is the OS for the Linux kernel plus the musl runtime.
LinuxMusl = newOsType("linux_musl", Host, false, X86, X86_64)
// Darwin is the OS for MacOS/Darwin host machines.
- Darwin = newOsType("darwin", Host, false, X86_64)
+ Darwin = newOsType("darwin", Host, false, Arm64, X86_64)
// LinuxBionic is the OS for the Linux kernel plus the Bionic libc runtime, but without the
// rest of Android.
LinuxBionic = newOsType("linux_bionic", Host, false, Arm64, X86_64)
@@ -408,7 +408,7 @@
// addPathDepsForProps does not descend into sub structs, so we need to descend into the
// arch-specific properties ourselves
- properties := []interface{}{}
+ var properties []interface{}
for _, archProperties := range m.archProperties {
for _, archProps := range archProperties {
archPropValues := reflect.ValueOf(archProps).Elem()
@@ -566,6 +566,8 @@
return variants
}
+var DarwinUniversalVariantTag = archDepTag{name: "darwin universal binary"}
+
// archMutator splits a module into a variant for each Target requested by the module. Target selection
// for a module is in three levels, OsClass, multilib, and then Target.
// OsClass selection is determined by:
@@ -652,7 +654,7 @@
prefer32 := os == Windows
// Determine the multilib selection for this module.
- multilib, extraMultilib := decodeMultilib(base, os.Class)
+ multilib, extraMultilib := decodeMultilib(base, os)
// Convert the multilib selection into a list of Targets.
targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
@@ -696,6 +698,21 @@
for i, m := range modules {
addTargetProperties(m, targets[i], multiTargets, i == 0)
m.base().setArchProperties(mctx)
+
+ // Install support doesn't understand Darwin+Arm64
+ if os == Darwin && targets[i].HostCross {
+ m.base().commonProperties.SkipInstall = true
+ }
+ }
+
+ // Create a dependency for Darwin Universal binaries from the primary to secondary
+ // architecture. The module itself will be responsible for calling lipo to merge the outputs.
+ if os == Darwin {
+ if multilib == "darwin_universal" && len(modules) == 2 {
+ mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[1], modules[0])
+ } else if multilib == "darwin_universal_common_first" && len(modules) == 3 {
+ mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[2], modules[1])
+ }
}
}
@@ -712,9 +729,9 @@
// 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, class OsClass) (multilib, extraMultilib string) {
+func decodeMultilib(base *ModuleBase, os OsType) (multilib, extraMultilib string) {
// First check the "android.compile_multilib" or "host.compile_multilib" properties.
- switch class {
+ switch os.Class {
case Device:
multilib = String(base.commonProperties.Target.Android.Compile_multilib)
case Host:
@@ -732,6 +749,26 @@
}
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
+ // merge the outputs of their corresponding variants together into a final binary. Most
+ // module types don't need to understand this logic, as we only build a small portion
+ // of the tree for Darwin, and only module types writing macho files need to do the
+ // merging.
+ //
+ // This logic is not enabled for:
+ // "common", as it's not an arch-specific variant
+ // "32", as Darwin never has a 32-bit variant
+ // !UseTargetVariants, as the module has opted into handling the arch-specific logic on
+ // its own.
+ if os == Darwin && multilib != "common" && multilib != "32" {
+ if multilib == "common_first" {
+ multilib = "darwin_universal_common_first"
+ } else {
+ multilib = "darwin_universal"
+ }
+ }
+
return multilib, ""
} else {
// For app modules a single arch variant will be created per OS class which is expected to handle all the
@@ -880,7 +917,8 @@
for _, archType := range osArchTypeMap[os] {
targets = append(targets, GetCompoundTargetField(os, archType))
- // Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
+ // Also add the special "linux_<arch>", "bionic_<arch>" , "glibc_<arch>", and
+ // "musl_<arch>" property structs.
if os.Linux() {
target := "Linux_" + archType.Name
if !InList(target, targets) {
@@ -893,6 +931,18 @@
targets = append(targets, target)
}
}
+ if os == Linux {
+ target := "Glibc_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
+ }
+ if os == LinuxMusl {
+ target := "Musl_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
+ }
}
}
@@ -956,10 +1006,11 @@
base := m.base()
- // Store the original list of top level property structs
- base.generalProperties = m.GetProperties()
+ if len(base.archProperties) != 0 {
+ panic(fmt.Errorf("module %s already has archProperties", m.Name()))
+ }
- for _, properties := range base.generalProperties {
+ getStructType := func(properties interface{}) reflect.Type {
propertiesValue := reflect.ValueOf(properties)
t := propertiesValue.Type()
if propertiesValue.Kind() != reflect.Ptr {
@@ -969,10 +1020,14 @@
propertiesValue = propertiesValue.Elem()
if propertiesValue.Kind() != reflect.Struct {
- panic(fmt.Errorf("properties must be a pointer to a struct, got %T",
+ panic(fmt.Errorf("properties must be a pointer to a struct, got a pointer to %T",
propertiesValue.Interface()))
}
+ return t
+ }
+ for _, properties := range m.GetProperties() {
+ t := getStructType(properties)
// Get or create the arch-specific property struct types for this property struct type.
archPropTypes := archPropTypeMap.Once(NewCustomOnceKey(t), func() interface{} {
return createArchPropTypeDesc(t)
@@ -992,9 +1047,6 @@
m.AddProperties(archProperties...)
}
- // Update the list of properties that can be set by a defaults module or a call to
- // AppendMatchingProperties or PrependMatchingProperties.
- base.customizableProperties = m.GetProperties()
}
func maybeBlueprintEmbed(src reflect.Value) reflect.Value {
@@ -1067,8 +1119,8 @@
func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) {
os := m.commonProperties.CompileOS
- for i := range m.generalProperties {
- genProps := m.generalProperties[i]
+ for i := range m.archProperties {
+ genProps := m.GetProperties()[i]
if m.archProperties[i] == nil {
continue
}
@@ -1340,11 +1392,25 @@
result = append(result, osArchProperties)
}
+ if os == Linux {
+ field := "Glibc_" + archType.Name
+ userFriendlyField := "target.glibc_" + "_" + archType.Name
+ if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+ result = append(result, osArchProperties)
+ }
+ }
+
if os == LinuxMusl {
+ field := "Musl_" + archType.Name
+ userFriendlyField := "target.musl_" + "_" + archType.Name
+ if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+ result = append(result, osArchProperties)
+ }
+
// Special case: to ease the transition from glibc to musl, apply linux_glibc
// properties (which has historically mean host linux) to musl variants.
- field := "Linux_glibc_" + archType.Name
- userFriendlyField := "target.linux_glibc_" + archType.Name
+ field = "Linux_glibc_" + archType.Name
+ userFriendlyField = "target.linux_glibc_" + archType.Name
if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
result = append(result, osArchProperties)
}
@@ -1395,8 +1461,8 @@
arch := m.Arch()
os := m.Os()
- for i := range m.generalProperties {
- genProps := m.generalProperties[i]
+ for i := range m.archProperties {
+ genProps := m.GetProperties()[i]
if m.archProperties[i] == nil {
continue
}
@@ -1587,10 +1653,12 @@
return PrefixInList(arch.Abi, "arm")
}
-// hasArmArch returns true if targets has at least non-native_bridge arm Android arch
+// hasArmAndroidArch returns true if targets has at least
+// one arm Android arch (possibly native bridged)
func hasArmAndroidArch(targets []Target) bool {
for _, target := range targets {
- if target.Os == Android && target.Arch.ArchType == Arm {
+ if target.Os == Android &&
+ (target.Arch.ArchType == Arm || target.Arch.ArchType == Arm64) {
return true
}
}
@@ -1786,6 +1854,15 @@
if len(buildTargets) == 0 {
buildTargets = filterMultilibTargets(targets, "lib64")
}
+ case "darwin_universal":
+ buildTargets = filterMultilibTargets(targets, "lib64")
+ // Reverse the targets so that the first architecture can depend on the second
+ // architecture module in order to merge the outputs.
+ reverseSliceInPlace(buildTargets)
+ case "darwin_universal_common_first":
+ archTargets := filterMultilibTargets(targets, "lib64")
+ reverseSliceInPlace(archTargets)
+ buildTargets = append(getCommonTargets(targets), archTargets...)
default:
return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", "prefer32" or "first_prefer32" found %q`,
multilib)
@@ -1963,8 +2040,8 @@
var archProperties []interface{}
// First find the property set in the module that corresponds to the requested
- // one. m.archProperties[i] corresponds to m.generalProperties[i].
- for i, generalProp := range m.generalProperties {
+ // one. m.archProperties[i] corresponds to m.GetProperties()[i].
+ for i, generalProp := range m.GetProperties() {
srcType := reflect.ValueOf(generalProp).Type()
if srcType == dstType {
archProperties = m.archProperties[i]
@@ -2006,17 +2083,10 @@
osToProp := ArchVariantProperties{}
archOsToProp := ArchVariantProperties{}
- var linuxStructs, bionicStructs []reflect.Value
- var ok bool
-
- linuxStructs, ok = getTargetStructs(ctx, archProperties, "Linux")
- if !ok {
- linuxStructs = make([]reflect.Value, 0)
- }
- bionicStructs, ok = getTargetStructs(ctx, archProperties, "Bionic")
- if !ok {
- bionicStructs = make([]reflect.Value, 0)
- }
+ linuxStructs := getTargetStructs(ctx, archProperties, "Linux")
+ bionicStructs := getTargetStructs(ctx, archProperties, "Bionic")
+ hostStructs := getTargetStructs(ctx, archProperties, "Host")
+ hostNotWindowsStructs := getTargetStructs(ctx, archProperties, "Not_windows")
// For android, linux, ...
for _, os := range osTypeList {
@@ -2025,9 +2095,10 @@
continue
}
osStructs := make([]reflect.Value, 0)
- osSpecificStructs, ok := getTargetStructs(ctx, archProperties, os.Field)
- if ok {
- osStructs = append(osStructs, osSpecificStructs...)
+
+ osSpecificStructs := getTargetStructs(ctx, archProperties, os.Field)
+ if os.Class == Host {
+ osStructs = append(osStructs, hostStructs...)
}
if os.Linux() {
osStructs = append(osStructs, linuxStructs...)
@@ -2035,37 +2106,44 @@
if os.Bionic() {
osStructs = append(osStructs, bionicStructs...)
}
+
+ if os == LinuxMusl {
+ osStructs = append(osStructs, getTargetStructs(ctx, archProperties, "Musl")...)
+ }
+ if os == Linux {
+ osStructs = append(osStructs, getTargetStructs(ctx, archProperties, "Glibc")...)
+ }
+
+ osStructs = append(osStructs, osSpecificStructs...)
+
+ if os.Class == Host && os != Windows {
+ osStructs = append(osStructs, hostNotWindowsStructs...)
+ }
osToProp[os.Name] = mergeStructs(ctx, osStructs, propertySet)
// For arm, x86, ...
for _, arch := range osArchTypeMap[os] {
osArchStructs := make([]reflect.Value, 0)
- targetField := GetCompoundTargetField(os, arch)
- targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
- targetStructs, ok := getTargetStructs(ctx, archProperties, targetField)
- if ok {
- osArchStructs = append(osArchStructs, targetStructs...)
- }
-
// Auto-combine with Linux_ and Bionic_ targets. This potentially results in
// repetition and select() bloat, but use of Linux_* and Bionic_* targets is rare.
// TODO(b/201423152): Look into cleanup.
if os.Linux() {
targetField := "Linux_" + arch.Name
- targetStructs, ok := getTargetStructs(ctx, archProperties, targetField)
- if ok {
- osArchStructs = append(osArchStructs, targetStructs...)
- }
+ targetStructs := getTargetStructs(ctx, archProperties, targetField)
+ osArchStructs = append(osArchStructs, targetStructs...)
}
if os.Bionic() {
targetField := "Bionic_" + arch.Name
- targetStructs, ok := getTargetStructs(ctx, archProperties, targetField)
- if ok {
- osArchStructs = append(osArchStructs, targetStructs...)
- }
+ targetStructs := getTargetStructs(ctx, archProperties, targetField)
+ osArchStructs = append(osArchStructs, targetStructs...)
}
+ targetField := GetCompoundTargetField(os, arch)
+ targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
+ targetStructs := getTargetStructs(ctx, archProperties, targetField)
+ osArchStructs = append(osArchStructs, targetStructs...)
+
archOsToProp[targetName] = mergeStructs(ctx, osArchStructs, propertySet)
}
}
@@ -2089,8 +2167,8 @@
// }
// }
// This would return a BaseCompilerProperties with BaseCompilerProperties.Srcs = ["foo.c"]
-func getTargetStructs(ctx ArchVariantContext, archProperties []interface{}, targetName string) ([]reflect.Value, bool) {
- propertyStructs := make([]reflect.Value, 0)
+func getTargetStructs(ctx ArchVariantContext, archProperties []interface{}, targetName string) []reflect.Value {
+ var propertyStructs []reflect.Value
for _, archProperty := range archProperties {
archPropValues := reflect.ValueOf(archProperty).Elem()
targetProp := archPropValues.FieldByName("Target").Elem()
@@ -2098,11 +2176,11 @@
if ok {
propertyStructs = append(propertyStructs, targetStruct)
} else {
- return propertyStructs, false
+ return []reflect.Value{}
}
}
- return propertyStructs, true
+ return propertyStructs
}
func mergeStructs(ctx ArchVariantContext, propertyStructs []reflect.Value, propertySet interface{}) interface{} {
diff --git a/android/bazel.go b/android/bazel.go
index 6aba759..f4fc038 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -15,6 +15,8 @@
package android
import (
+ "bufio"
+ "errors"
"fmt"
"io/ioutil"
"path/filepath"
@@ -39,6 +41,10 @@
// 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
+
+ // CanConvertToBazel is set via InitBazelModule to indicate that a module type can be converted to
+ // Bazel with Bp2build.
+ CanConvertToBazel bool `blueprint:"mutated"`
}
// Properties contains common module properties for Bazel migration purposes.
@@ -48,10 +54,30 @@
Bazel_module bazelModuleProperties
}
+// namespacedVariableProperties is a map from a string representing a Soong
+// config variable namespace, like "android" or "vendor_name" to a slice of
+// pointer to a struct containing a single field called Soong_config_variables
+// whose value mirrors the structure in the Blueprint file.
+type namespacedVariableProperties map[string][]interface{}
+
// BazelModuleBase contains the property structs with metadata for modules which can be converted to
// Bazel.
type BazelModuleBase struct {
bazelProperties properties
+
+ // namespacedVariableProperties is used for soong_config_module_type support
+ // in bp2build. Soong config modules allow users to set module properties
+ // based on custom product variables defined in Android.bp files. These
+ // variables are namespaced to prevent clobbering, especially when set from
+ // Makefiles.
+ namespacedVariableProperties namespacedVariableProperties
+
+ // baseModuleType is set when this module was created from a module type
+ // defined by a soong_config_module_type. Every soong_config_module_type
+ // "wraps" another module type, e.g. a soong_config_module_type can wrap a
+ // cc_defaults to a custom_cc_defaults, or cc_binary to a custom_cc_binary.
+ // This baseModuleType is set to the wrapped module type.
+ baseModuleType string
}
// Bazelable is specifies the interface for modules that can be converted to Bazel.
@@ -60,9 +86,24 @@
HasHandcraftedLabel() bool
HandcraftedLabel() string
GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
- ConvertWithBp2build(ctx BazelConversionPathContext) bool
- convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool
+ ShouldConvertWithBp2build(ctx BazelConversionContext) bool
+ shouldConvertWithBp2build(ctx BazelConversionContext, 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
+ // (e.g. acme, android) to a map of interfaces{}, which are really
+ // reflect.Struct pointers, representing the value of the
+ // soong_config_variables property of a module. The struct pointer is the
+ // 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
+ // propagation of the struct pointers.
+ namespacedVariableProps() namespacedVariableProperties
+ setNamespacedVariableProps(props namespacedVariableProperties)
+ BaseModuleType() string
+ SetBaseModuleType(baseModuleType string)
}
// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
@@ -75,6 +116,7 @@
// properties.
func InitBazelModule(module BazelModule) {
module.AddProperties(module.bazelProps())
+ module.bazelProps().Bazel_module.CanConvertToBazel = true
}
// bazelProps returns the Bazel properties for the given BazelModuleBase.
@@ -82,6 +124,22 @@
return &b.bazelProperties
}
+func (b *BazelModuleBase) namespacedVariableProps() namespacedVariableProperties {
+ return b.namespacedVariableProperties
+}
+
+func (b *BazelModuleBase) setNamespacedVariableProps(props namespacedVariableProperties) {
+ b.namespacedVariableProperties = props
+}
+
+func (b *BazelModuleBase) BaseModuleType() string {
+ return b.baseModuleType
+}
+
+func (b *BazelModuleBase) SetBaseModuleType(baseModuleType string) {
+ b.baseModuleType = baseModuleType
+}
+
// HasHandcraftedLabel returns whether this module has a handcrafted Bazel label.
func (b *BazelModuleBase) HasHandcraftedLabel() bool {
return b.bazelProperties.Bazel_module.Label != nil
@@ -97,7 +155,7 @@
if b.HasHandcraftedLabel() {
return b.HandcraftedLabel()
}
- if b.ConvertWithBp2build(ctx) {
+ if b.ShouldConvertWithBp2build(ctx) {
return bp2buildModuleLabel(ctx, module)
}
return "" // no label for unconverted module
@@ -140,6 +198,7 @@
// 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,
@@ -150,7 +209,8 @@
"build/bazel/platforms":/* recursive = */ true,
"build/bazel/product_variables":/* recursive = */ true,
"build/bazel_common_rules":/* recursive = */ true,
- "build/make/tools":/* recursive = */ true,
+ // build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
+ "build/make/tools":/* recursive = */ false,
"build/pesto":/* recursive = */ true,
// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
@@ -158,7 +218,6 @@
"external/bazelbuild-rules_android":/* recursive = */ true,
"external/bazel-skylib":/* recursive = */ true,
"external/guava":/* recursive = */ true,
- "external/error_prone":/* recursive = */ true,
"external/jsr305":/* recursive = */ true,
"frameworks/ex/common":/* recursive = */ true,
@@ -166,6 +225,8 @@
"packages/apps/QuickSearchBox":/* recursive = */ true,
"packages/apps/WallpaperPicker":/* recursive = */ false,
+ "prebuilts/gcc":/* recursive = */ true,
+ "prebuilts/build-tools":/* recursive = */ false,
"prebuilts/sdk":/* recursive = */ false,
"prebuilts/sdk/current/extras/app-toolkit":/* recursive = */ false,
"prebuilts/sdk/current/support":/* recursive = */ false,
@@ -175,114 +236,335 @@
// Configure modules in these directories to enable bp2build_available: true or false by default.
bp2buildDefaultConfig = Bp2BuildConfig{
- "bionic": Bp2BuildDefaultTrueRecursively,
+ "art/libartpalette": Bp2BuildDefaultTrueRecursively,
+ "art/libdexfile": Bp2BuildDefaultTrueRecursively,
+ "art/runtime": Bp2BuildDefaultTrueRecursively,
+ "art/tools": Bp2BuildDefaultTrue,
+ "bionic": Bp2BuildDefaultTrueRecursively,
+ "bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
+ "build/bazel/examples/soong_config_variables": Bp2BuildDefaultTrueRecursively,
"build/bazel/examples/apex/minimal": Bp2BuildDefaultTrueRecursively,
+ "build/make/tools/signapk": Bp2BuildDefaultTrue,
+ "build/soong": Bp2BuildDefaultTrue,
+ "build/soong/cc/libbuildversion": Bp2BuildDefaultTrue, // Skip tests subdir
+ "build/soong/cc/ndkstubgen": Bp2BuildDefaultTrue,
+ "build/soong/cc/symbolfile": Bp2BuildDefaultTrue,
+ "build/soong/linkerconfig": Bp2BuildDefaultTrueRecursively,
+ "build/soong/scripts": Bp2BuildDefaultTrueRecursively,
+ "cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively,
+ "development/apps/DevelopmentSettings": Bp2BuildDefaultTrue,
+ "development/apps/Fallback": Bp2BuildDefaultTrue,
+ "development/apps/WidgetPreview": Bp2BuildDefaultTrue,
+ "development/samples/BasicGLSurfaceView": Bp2BuildDefaultTrue,
+ "development/samples/BluetoothChat": Bp2BuildDefaultTrue,
+ "development/samples/BrokenKeyDerivation": Bp2BuildDefaultTrue,
+ "development/samples/Compass": Bp2BuildDefaultTrue,
+ "development/samples/ContactManager": Bp2BuildDefaultTrue,
+ "development/samples/FixedGridLayout": Bp2BuildDefaultTrue,
+ "development/samples/HelloEffects": Bp2BuildDefaultTrue,
+ "development/samples/Home": Bp2BuildDefaultTrue,
+ "development/samples/HoneycombGallery": Bp2BuildDefaultTrue,
+ "development/samples/JetBoy": Bp2BuildDefaultTrue,
+ "development/samples/KeyChainDemo": Bp2BuildDefaultTrue,
+ "development/samples/LceDemo": Bp2BuildDefaultTrue,
+ "development/samples/LunarLander": Bp2BuildDefaultTrue,
+ "development/samples/MultiResolution": Bp2BuildDefaultTrue,
+ "development/samples/MultiWindow": Bp2BuildDefaultTrue,
+ "development/samples/NotePad": Bp2BuildDefaultTrue,
+ "development/samples/Obb": Bp2BuildDefaultTrue,
+ "development/samples/RSSReader": Bp2BuildDefaultTrue,
+ "development/samples/ReceiveShareDemo": Bp2BuildDefaultTrue,
+ "development/samples/SearchableDictionary": Bp2BuildDefaultTrue,
+ "development/samples/SipDemo": Bp2BuildDefaultTrue,
+ "development/samples/SkeletonApp": Bp2BuildDefaultTrue,
+ "development/samples/Snake": Bp2BuildDefaultTrue,
+ "development/samples/SpellChecker/": Bp2BuildDefaultTrueRecursively,
+ "development/samples/ThemedNavBarKeyboard": Bp2BuildDefaultTrue,
+ "development/samples/ToyVpn": Bp2BuildDefaultTrue,
+ "development/samples/TtsEngine": Bp2BuildDefaultTrue,
+ "development/samples/USB/AdbTest": Bp2BuildDefaultTrue,
+ "development/samples/USB/MissileLauncher": Bp2BuildDefaultTrue,
+ "development/samples/VoiceRecognitionService": Bp2BuildDefaultTrue,
+ "development/samples/VoicemailProviderDemo": Bp2BuildDefaultTrue,
+ "development/samples/WiFiDirectDemo": Bp2BuildDefaultTrue,
"development/sdk": Bp2BuildDefaultTrueRecursively,
"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
+ "external/auto/common": Bp2BuildDefaultTrueRecursively,
+ "external/auto/service": Bp2BuildDefaultTrueRecursively,
"external/boringssl": Bp2BuildDefaultTrueRecursively,
+ "external/bouncycastle": Bp2BuildDefaultTrue,
"external/brotli": Bp2BuildDefaultTrue,
+ "external/conscrypt": Bp2BuildDefaultTrue,
+ "external/error_prone": Bp2BuildDefaultTrueRecursively,
"external/fmtlib": Bp2BuildDefaultTrueRecursively,
- "external/googletest/googletest": 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,
+ "frameworks/base/media/tests/MediaDump": Bp2BuildDefaultTrue,
+ "frameworks/base/startop/apps/test": Bp2BuildDefaultTrue,
"frameworks/native/libs/adbd_auth": Bp2BuildDefaultTrueRecursively,
+ "frameworks/native/opengl/tests/gl2_cameraeye": Bp2BuildDefaultTrue,
+ "frameworks/native/opengl/tests/gl2_java": Bp2BuildDefaultTrue,
+ "frameworks/native/opengl/tests/testLatency": Bp2BuildDefaultTrue,
+ "frameworks/native/opengl/tests/testPauseResume": Bp2BuildDefaultTrue,
+ "frameworks/native/opengl/tests/testViewport": Bp2BuildDefaultTrue,
+ "frameworks/proto_logging/stats/stats_log_api_gen": Bp2BuildDefaultTrueRecursively,
+ "libnativehelper": Bp2BuildDefaultTrueRecursively,
+ "packages/apps/DevCamera": Bp2BuildDefaultTrue,
+ "packages/apps/HTMLViewer": Bp2BuildDefaultTrue,
+ "packages/apps/Protips": Bp2BuildDefaultTrue,
+ "packages/modules/StatsD/lib/libstatssocket": Bp2BuildDefaultTrueRecursively,
"packages/modules/adb": Bp2BuildDefaultTrue,
+ "packages/modules/adb/apex": Bp2BuildDefaultTrue,
"packages/modules/adb/crypto": Bp2BuildDefaultTrueRecursively,
"packages/modules/adb/libs": Bp2BuildDefaultTrueRecursively,
"packages/modules/adb/pairing_auth": Bp2BuildDefaultTrueRecursively,
"packages/modules/adb/pairing_connection": Bp2BuildDefaultTrueRecursively,
"packages/modules/adb/proto": Bp2BuildDefaultTrueRecursively,
"packages/modules/adb/tls": Bp2BuildDefaultTrueRecursively,
+ "packages/providers/MediaProvider/tools/dialogs": Bp2BuildDefaultTrue,
+ "packages/screensavers/Basic": Bp2BuildDefaultTrue,
+ "packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultTrue,
"prebuilts/clang/host/linux-x86": Bp2BuildDefaultTrueRecursively,
+ "prebuilts/tools/common/m2": Bp2BuildDefaultTrue,
+ "system/apex": Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
+ "system/apex/proto": Bp2BuildDefaultTrueRecursively,
+ "system/apex/libs": Bp2BuildDefaultTrueRecursively,
+ "system/core/debuggerd": Bp2BuildDefaultTrueRecursively,
"system/core/diagnose_usb": Bp2BuildDefaultTrueRecursively,
"system/core/libasyncio": Bp2BuildDefaultTrue,
"system/core/libcrypto_utils": Bp2BuildDefaultTrueRecursively,
"system/core/libcutils": Bp2BuildDefaultTrueRecursively,
+ "system/core/libpackagelistparser": Bp2BuildDefaultTrueRecursively,
"system/core/libprocessgroup": Bp2BuildDefaultTrue,
"system/core/libprocessgroup/cgrouprc": Bp2BuildDefaultTrue,
"system/core/libprocessgroup/cgrouprc_format": Bp2BuildDefaultTrue,
+ "system/core/libsystem": Bp2BuildDefaultTrueRecursively,
+ "system/core/libutils": Bp2BuildDefaultTrueRecursively,
+ "system/core/libvndksupport": Bp2BuildDefaultTrueRecursively,
"system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
"system/libbase": Bp2BuildDefaultTrueRecursively,
+ "system/libprocinfo": Bp2BuildDefaultTrue,
"system/libziparchive": Bp2BuildDefaultTrueRecursively,
"system/logging/liblog": Bp2BuildDefaultTrueRecursively,
"system/sepolicy/apex": Bp2BuildDefaultTrueRecursively,
"system/timezone/apex": Bp2BuildDefaultTrueRecursively,
"system/timezone/output_data": Bp2BuildDefaultTrueRecursively,
+ "system/unwinding/libbacktrace": Bp2BuildDefaultTrueRecursively,
+ "system/unwinding/libunwindstack": Bp2BuildDefaultTrueRecursively,
+ "tools/apksig": Bp2BuildDefaultTrue,
+ "tools/platform-compat/java/android/compat": Bp2BuildDefaultTrueRecursively,
}
// Per-module denylist to always opt modules out of both bp2build and mixed builds.
bp2buildModuleDoNotConvertList = []string{
- "libc_malloc_debug", // depends on libunwindstack, which depends on unsupported module art_cc_library_statics
+ "libnativehelper_compat_libc", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
+
+ "libart", // depends on unconverted modules: art_operator_srcs, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfile, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbase, libprofile, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api
+ "libart-runtime-gtest", // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libbacktrace, libartbase-art-gtest
+ "libart_headers", // depends on unconverted modules: art_libartbase_headers
+ "libartd", // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api, art_operator_srcs, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfiled, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbased, libprofiled, cpp-define-generator-asm-support
+ "libartd-runtime-gtest", // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libbacktrace, libartbased-art-gtest
+ "libstatslog_art", // depends on unconverted modules: statslog_art.cpp, statslog_art.h
+ "statslog_art.h", "statslog_art.cpp", // depends on unconverted modules: stats-log-api-gen
+
+ "libandroid_runtime_lazy", // depends on unconverted modules: libbinder_headers
+ "libcmd", // depends on unconverted modules: libbinder
+
+ "libdexfile_support_static", // Depends on unconverted module: libdexfile_external_headers
+ "libunwindstack_local", "libunwindstack_utils", "libc_malloc_debug", "libfdtrack", // Depends on unconverted module: libunwindstack
+
+ "libdexfile_support", // TODO(b/210546943): Enabled based on product variables.
+ "libdexfile_external_headers", // TODO(b/210546943): Enabled based on product variables.
+
+ "libunwindstack", // Depends on unconverted module libdexfile_support.
+ "libnativehelper_compat_libc++", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
+
+ "chkcon", "sefcontext_compile", // depends on unconverted modules: libsepol
+
+ "libsepol", // TODO(b/207408632): Unsupported case of .l sources in cc library rules
+
+ "gen-kotlin-build-file.py", // module has same name as source
+
+ "libactivitymanager_aidl", // TODO(b/207426160): Depends on activity_manager_procstate_aidl, which is an aidl filegroup.
+
+ "libnativehelper_lazy_mts_jni", "libnativehelper_mts_jni", // depends on unconverted modules: libgmock_ndk
+ "libnativetesthelper_jni", "libgmock_main_ndk", "libgmock_ndk", // depends on unconverted module: libgtest_ndk_c++
+
+ "statslog-framework-java-gen", "statslog.cpp", "statslog.h", "statslog.rs", "statslog_header.rs", // depends on unconverted modules: stats-log-api-gen
+
+ "stats-log-api-gen", // depends on unconverted modules: libstats_proto_host, libprotobuf-cpp-full
+
+ "libstatslog", // depends on unconverted modules: statslog.cpp, statslog.h, ...
+
+ "cmd", // depends on unconverted module packagemanager_aidl-cpp, of unsupported type aidl_interface
+ "servicedispatcher", // depends on unconverted module android.debug_aidl, of unsupported type aidl_interface
+ "libutilscallstack", // depends on unconverted module libbacktrace
+ "libbacktrace", // depends on unconverted module libunwindstack
+ "libdebuggerd_handler", // depends on unconverted module libdebuggerd_handler_core
+ "libdebuggerd_handler_core", "libdebuggerd_handler_fallback", // depends on unconverted module libdebuggerd
+ "unwind_for_offline", // depends on unconverted module libunwindstack_utils
+ "libdebuggerd", // depends on unconverted modules libdexfile_support, libunwindstack, gwp_asan_crash_handler, libtombstone_proto, libprotobuf-cpp-lite
+ "libdexfile_static", // depends on libartpalette, libartbase, libdexfile, which are of unsupported type: art_cc_library.
+
+ "static_crasher", // depends on unconverted modules: libdebuggerd_handler
+
+ "pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack
"libbase_ndk", // http://b/186826477, fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
- "libprotobuf-python", // contains .proto sources
- "libprotobuf-internal-protos", // we don't handle path property for fileegroups
- "libprotobuf-internal-python-srcs", // we don't handle path property for fileegroups
+ "libprotobuf-internal-protos", // b/210751803, we don't handle path property for filegroups
+ "libprotobuf-internal-python-srcs", // b/210751803, we don't handle path property for filegroups
+ "libprotobuf-java-full", // b/210751803, we don't handle path property for filegroups
+ "host-libprotobuf-java-full", // b/210751803, we don't handle path property for filegroups
+ "libprotobuf-java-util-full", // b/210751803, we don't handle path property for filegroups
- "libseccomp_policy", // b/201094425: depends on func_to_syscall_nrs, which depends on py_binary, which is unsupported in mixed builds.
- "libfdtrack", // depends on libunwindstack, which depends on unsupported module art_cc_library_statics
+ "conscrypt", // b/210751803, we don't handle path property for filegroups
+ "conscrypt-for-host", // b/210751803, we don't handle path property for filegroups
- "gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset
+ "host-libprotobuf-java-lite", // b/217236083, java_library cannot have deps without srcs
+ "host-libprotobuf-java-micro", // b/217236083, java_library cannot have deps without srcs
+ "host-libprotobuf-java-nano", // b/217236083, java_library cannot have deps without srcs
+ "error_prone_core", // b/217236083, java_library cannot have deps without srcs
+ "bouncycastle-host", // b/217236083, java_library cannot have deps without srcs
+
+ "apex_manifest_proto_java", // b/215230097, we don't handle .proto files in java_library srcs attribute
+
+ "libc_musl_sysroot_bionic_arch_headers", // b/218405924, depends on soong_zip
+ "libc_musl_sysroot_bionic_headers", // b/218405924, depends on soong_zip and generates duplicate srcs
+
+ // python protos
+ "libprotobuf-python", // contains .proto sources
+ "conv_linker_config", // depends on linker_config_proto, a python lib with proto sources
+ "apex_build_info_proto", "apex_manifest_proto", // a python lib with proto sources
+ "linker_config_proto", // contains .proto sources
"brotli-fuzzer-corpus", // b/202015218: outputs are in location incompatible with bazel genrule handling.
- // //external/libcap/...
- "libcap", // http://b/198595332, depends on _makenames, a cc_binary
- "cap_names.h", // http://b/198596102, depends on _makenames, a cc_binary
-
- // Tests. Handle later.
- "libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found
- "libjemalloc5_integrationtest",
- "libjemalloc5_stresstestlib",
- "libjemalloc5_unittest",
+ // b/203369847: multiple genrules in the same package creating the same file
+ // //development/sdk/...
+ "platform_tools_properties",
+ "build_tools_source_properties",
// APEX support
- "com.android.runtime", // http://b/194746715, apex, depends on 'libc_malloc_debug'
-
- "libadb_crypto", // Depends on libadb_protos
- "libadb_crypto_static", // Depends on libadb_protos_static
- "libadb_pairing_connection", // Depends on libadb_protos
- "libadb_pairing_connection_static", // Depends on libadb_protos_static
- "libadb_pairing_server", // Depends on libadb_protos
- "libadb_pairing_server_static", // Depends on libadb_protos_static
- "libadbd", // Depends on libadbd_core
- "libadbd_core", // Depends on libadb_protos
- "libadbd_services", // Depends on libadb_protos
-
- "libadb_protos_static", // b/200601772: Requires cc_library proto support
- "libadb_protos", // b/200601772: Requires cc_library proto support
- "libapp_processes_protos_lite", // b/200601772: Requires cc_library proto support
+ "com.android.runtime", // depends on unconverted modules: bionic-linker-config, linkerconfig
"libgtest_ndk_c++", // b/201816222: Requires sdk_version support.
"libgtest_main_ndk_c++", // b/201816222: Requires sdk_version support.
+
+ "abb", // depends on unconverted modules: libcmd, libbinder
+ "adb", // depends on unconverted modules: AdbWinApi, libadb_host, libandroidfw, libapp_processes_protos_full, libfastdeploy_host, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
+ "libadb_host", // depends on unconverted modules: libopenscreen-discovery, libopenscreen-platform-impl, libusb, AdbWinApi
+ "libfastdeploy_host", // depends on unconverted modules: libandroidfw, libusb, AdbWinApi
+ "linker", // depends on unconverted modules: libdebuggerd_handler_fallback
+ "linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_*
+ "versioner", // depends on unconverted modules: libclang_cxx_host, libLLVM_host, of unsupported type llvm_host_prebuilt_library_shared
+
+ "linkerconfig", // http://b/202876379 has arch-variant static_executable
+ "mdnsd", // http://b/202876379 has arch-variant static_executable
+
+ "CarHTMLViewer", // depends on unconverted modules android.car-stubs, car-ui-lib
+
+ "libdexfile", // depends on unconverted modules: dexfile_operator_srcs, libartbase, libartpalette,
+ "libdexfiled", // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette
+
+ // go deps:
+ "apex-protos", // depends on soong_zip, a go binary
+ "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.
+ "robolectric-sqlite4java-native", // depends on soong_zip, a go binary
+ "robolectric_tzdata", // depends on soong_zip, a go binary
+
+ "android_icu4j_srcgen_binary", // Bazel build error: deps not allowed without srcs; move to runtime_deps
+ "core-icu4j-for-host", // Bazel build error: deps not allowed without srcs; move to runtime_deps
+
+ // java deps
+ "android_icu4j_srcgen", // depends on unconverted modules: currysrc
+ "bin2c_fastdeployagent", // depends on deployagent, a java binary
+ "currysrc", // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9
+ "robolectric-sqlite4java-0.282", // depends on unconverted modules: robolectric-sqlite4java-import, robolectric-sqlite4java-native
+ "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
+
+ "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
+
+ "art-script", // depends on unconverted modules: dalvikvm, dex2oat
+ "dex2oat-script", // depends on unconverted modules: dex2oat
+
+ "error_prone_checkerframework_dataflow_nullaway", // TODO(b/219908977): "Error in fail: deps not allowed without srcs; move to runtime_deps?"
}
// Per-module denylist of cc_library modules to only generate the static
// variant if their shared variant isn't ready or buildable by Bazel.
- bp2buildCcLibraryStaticOnlyList = []string{
- "libjemalloc5", // http://b/188503688, cc_library, `target: { android: { enabled: false } }` for android targets.
- }
+ bp2buildCcLibraryStaticOnlyList = []string{}
// Per-module denylist to opt modules out of mixed builds. Such modules will
// still be generated via bp2build.
mixedBuildsDisabledList = []string{
- "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
- "func_to_syscall_nrs", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
- "libseccomp_policy_app_zygote_sources", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
- "libseccomp_policy_app_sources", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
- "libseccomp_policy_system_sources", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
+ "art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
+ "libbrotli", // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
+ "minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
+
+ "cap_names.h", // TODO(b/204913827) runfiles need to be handled in mixed builds
+ "libcap", // TODO(b/204913827) runfiles need to be handled in mixed builds
+ "libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
+
+ // Depends on libprotobuf-cpp-*
+ "libadb_pairing_connection",
+ "libadb_pairing_connection_static",
+ "libadb_pairing_server", "libadb_pairing_server_static",
+
+ // TODO(b/204811222) support suffix in cc_binary
+ "acvp_modulewrapper",
+ "android.hardware.media.c2@1.0-service-v4l2",
+ "app_process",
+ "bar_test",
+ "bench_cxa_atexit",
+ "bench_noop",
+ "bench_noop_nostl",
+ "bench_noop_static",
+ "boringssl_self_test",
+ "boringssl_self_test_vendor",
+ "bssl",
+ "cavp",
+ "crash_dump",
+ "crasher",
+ "libcxx_test_template",
+ "linker",
+ "memory_replay",
+ "native_bridge_guest_linker",
+ "native_bridge_stub_library_defaults",
+ "noop",
+ "simpleperf_ndk",
+ "toybox-static",
+ "zlib_bench",
}
// Used for quicker lookups
@@ -328,7 +610,14 @@
// MixedBuildsEnabled checks that a module is ready to be replaced by a
// converted or handcrafted Bazel target.
-func (b *BazelModuleBase) MixedBuildsEnabled(ctx BazelConversionPathContext) bool {
+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
}
@@ -346,28 +635,25 @@
}
// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
-func convertedToBazel(ctx BazelConversionPathContext, module blueprint.Module) bool {
+func convertedToBazel(ctx BazelConversionContext, module blueprint.Module) bool {
b, ok := module.(Bazelable)
if !ok {
return false
}
- return b.convertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
+ return b.shouldConvertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
}
-// ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
-func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
- return b.convertWithBp2build(ctx, ctx.Module())
+// ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
+func (b *BazelModuleBase) ShouldConvertWithBp2build(ctx BazelConversionContext) bool {
+ return b.shouldConvertWithBp2build(ctx, ctx.Module())
}
-func (b *BazelModuleBase) convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool {
+func (b *BazelModuleBase) shouldConvertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool {
if bp2buildModuleDoNotConvert[module.Name()] {
return false
}
- // Ensure that the module type of this module has a bp2build converter. This
- // prevents mixed builds from using auto-converted modules just by matching
- // the package dir; it also has to have a bp2build mutator as well.
- if ctx.Config().bp2buildModuleTypeConfig[ctx.OtherModuleType(module)] == false {
+ if !b.bazelProps().Bazel_module.CanConvertToBazel {
return false
}
@@ -442,3 +728,36 @@
}
return string(data[:]), nil
}
+
+func registerBp2buildConversionMutator(ctx RegisterMutatorsContext) {
+ ctx.TopDown("bp2build_conversion", convertWithBp2build).Parallel()
+}
+
+func convertWithBp2build(ctx TopDownMutatorContext) {
+ bModule, ok := ctx.Module().(Bazelable)
+ if !ok || !bModule.shouldConvertWithBp2build(ctx, ctx.Module()) {
+ return
+ }
+
+ bModule.ConvertWithBp2build(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.
+func GetMainClassInManifest(c Config, filepath string) (string, error) {
+ file, err := c.fs.Open(filepath)
+ if err != nil {
+ return "", err
+ }
+ defer file.Close()
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "Main-Class:") {
+ return strings.TrimSpace(line[len("Main-Class:"):]), nil
+ }
+ }
+
+ return "", errors.New("Main-Class is not found.")
+}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 6648a2f..0052551 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -48,11 +48,17 @@
StarlarkFunctionBody() string
}
+// Portion of cquery map key to describe target configuration.
+type configKey struct {
+ archType ArchType
+ osType OsType
+}
+
// Map key to describe bazel cquery requests.
type cqueryKey struct {
label string
requestType cqueryRequest
- archType ArchType
+ configKey configKey
}
// bazelHandler is the interface for a helper object related to deferring to Bazel for
@@ -72,14 +78,14 @@
// has been queued to be run later.
// Returns result files built by building the given bazel target label.
- GetOutputFiles(label string, archType ArchType) ([]string, bool)
+ GetOutputFiles(label string, cfgKey configKey) ([]string, bool)
// 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, archType ArchType) (cquery.CcInfo, bool, error)
+ GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error)
// Returns the executable binary resultant from building together the python sources
- GetPythonBinary(label string, archType ArchType) (string, bool)
+ GetPythonBinary(label string, cfgKey configKey) (string, bool)
// ** End cquery methods
@@ -140,17 +146,17 @@
LabelToPythonBinary map[string]string
}
-func (m MockBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
+func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
result, ok := m.LabelToOutputFiles[label]
return result, ok
}
-func (m MockBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
result, ok := m.LabelToCcInfo[label]
return result, ok, nil
}
-func (m MockBazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
+func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
result, ok := m.LabelToPythonBinary[label]
return result, ok
}
@@ -171,8 +177,8 @@
var _ BazelContext = MockBazelContext{}
-func (bazelCtx *bazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
- rawString, ok := bazelCtx.cquery(label, cquery.GetOutputFiles, archType)
+func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
+ rawString, ok := bazelCtx.cquery(label, cquery.GetOutputFiles, cfgKey)
var ret []string
if ok {
bazelOutput := strings.TrimSpace(rawString)
@@ -181,8 +187,8 @@
return ret, ok
}
-func (bazelCtx *bazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
- result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, archType)
+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
}
@@ -192,8 +198,8 @@
return ret, ok, err
}
-func (bazelCtx *bazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
- rawString, ok := bazelCtx.cquery(label, cquery.GetPythonBinary, archType)
+func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
+ rawString, ok := bazelCtx.cquery(label, cquery.GetPythonBinary, cfgKey)
var ret string
if ok {
bazelOutput := strings.TrimSpace(rawString)
@@ -202,19 +208,15 @@
return ret, ok
}
-func (n noopBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
+func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
panic("unimplemented")
}
-func (n noopBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
panic("unimplemented")
}
-func (n noopBazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
- panic("unimplemented")
-}
-
-func (n noopBazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
+func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
panic("unimplemented")
}
@@ -303,8 +305,8 @@
// returns (result, true). If the request is queued but no results are available,
// then returns ("", false).
func (context *bazelContext) cquery(label string, requestType cqueryRequest,
- archType ArchType) (string, bool) {
- key := cqueryKey{label, requestType, archType}
+ cfgKey configKey) (string, bool) {
+ key := cqueryKey{label, requestType, cfgKey}
if result, ok := context.results[key]; ok {
return result, true
} else {
@@ -419,7 +421,7 @@
def _config_node_transition_impl(settings, attr):
return {
- "//command_line_option:platforms": "@//build/bazel/platforms:android_%s" % attr.arch,
+ "//command_line_option:platforms": "@//build/bazel/platforms:%s_%s" % (attr.os, attr.arch),
}
_config_node_transition = transition(
@@ -437,7 +439,8 @@
implementation = _passthrough_rule_impl,
attrs = {
"arch" : attr.string(mandatory = True),
- "deps" : attr.label_list(cfg = _config_node_transition),
+ "os" : 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"),
},
)
@@ -488,38 +491,32 @@
configNodeFormatString := `
config_node(name = "%s",
arch = "%s",
+ os = "%s",
deps = [%s],
)
`
- commonArchFilegroupString := `
-filegroup(name = "common",
- srcs = [%s],
-)
-`
-
configNodesSection := ""
- labelsByArch := map[string][]string{}
+ labelsByConfig := map[string][]string{}
for val, _ := range context.requests {
labelString := fmt.Sprintf("\"@%s\"", val.label)
- archString := getArchString(val)
- labelsByArch[archString] = append(labelsByArch[archString], labelString)
+ configString := getConfigString(val)
+ labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
}
allLabels := []string{}
- for archString, labels := range labelsByArch {
- if archString == "common" {
- // arch-less labels (e.g. filegroups) don't need a config_node
- allLabels = append(allLabels, "\":common\"")
- labelsString := strings.Join(labels, ",\n ")
- configNodesSection += fmt.Sprintf(commonArchFilegroupString, labelsString)
- } else {
- // Create a config_node, and add the config_node's label to allLabels
- allLabels = append(allLabels, fmt.Sprintf("\":%s\"", archString))
- labelsString := strings.Join(labels, ",\n ")
- configNodesSection += fmt.Sprintf(configNodeFormatString, archString, archString, labelsString)
+ for configString, labels := range labelsByConfig {
+ configTokens := strings.Split(configString, "|")
+ if len(configTokens) != 2 {
+ panic(fmt.Errorf("Unexpected config string format: %s", configString))
}
+ archString := configTokens[0]
+ osString := configTokens[1]
+ targetString := fmt.Sprintf("%s_%s", osString, archString)
+ allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString))
+ labelsString := strings.Join(labels, ",\n ")
+ configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, labelsString)
}
return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n ")))
@@ -591,9 +588,11 @@
# specific platform architecture in mixed builds. This is consistent with how
# Soong treats filegroups, but it may not be the case with manually-written
# filegroup BUILD targets.
- if target.kind in ["filegroup", ""]:
- return "common"
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"]
if len(platforms) != 1:
# An individual configured target should have only one platform architecture.
@@ -604,9 +603,9 @@
if platform_name == "host":
return "HOST"
elif platform_name.startswith("android_"):
- return platform_name[len("android_"):]
+ return platform_name[len("android_"):] + "|" + platform_name[:len("android_")-1]
elif platform_name.startswith("linux_"):
- return platform_name[len("linux_"):]
+ return platform_name[len("linux_"):] + "|" + platform_name[:len("linux_")-1]
else:
fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
return "UNKNOWN"
@@ -696,7 +695,7 @@
cqueryOutput, cqueryErr, err = context.issueBazelCommand(
context.paths,
bazel.CqueryBuildRootRunName,
- bazelCommand{"cquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
+ bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)},
"--output=starlark",
"--starlark:file="+absolutePath(cqueryFileRelpath))
err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"),
@@ -854,14 +853,23 @@
}
func getCqueryId(key cqueryKey) string {
- return key.label + "|" + getArchString(key)
+ return key.label + "|" + getConfigString(key)
}
-func getArchString(key cqueryKey) string {
- arch := key.archType.Name
- if len(arch) > 0 {
- return arch
- } else {
- return "x86_64"
+func getConfigString(key cqueryKey) string {
+ arch := key.configKey.archType.Name
+ if len(arch) == 0 || arch == "common" {
+ // 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" {
+ // Use host OS, which is currently hardcoded to be linux.
+ os = "linux"
+ }
+ return arch + "|" + os
+}
+
+func GetConfigKey(ctx ModuleContext) configKey {
+ return configKey{archType: ctx.Arch().ArchType, osType: ctx.Os()}
}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index cdf1a63..ad5b63b 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -9,11 +9,11 @@
func TestRequestResultsAfterInvokeBazel(t *testing.T) {
label := "//foo:bar"
- arch := Arm64
+ cfg := configKey{Arm64, Android}
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
- bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: `//foo:bar|arm64>>out/foo/bar.txt`,
+ bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64|android>>out/foo/bar.txt`,
})
- g, ok := bazelContext.GetOutputFiles(label, arch)
+ g, ok := bazelContext.GetOutputFiles(label, cfg)
if ok {
t.Errorf("Did not expect cquery results prior to running InvokeBazel(), but got %s", g)
}
@@ -21,7 +21,7 @@
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
- g, ok = bazelContext.GetOutputFiles(label, arch)
+ 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) {
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 9957369..f353a9d 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -68,24 +68,37 @@
// cannot be resolved,the function will panic. This is often due to the dependency not being added
// via an AddDependency* method.
-// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
-// order to form a Bazel-compatible label for conversion.
-type BazelConversionPathContext interface {
- EarlyModulePathContext
+// 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.
+type BazelConversionContext interface {
+ Config() Config
- GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
- ModuleFromName(name string) (blueprint.Module, bool)
Module() Module
OtherModuleType(m blueprint.Module) string
OtherModuleName(m blueprint.Module) string
OtherModuleDir(m blueprint.Module) string
+}
+
+// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
+// order to form a Bazel-compatible label for conversion.
+type BazelConversionPathContext interface {
+ EarlyModulePathContext
+ BazelConversionContext
+
+ ModuleErrorf(fmt string, args ...interface{})
+ PropertyErrorf(property, fmt string, args ...interface{})
+ GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
+ ModuleFromName(name string) (blueprint.Module, bool)
AddUnconvertedBp2buildDep(string)
+ AddMissingBp2buildDep(dep string)
}
// BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
// module within the given ctx.
-func BazelLabelForModuleDeps(ctx TopDownMutatorContext, modules []string) bazel.LabelList {
+func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel)
}
@@ -95,15 +108,15 @@
// list which corresponds to dependencies on the module within the given ctx, and the excluded
// dependencies. Prebuilt dependencies will be appended with _alwayslink so they can be handled as
// whole static libraries.
-func BazelLabelForModuleDepsExcludes(ctx TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel)
}
// BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("<module>"
// or ":<module>") and applies moduleToLabelFn to determine and return a Bazel-compatible label
// which corresponds to dependencies on the module within the given ctx.
-func BazelLabelForModuleDepsWithFn(ctx TopDownMutatorContext, modules []string,
- moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList {
+func BazelLabelForModuleDepsWithFn(ctx BazelConversionPathContext, modules []string,
+ moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
var labels bazel.LabelList
// In some cases, a nil string list is different than an explicitly empty list.
if len(modules) == 0 && modules != nil {
@@ -117,8 +130,10 @@
}
if m, t := SrcIsModuleWithTag(module); m != "" {
l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn)
- l.OriginalModuleName = bpText
- labels.Includes = append(labels.Includes, l)
+ if l != nil {
+ l.OriginalModuleName = bpText
+ labels.Includes = append(labels.Includes, *l)
+ }
} else {
ctx.ModuleErrorf("%q, is not a module reference", module)
}
@@ -131,8 +146,8 @@
// to other modules, ("<module>" or ":<module>"). It applies moduleToLabelFn to determine and return a
// Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and
// the excluded dependencies.
-func BazelLabelForModuleDepsExcludesWithFn(ctx TopDownMutatorContext, modules, excludes []string,
- moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList {
+func BazelLabelForModuleDepsExcludesWithFn(ctx BazelConversionPathContext, modules, excludes []string,
+ moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn)
if len(excludes) == 0 {
return moduleLabels
@@ -144,12 +159,18 @@
}
}
-func BazelLabelForModuleSrcSingle(ctx TopDownMutatorContext, path string) bazel.Label {
- return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
+func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
+ if srcs := BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
+ return srcs[0]
+ }
+ return bazel.Label{}
}
-func BazelLabelForModuleDepSingle(ctx TopDownMutatorContext, path string) bazel.Label {
- return BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes[0]
+func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
+ if srcs := BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
+ return srcs[0]
+ }
+ return bazel.Label{}
}
// BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
@@ -158,7 +179,7 @@
// relative if within the same package).
// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
// will have already been handled by the path_deps mutator.
-func BazelLabelForModuleSrc(ctx TopDownMutatorContext, paths []string) bazel.LabelList {
+func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
}
@@ -168,7 +189,7 @@
// (absolute if in a different package or relative if within the same package).
// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
// will have already been handled by the path_deps mutator.
-func BazelLabelForModuleSrcExcludes(ctx TopDownMutatorContext, paths, excludes []string) bazel.LabelList {
+func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
excluded := make([]string, 0, len(excludeLabels.Includes))
for _, e := range excludeLabels.Includes {
@@ -267,6 +288,16 @@
return newPaths
}
+// Converts root-relative Paths to a list of bazel.Label relative to the module in ctx.
+func RootToModuleRelativePaths(ctx BazelConversionPathContext, paths Paths) []bazel.Label {
+ var newPaths []bazel.Label
+ for _, path := range PathsWithModuleSrcSubDir(ctx, paths, "") {
+ s := path.Rel()
+ newPaths = append(newPaths, bazel.Label{Label: s})
+ }
+ return newPaths
+}
+
// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source
// directory and Bazel target labels, excluding those included in the excludes argument (which
// should already be expanded to resolve references to Soong-modules). Valid elements of paths
@@ -288,7 +319,7 @@
// Properties passed as the paths or excludes argument must have been annotated with struct tag
// `android:"path"` so that dependencies on other modules will have already been handled by the
// path_deps mutator.
-func expandSrcsForBazel(ctx TopDownMutatorContext, paths, expandedExcludes []string) bazel.LabelList {
+func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
if paths == nil {
return bazel.LabelList{}
}
@@ -306,9 +337,9 @@
for _, p := range paths {
if m, tag := SrcIsModuleWithTag(p); m != "" {
l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel)
- if !InList(l.Label, expandedExcludes) {
+ if l != nil && !InList(l.Label, expandedExcludes) {
l.OriginalModuleName = fmt.Sprintf(":%s", m)
- labels.Includes = append(labels.Includes, l)
+ labels.Includes = append(labels.Includes, *l)
}
} else {
var expandedPaths []bazel.Label
@@ -316,12 +347,7 @@
// e.g. turn "math/*.c" in
// external/arm-optimized-routines to external/arm-optimized-routines/math/*.c
rootRelativeGlobPath := pathForModuleSrc(ctx, p).String()
- globbedPaths := GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes)
- globbedPaths = PathsWithModuleSrcSubDir(ctx, globbedPaths, "")
- for _, path := range globbedPaths {
- s := path.Rel()
- expandedPaths = append(expandedPaths, bazel.Label{Label: s})
- }
+ expandedPaths = RootToModuleRelativePaths(ctx, GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes))
} else {
if !InList(p, expandedExcludes) {
expandedPaths = append(expandedPaths, bazel.Label{Label: p})
@@ -336,11 +362,17 @@
// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
// module. The label will be relative to the current directory if appropriate. The dependency must
// already be resolved by either deps mutator or path deps mutator.
-func getOtherModuleLabel(ctx TopDownMutatorContext, dep, tag string,
- labelFromModule func(TopDownMutatorContext, blueprint.Module) string) bazel.Label {
+func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string,
+ labelFromModule func(BazelConversionPathContext, blueprint.Module) string) *bazel.Label {
m, _ := ctx.ModuleFromName(dep)
+ // The module was not found in an Android.bp file, this is often due to:
+ // * a limited manifest
+ // * a required module not being converted from Android.mk
if m == nil {
- panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name()))
+ ctx.AddMissingBp2buildDep(dep)
+ return &bazel.Label{
+ Label: ":" + dep + "__BP2BUILD__MISSING__DEP",
+ }
}
if !convertedToBazel(ctx, m) {
ctx.AddUnconvertedBp2buildDep(dep)
@@ -354,12 +386,12 @@
otherLabel = bazelShortLabel(otherLabel)
}
- return bazel.Label{
+ return &bazel.Label{
Label: otherLabel,
}
}
-func BazelModuleLabel(ctx TopDownMutatorContext, module blueprint.Module) string {
+func BazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
if !convertedToBazel(ctx, module) {
return bp2buildModuleLabel(ctx, module)
@@ -370,11 +402,17 @@
func bazelShortLabel(label string) string {
i := strings.Index(label, ":")
+ if i == -1 {
+ panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
+ }
return label[i:]
}
func bazelPackage(label string) string {
i := strings.Index(label, ":")
+ if i == -1 {
+ panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
+ }
return label[0:i]
}
@@ -382,7 +420,7 @@
return bazelPackage(label1) == bazelPackage(label2)
}
-func bp2buildModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
+func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string {
moduleName := ctx.OtherModuleName(module)
moduleDir := ctx.OtherModuleDir(module)
return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
diff --git a/android/config.go b/android/config.go
index c8d7cfd..57eff92 100644
--- a/android/config.go
+++ b/android/config.go
@@ -24,6 +24,7 @@
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
"runtime"
"strconv"
"strings"
@@ -37,6 +38,7 @@
"android/soong/android/soongconfig"
"android/soong/bazel"
"android/soong/remoteexec"
+ "android/soong/starlark_fmt"
)
// Bool re-exports proptools.Bool for the android package.
@@ -155,8 +157,9 @@
fs pathtools.FileSystem
mockBpList string
- bp2buildPackageConfig Bp2BuildConfig
- bp2buildModuleTypeConfig map[string]bool
+ runningAsBp2Build bool
+ bp2buildPackageConfig Bp2BuildConfig
+ Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
// in tests when a path doesn't exist.
@@ -272,15 +275,43 @@
return fmt.Errorf("Could not create dir %s: %s", dir, err)
}
- data, err := json.MarshalIndent(&config, "", " ")
+ nonArchVariantProductVariables := []string{}
+ archVariantProductVariables := []string{}
+ p := variableProperties{}
+ t := reflect.TypeOf(p.Product_variables)
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ nonArchVariantProductVariables = append(nonArchVariantProductVariables, strings.ToLower(f.Name))
+ if proptools.HasTag(f, "android", "arch_variant") {
+ archVariantProductVariables = append(archVariantProductVariables, strings.ToLower(f.Name))
+ }
+ }
+
+ nonArchVariantProductVariablesJson := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0)
+ if err != nil {
+ return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
+ }
+
+ archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0)
+ if err != nil {
+ return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
+ }
+
+ configJson, err := json.MarshalIndent(&config, "", " ")
if err != nil {
return fmt.Errorf("cannot marshal config data: %s", err.Error())
}
bzl := []string{
bazel.GeneratedBazelFileWarning,
- fmt.Sprintf(`_product_vars = json.decode("""%s""")`, data),
- "product_vars = _product_vars\n",
+ fmt.Sprintf(`_product_vars = json.decode("""%s""")`, configJson),
+ fmt.Sprintf(`_product_var_constraints = %s`, nonArchVariantProductVariablesJson),
+ fmt.Sprintf(`_arch_variant_product_var_constraints = %s`, archVariantProductVariablesJson),
+ "\n", `
+product_vars = _product_vars
+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)
if err != nil {
@@ -322,7 +353,7 @@
DeviceName: stringPtr("test_device"),
Platform_sdk_version: intPtr(30),
Platform_sdk_codename: stringPtr("S"),
- Platform_version_active_codenames: []string{"S"},
+ Platform_version_active_codenames: []string{"S", "Tiramisu"},
DeviceSystemSdkVersions: []string{"14", "15"},
Platform_systemsdk_versions: []string{"29", "30"},
AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
@@ -333,10 +364,8 @@
ShippingApiLevel: stringPtr("30"),
},
- outDir: buildDir,
- // soongOutDir is inconsistent with production (it should be buildDir + "/soong")
- // but a lot of tests assume this :(
- soongOutDir: buildDir,
+ outDir: buildDir,
+ soongOutDir: filepath.Join(buildDir, "soong"),
captureBuild: true,
env: envCopy,
@@ -353,7 +382,7 @@
config.mockFileSystem(bp, fs)
- config.bp2buildModuleTypeConfig = map[string]bool{}
+ determineBuildOS(config)
return Config{config}
}
@@ -361,8 +390,6 @@
func modifyTestConfigToSupportArchMutator(testConfig Config) {
config := testConfig.config
- determineBuildOS(config)
-
config.Targets = map[OsType][]Target{
Android: []Target{
{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
@@ -522,7 +549,6 @@
config.BazelContext, err = NewBazelContext(config)
config.bp2buildPackageConfig = bp2buildDefaultConfig
- config.bp2buildModuleTypeConfig = make(map[string]bool)
return Config{config}, err
}
@@ -564,23 +590,30 @@
// BlueprintToolLocation returns the directory containing build system tools
// from Blueprint, like soong_zip and merge_zips.
func (c *config) HostToolDir() string {
- return filepath.Join(c.soongOutDir, "host", c.PrebuiltOS(), "bin")
+ if c.KatiEnabled() {
+ return filepath.Join(c.outDir, "host", c.PrebuiltOS(), "bin")
+ } else {
+ return filepath.Join(c.soongOutDir, "host", c.PrebuiltOS(), "bin")
+ }
}
func (c *config) HostToolPath(ctx PathContext, tool string) Path {
- return PathForOutput(ctx, "host", c.PrebuiltOS(), "bin", tool)
+ path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin", false, tool)
+ return path
}
-func (c *config) HostJNIToolPath(ctx PathContext, path string) Path {
+func (c *config) HostJNIToolPath(ctx PathContext, lib string) Path {
ext := ".so"
if runtime.GOOS == "darwin" {
ext = ".dylib"
}
- return PathForOutput(ctx, "host", c.PrebuiltOS(), "lib64", path+ext)
+ path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "lib64", false, lib+ext)
+ return path
}
-func (c *config) HostJavaToolPath(ctx PathContext, path string) Path {
- return PathForOutput(ctx, "host", c.PrebuiltOS(), "framework", path)
+func (c *config) HostJavaToolPath(ctx PathContext, tool string) Path {
+ path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "framework", false, tool)
+ return path
}
// PrebuiltOS returns the name of the host OS used in prebuilts directories.
@@ -749,6 +782,16 @@
return levels
}
+func (c *config) LatestPreviewApiLevel() ApiLevel {
+ level := NoneApiLevel
+ for _, l := range c.PreviewApiLevels() {
+ if l.GreaterThan(level) {
+ level = l
+ }
+ }
+ return level
+}
+
func (c *config) AllSupportedApiLevels() []ApiLevel {
var levels []ApiLevel
levels = append(levels, c.FinalApiLevels()...)
@@ -840,7 +883,7 @@
// Returns true if building apps that aren't bundled with the platform.
// UnbundledBuild() is always true when this is true.
func (c *config) UnbundledBuildApps() bool {
- return Bool(c.productVariables.Unbundled_build_apps)
+ return len(c.productVariables.Unbundled_build_apps) > 0
}
// Returns true if building image that aren't bundled with the platform.
@@ -866,8 +909,13 @@
return Bool(c.productVariables.Eng)
}
+// DevicePrimaryArchType returns the ArchType for the first configured device architecture, or
+// Common if there are no device architectures.
func (c *config) DevicePrimaryArchType() ArchType {
- return c.Targets[Android][0].Arch.ArchType
+ if androidTargets := c.Targets[Android]; len(androidTargets) > 0 {
+ return androidTargets[0].Arch.ArchType
+ }
+ return Common
}
func (c *config) SanitizeHost() []string {
@@ -1168,10 +1216,6 @@
return c.config.productVariables.DeviceKernelHeaders
}
-func (c *deviceConfig) SamplingPGO() bool {
- return Bool(c.config.productVariables.SamplingPGO)
-}
-
// JavaCoverageEnabledForPath returns whether Java code coverage is enabled for
// path. Coverage is enabled by default when the product variable
// JavaCoveragePaths is empty. If JavaCoveragePaths is not empty, coverage is
@@ -1227,6 +1271,10 @@
return coverage
}
+func (c *deviceConfig) AfdoAdditionalProfileDirs() []string {
+ return c.config.productVariables.AfdoAdditionalProfileDirs
+}
+
func (c *deviceConfig) PgoAdditionalProfileDirs() []string {
return c.config.productVariables.PgoAdditionalProfileDirs
}
@@ -1447,6 +1495,10 @@
return String(c.config.productVariables.TotSepolicyVersion)
}
+func (c *deviceConfig) PlatformSepolicyCompatVersions() []string {
+ return c.config.productVariables.PlatformSepolicyCompatVersions
+}
+
func (c *deviceConfig) BoardSepolicyVers() string {
if ver := String(c.config.productVariables.BoardSepolicyVers); ver != "" {
return ver
@@ -1454,10 +1506,42 @@
return c.PlatformSepolicyVersion()
}
+func (c *deviceConfig) BoardPlatVendorPolicy() []string {
+ return c.config.productVariables.BoardPlatVendorPolicy
+}
+
func (c *deviceConfig) BoardReqdMaskPolicy() []string {
return c.config.productVariables.BoardReqdMaskPolicy
}
+func (c *deviceConfig) BoardSystemExtPublicPrebuiltDirs() []string {
+ return c.config.productVariables.BoardSystemExtPublicPrebuiltDirs
+}
+
+func (c *deviceConfig) BoardSystemExtPrivatePrebuiltDirs() []string {
+ return c.config.productVariables.BoardSystemExtPrivatePrebuiltDirs
+}
+
+func (c *deviceConfig) BoardProductPublicPrebuiltDirs() []string {
+ return c.config.productVariables.BoardProductPublicPrebuiltDirs
+}
+
+func (c *deviceConfig) BoardProductPrivatePrebuiltDirs() []string {
+ return c.config.productVariables.BoardProductPrivatePrebuiltDirs
+}
+
+func (c *deviceConfig) SystemExtSepolicyPrebuiltApiDir() string {
+ return String(c.config.productVariables.SystemExtSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) ProductSepolicyPrebuiltApiDir() string {
+ return String(c.config.productVariables.ProductSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) IsPartnerTrebleSepolicyTestEnabled() bool {
+ return c.SystemExtSepolicyPrebuiltApiDir() != "" || c.ProductSepolicyPrebuiltApiDir() != ""
+}
+
func (c *deviceConfig) DirectedVendorSnapshot() bool {
return c.config.productVariables.DirectedVendorSnapshot
}
@@ -1578,6 +1662,10 @@
return c.config.productVariables.SepolicyFreezeTestExtraPrebuiltDirs
}
+func (c *deviceConfig) GenerateAidlNdkPlatformBackend() bool {
+ 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
@@ -1667,7 +1755,7 @@
}
// Append a list of (apex, jar) pairs to the list.
-func (l *ConfiguredJarList) AppendList(other ConfiguredJarList) ConfiguredJarList {
+func (l *ConfiguredJarList) AppendList(other *ConfiguredJarList) ConfiguredJarList {
apexes := make([]string, 0, l.Len()+other.Len())
jars := make([]string, 0, l.Len()+other.Len())
@@ -1906,3 +1994,8 @@
func (c *config) RBEWrapper() string {
return c.GetenvWithDefault("RBE_WRAPPER", remoteexec.DefaultWrapperPath)
}
+
+// UseHostMusl returns true if the host target has been configured to build against musl libc.
+func (c *config) UseHostMusl() bool {
+ return Bool(c.productVariables.HostMusl)
+}
diff --git a/android/deapexer.go b/android/deapexer.go
index bed6574..265f531 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -143,12 +143,16 @@
}
// FindDeapexerProviderForModule searches through the direct dependencies of the current context
-// module for a DeapexerTag dependency and returns its DeapexerInfo. If there is an error then it is
-// reported with ctx.ModuleErrorf and nil is returned.
+// module for a DeapexerTag dependency and returns its DeapexerInfo. If a single nonambiguous
+// deapexer module isn't found then errors are reported with ctx.ModuleErrorf and nil is returned.
func FindDeapexerProviderForModule(ctx ModuleContext) *DeapexerInfo {
var di *DeapexerInfo
ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) {
p := ctx.OtherModuleProvider(m, DeapexerProvider).(DeapexerInfo)
+ if di != nil {
+ ctx.ModuleErrorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s",
+ di.ApexModuleName(), p.ApexModuleName())
+ }
di = &p
})
if di != nil {
diff --git a/android/defaults.go b/android/defaults.go
index be80cf1..8b121f6 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -89,14 +89,13 @@
var _ Defaultable = (*DefaultableModuleBase)(nil)
func InitDefaultableModule(module DefaultableModule) {
- if module.(Module).base().module == nil {
+ if module.base().module == nil {
panic("InitAndroidModule must be called before InitDefaultableModule")
}
- module.setProperties(module.(Module).GetProperties(), module.(Module).base().variableProperties)
+
+ module.setProperties(module.GetProperties(), module.base().variableProperties)
module.AddProperties(module.defaults())
-
- module.base().customizableProperties = module.GetProperties()
}
// A restricted subset of context methods, similar to LoadHookContext.
@@ -118,6 +117,11 @@
type DefaultsModuleBase struct {
DefaultableModuleBase
+
+ // 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().
+ BazelModuleBase
}
// The common pattern for defaults modules is to register separate instances of
@@ -160,6 +164,7 @@
type DefaultsModule interface {
Module
Defaults
+ Bazelable
}
func (d *DefaultsModuleBase) properties() []interface{} {
@@ -170,8 +175,11 @@
return d.defaultableVariableProperties
}
-func (d *DefaultsModuleBase) GenerateAndroidBuildActions(ctx ModuleContext) {
-}
+func (d *DefaultsModuleBase) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+// ConvertWithBp2build to fulfill Bazelable interface; however, at this time defaults module are
+// *NOT* converted with bp2build
+func (defaultable *DefaultsModuleBase) ConvertWithBp2build(ctx TopDownMutatorContext) {}
func InitDefaultsModule(module DefaultsModule) {
commonProperties := &commonProperties{}
@@ -182,6 +190,8 @@
&ApexProperties{},
&distProperties{})
+ // Bazel module must be initialized _before_ Defaults to be included in cc_defaults module.
+ InitBazelModule(module)
initAndroidModuleBase(module)
initProductVariableModule(module)
initArchModule(module)
@@ -208,15 +218,64 @@
// The applicable licenses property for defaults is 'licenses'.
setPrimaryLicensesProperty(module, "licenses", &commonProperties.Licenses)
- base.module = module
}
var _ Defaults = (*DefaultsModuleBase)(nil)
+// applyNamespacedVariableDefaults only runs in bp2build mode for
+// defaultable/defaults modules. Its purpose is to merge namespaced product
+// variable props from defaults deps, even if those defaults are custom module
+// types created from soong_config_module_type, e.g. one that's wrapping a
+// cc_defaults or java_defaults.
+func applyNamespacedVariableDefaults(defaultDep Defaults, ctx TopDownMutatorContext) {
+ var dep, b Bazelable
+
+ dep, ok := defaultDep.(Bazelable)
+ if !ok {
+ if depMod, ok := defaultDep.(Module); ok {
+ // Track that this dependency hasn't been converted to bp2build yet.
+ ctx.AddUnconvertedBp2buildDep(depMod.Name())
+ return
+ } else {
+ panic("Expected default dep to be a Module.")
+ }
+ }
+
+ b, ok = ctx.Module().(Bazelable)
+ if !ok {
+ return
+ }
+
+ // namespacedVariableProps is a map from namespaces (e.g. acme, android,
+ // vendor_foo) to a slice of soong_config_variable struct pointers,
+ // containing properties for that particular module.
+ src := dep.namespacedVariableProps()
+ dst := b.namespacedVariableProps()
+ if dst == nil {
+ dst = make(namespacedVariableProperties)
+ }
+
+ // Propagate all soong_config_variable structs from the dep. We'll merge the
+ // actual property values later in variable.go.
+ for namespace := range src {
+ if dst[namespace] == nil {
+ dst[namespace] = []interface{}{}
+ }
+ for _, i := range src[namespace] {
+ dst[namespace] = append(dst[namespace], i)
+ }
+ }
+
+ b.setNamespacedVariableProps(dst)
+}
+
func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
defaultsList []Defaults) {
for _, defaults := range defaultsList {
+ if ctx.Config().runningAsBp2Build {
+ applyNamespacedVariableDefaults(defaults, ctx)
+ }
for _, prop := range defaultable.defaultableProperties {
if prop == defaultable.defaultableVariableProperties {
defaultable.applyDefaultVariableProperties(ctx, defaults, prop)
diff --git a/android/defs.go b/android/defs.go
index c8e2e9b..362b382 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -52,10 +52,10 @@
// A copy rule.
Cp = pctx.AndroidStaticRule("Cp",
blueprint.RuleParams{
- Command: "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out",
+ Command: "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out$extraCmds",
Description: "cp $out",
},
- "cpFlags")
+ "cpFlags", "extraCmds")
// A copy rule that only updates the output if it changed.
CpIfChanged = pctx.AndroidStaticRule("CpIfChanged",
@@ -68,10 +68,10 @@
CpExecutable = pctx.AndroidStaticRule("CpExecutable",
blueprint.RuleParams{
- Command: "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out && chmod +x $out",
+ Command: "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out && chmod +x $out$extraCmds",
Description: "cp $out",
},
- "cpFlags")
+ "cpFlags", "extraCmds")
// A timestamp touch rule.
Touch = pctx.AndroidStaticRule("Touch",
diff --git a/android/filegroup.go b/android/filegroup.go
index 2cf5567..c932ffa 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -22,7 +22,6 @@
func init() {
RegisterModuleType("filegroup", FileGroupFactory)
- RegisterBp2BuildMutator("filegroup", FilegroupBp2Build)
}
var PrepareForTestWithFilegroup = FixtureRegisterWithContext(func(ctx RegistrationContext) {
@@ -34,12 +33,8 @@
Srcs bazel.LabelListAttribute
}
-func FilegroupBp2Build(ctx TopDownMutatorContext) {
- fg, ok := ctx.Module().(*fileGroup)
- if !ok || !fg.ConvertWithBp2build(ctx) {
- return
- }
-
+// ConvertWithBp2build performs bp2build conversion of filegroup
+func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) {
srcs := bazel.MakeLabelListAttribute(
BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
@@ -112,15 +107,24 @@
return module
}
-func (fg *fileGroup) GenerateBazelBuildActions(ctx ModuleContext) bool {
+func (fg *fileGroup) maybeGenerateBazelBuildActions(ctx ModuleContext) {
if !fg.MixedBuildsEnabled(ctx) {
- return false
+ return
+ }
+
+ archVariant := ctx.Arch().ArchType
+ osVariant := ctx.Os()
+ if len(fg.Srcs()) == 1 && fg.Srcs()[0].Base() == fg.Name() {
+ // This will be a regular file target, not filegroup, in Bazel.
+ // See FilegroupBp2Build for more information.
+ archVariant = Common
+ osVariant = CommonOS
}
bazelCtx := ctx.Config().BazelContext
- filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), Common)
+ filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{archVariant, osVariant})
if !ok {
- return false
+ return
}
bazelOuts := make(Paths, 0, len(filePaths))
@@ -130,19 +134,15 @@
}
fg.srcs = bazelOuts
-
- return true
}
func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
- if fg.GenerateBazelBuildActions(ctx) {
- return
- }
-
fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
if fg.properties.Path != nil {
fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
}
+
+ fg.maybeGenerateBazelBuildActions(ctx)
}
func (fg *fileGroup) Srcs() Paths {
diff --git a/android/hooks.go b/android/hooks.go
index 85fc081..bded764 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -65,10 +65,10 @@
return l.bp.ModuleFactories()
}
-func (l *loadHookContext) AppendProperties(props ...interface{}) {
+func (l *loadHookContext) appendPrependHelper(props []interface{},
+ extendFn func([]interface{}, interface{}, proptools.ExtendPropertyFilterFunc) error) {
for _, p := range props {
- err := proptools.AppendMatchingProperties(l.Module().base().customizableProperties,
- p, nil)
+ err := extendFn(l.Module().base().GetProperties(), p, nil)
if err != nil {
if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
@@ -78,19 +78,12 @@
}
}
}
+func (l *loadHookContext) AppendProperties(props ...interface{}) {
+ l.appendPrependHelper(props, proptools.AppendMatchingProperties)
+}
func (l *loadHookContext) PrependProperties(props ...interface{}) {
- for _, p := range props {
- err := proptools.PrependMatchingProperties(l.Module().base().customizableProperties,
- p, nil)
- if err != nil {
- if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
- l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
- } else {
- panic(err)
- }
- }
- }
+ l.appendPrependHelper(props, proptools.PrependMatchingProperties)
}
func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
diff --git a/android/license.go b/android/license.go
index 8bfd3ba..ebee055 100644
--- a/android/license.go
+++ b/android/license.go
@@ -63,7 +63,7 @@
func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) {
// license modules have no licenses, but license_kinds must refer to license_kind modules
mergeStringProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName())
- mergePathProps(&m.base().commonProperties.Effective_license_text, PathsForModuleSrc(ctx, m.properties.License_text)...)
+ namePathProps(&m.base().commonProperties.Effective_license_text, m.properties.Package_name, PathsForModuleSrc(ctx, m.properties.License_text)...)
for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
if lk, ok := module.(*licenseKindModule); ok {
mergeStringProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...)
@@ -80,9 +80,6 @@
base := module.base()
module.AddProperties(&base.nameProperties, &module.properties)
- base.generalProperties = module.GetProperties()
- base.customizableProperties = module.GetProperties()
-
// The visibility property needs to be checked and parsed by the visibility module.
setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
diff --git a/android/license_kind.go b/android/license_kind.go
index ddecd77..838dedd 100644
--- a/android/license_kind.go
+++ b/android/license_kind.go
@@ -53,9 +53,6 @@
base := module.base()
module.AddProperties(&base.nameProperties, &module.properties)
- base.generalProperties = module.GetProperties()
- base.customizableProperties = module.GetProperties()
-
// The visibility property needs to be checked and parsed by the visibility module.
setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
diff --git a/android/license_metadata.go b/android/license_metadata.go
new file mode 100644
index 0000000..6a5b0da
--- /dev/null
+++ b/android/license_metadata.go
@@ -0,0 +1,254 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+var (
+ _ = pctx.HostBinToolVariable("licenseMetadataCmd", "build_license_metadata")
+
+ licenseMetadataRule = pctx.AndroidStaticRule("licenseMetadataRule", blueprint.RuleParams{
+ Command: "${licenseMetadataCmd} -o $out @${out}.rsp",
+ CommandDeps: []string{"${licenseMetadataCmd}"},
+ Rspfile: "${out}.rsp",
+ RspfileContent: "${args}",
+ }, "args")
+)
+
+func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
+ base := ctx.Module().base()
+
+ if !base.Enabled() {
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(ctx.Module()) {
+ return
+ }
+
+ var outputFiles Paths
+ if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok {
+ outputFiles, _ = outputFileProducer.OutputFiles("")
+ outputFiles = PathsIfNonNil(outputFiles...)
+ }
+
+ isContainer := isContainerFromFileExtensions(base.installFiles, outputFiles)
+
+ var allDepMetadataFiles Paths
+ var allDepMetadataArgs []string
+ var allDepOutputFiles Paths
+ var allDepMetadataDepSets []*PathsDepSet
+
+ ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) {
+ dep, _ := bpdep.(Module)
+ if dep == nil {
+ return
+ }
+ if !dep.Enabled() {
+ return
+ }
+
+ if ctx.OtherModuleHasProvider(dep, LicenseMetadataProvider) {
+ info := ctx.OtherModuleProvider(dep, LicenseMetadataProvider).(*LicenseMetadataInfo)
+ allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
+ if isContainer || IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) {
+ allDepMetadataDepSets = append(allDepMetadataDepSets, info.LicenseMetadataDepSet)
+ }
+
+ depAnnotations := licenseAnnotationsFromTag(ctx.OtherModuleDependencyTag(dep))
+
+ allDepMetadataArgs = append(allDepMetadataArgs, info.LicenseMetadataPath.String()+depAnnotations)
+
+ if depInstallFiles := dep.base().installFiles; len(depInstallFiles) > 0 {
+ allDepOutputFiles = append(allDepOutputFiles, depInstallFiles.Paths()...)
+ } else if depOutputFiles, err := outputFilesForModule(ctx, dep, ""); err == nil {
+ depOutputFiles = PathsIfNonNil(depOutputFiles...)
+ allDepOutputFiles = append(allDepOutputFiles, depOutputFiles...)
+ }
+ }
+ })
+
+ allDepMetadataFiles = SortedUniquePaths(allDepMetadataFiles)
+ sort.Strings(allDepMetadataArgs)
+ allDepOutputFiles = SortedUniquePaths(allDepOutputFiles)
+
+ var orderOnlyDeps Paths
+ var args []string
+
+ if t := ctx.ModuleType(); t != "" {
+ args = append(args,
+ "-mt "+proptools.NinjaAndShellEscape(t))
+ }
+
+ args = append(args,
+ "-r "+proptools.NinjaAndShellEscape(ctx.ModuleDir()),
+ "-mc UNKNOWN")
+
+ if p := base.commonProperties.Effective_package_name; p != nil {
+ args = append(args,
+ "-p "+proptools.NinjaAndShellEscape(*p))
+ }
+
+ args = append(args,
+ JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_kinds), "-k "))
+
+ args = append(args,
+ JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_conditions), "-c "))
+
+ args = append(args,
+ JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_text.Strings()), "-n "))
+
+ if isContainer {
+ transitiveDeps := newPathsDepSet(nil, allDepMetadataDepSets).ToList()
+ args = append(args,
+ JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(transitiveDeps.Strings()), "-d "))
+ orderOnlyDeps = append(orderOnlyDeps, transitiveDeps...)
+ } else {
+ args = append(args,
+ JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepMetadataArgs), "-d "))
+ orderOnlyDeps = append(orderOnlyDeps, allDepMetadataFiles...)
+ }
+
+ args = append(args,
+ JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepOutputFiles.Strings()), "-s "))
+
+ // Install map
+ args = append(args,
+ JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.licenseInstallMap), "-m "))
+
+ // Built files
+ 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
+ args = append(args,
+ JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.installFiles.Strings()), "-i "))
+
+ if isContainer {
+ args = append(args, "--is_container")
+ }
+
+ ctx.Build(pctx, BuildParams{
+ Rule: licenseMetadataRule,
+ Output: licenseMetadataFile,
+ OrderOnly: orderOnlyDeps,
+ Description: "license metadata",
+ Args: map[string]string{
+ "args": strings.Join(args, " "),
+ },
+ })
+
+ ctx.SetProvider(LicenseMetadataProvider, &LicenseMetadataInfo{
+ LicenseMetadataPath: licenseMetadataFile,
+ LicenseMetadataDepSet: newPathsDepSet(Paths{licenseMetadataFile}, allDepMetadataDepSets),
+ })
+}
+
+func isContainerFromFileExtensions(installPaths InstallPaths, builtPaths Paths) bool {
+ var paths Paths
+ if len(installPaths) > 0 {
+ paths = installPaths.Paths()
+ } else {
+ paths = builtPaths
+ }
+
+ for _, path := range paths {
+ switch path.Ext() {
+ case ".zip", ".tar", ".tgz", ".tar.gz", ".img", ".srcszip", ".apex":
+ return true
+ }
+ }
+
+ return false
+}
+
+// LicenseMetadataProvider is used to propagate license metadata paths between modules.
+var LicenseMetadataProvider = blueprint.NewProvider(&LicenseMetadataInfo{})
+
+// LicenseMetadataInfo stores the license metadata path for a module.
+type LicenseMetadataInfo struct {
+ LicenseMetadataPath Path
+ LicenseMetadataDepSet *PathsDepSet
+}
+
+// licenseAnnotationsFromTag returns the LicenseAnnotations for a tag (if any) converted into
+// a string, or an empty string if there are none.
+func licenseAnnotationsFromTag(tag blueprint.DependencyTag) string {
+ if annoTag, ok := tag.(LicenseAnnotationsDependencyTag); ok {
+ annos := annoTag.LicenseAnnotations()
+ if len(annos) > 0 {
+ annoStrings := make([]string, len(annos))
+ for i, s := range annos {
+ annoStrings[i] = string(s)
+ }
+ return ":" + strings.Join(annoStrings, ",")
+ }
+ }
+ return ""
+}
+
+// LicenseAnnotationsDependencyTag is implemented by dependency tags in order to provide a
+// list of license dependency annotations.
+type LicenseAnnotationsDependencyTag interface {
+ LicenseAnnotations() []LicenseAnnotation
+}
+
+// LicenseAnnotation is an enum of annotations that can be applied to dependencies for propagating
+// license information.
+type LicenseAnnotation string
+
+const (
+ // LicenseAnnotationSharedDependency should be returned by LicenseAnnotations implementations
+ // of dependency tags when the usage of the dependency is dynamic, for example a shared library
+ // linkage for native modules or as a classpath library for java modules.
+ //
+ // Dependency tags that need to always return LicenseAnnotationSharedDependency
+ // can embed LicenseAnnotationSharedDependencyTag to implement LicenseAnnotations.
+ LicenseAnnotationSharedDependency LicenseAnnotation = "dynamic"
+
+ // LicenseAnnotationToolchain should be returned by LicenseAnnotations implementations of
+ // dependency tags when the dependency is used as a toolchain.
+ //
+ // Dependency tags that need to always return LicenseAnnotationToolchain
+ // can embed LicenseAnnotationToolchainDependencyTag to implement LicenseAnnotations.
+ LicenseAnnotationToolchain LicenseAnnotation = "toolchain"
+)
+
+// LicenseAnnotationSharedDependencyTag can be embedded in a dependency tag to implement
+// LicenseAnnotations that always returns LicenseAnnotationSharedDependency.
+type LicenseAnnotationSharedDependencyTag struct{}
+
+func (LicenseAnnotationSharedDependencyTag) LicenseAnnotations() []LicenseAnnotation {
+ return []LicenseAnnotation{LicenseAnnotationSharedDependency}
+}
+
+// LicenseAnnotationToolchainDependencyTag can be embedded in a dependency tag to implement
+// LicenseAnnotations that always returns LicenseAnnotationToolchain.
+type LicenseAnnotationToolchainDependencyTag struct{}
+
+func (LicenseAnnotationToolchainDependencyTag) LicenseAnnotations() []LicenseAnnotation {
+ return []LicenseAnnotation{LicenseAnnotationToolchain}
+}
diff --git a/android/license_sdk_member.go b/android/license_sdk_member.go
index 2ce921b..b17defe 100644
--- a/android/license_sdk_member.go
+++ b/android/license_sdk_member.go
@@ -90,7 +90,10 @@
// Populate the properties from the variant.
l := variant.(*licenseModule)
p.License_kinds = l.properties.License_kinds
- p.License_text = l.base().commonProperties.Effective_license_text
+ p.License_text = make(Paths, 0, len(l.base().commonProperties.Effective_license_text))
+ for _, np := range l.base().commonProperties.Effective_license_text {
+ p.License_text = append(p.License_text, np.Path)
+ }
}
func (p *licenseSdkMemberProperties) AddToPropertySet(ctx SdkMemberContext, propertySet BpPropertySet) {
diff --git a/android/licenses.go b/android/licenses.go
index bcd85f9..b51a06b 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -213,7 +213,7 @@
m.base().commonProperties.Effective_package_name = l.properties.Package_name
}
mergeStringProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...)
- mergePathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
+ mergeNamedPathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
mergeStringProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...)
mergeStringProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...)
} else {
@@ -239,10 +239,24 @@
*prop = SortedUniqueStrings(*prop)
}
-// Update a property Path array with a distinct union of its values and a list of new values.
-func mergePathProps(prop *Paths, values ...Path) {
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func namePathProps(prop *NamedPaths, name *string, values ...Path) {
+ if name == nil {
+ for _, value := range values {
+ *prop = append(*prop, NamedPath{value, ""})
+ }
+ } else {
+ for _, value := range values {
+ *prop = append(*prop, NamedPath{value, *name})
+ }
+ }
+ *prop = SortedUniqueNamedPaths(*prop)
+}
+
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func mergeNamedPathProps(prop *NamedPaths, values ...NamedPath) {
*prop = append(*prop, values...)
- *prop = SortedUniquePaths(*prop)
+ *prop = SortedUniqueNamedPaths(*prop)
}
// Get the licenses property falling back to the package default.
@@ -308,3 +322,15 @@
}
var LicenseInfoProvider = blueprint.NewProvider(LicenseInfo{})
+
+func init() {
+ RegisterMakeVarsProvider(pctx, licensesMakeVarsProvider)
+}
+
+func licensesMakeVarsProvider(ctx MakeVarsContext) {
+ ctx.Strict("BUILD_LICENSE_METADATA",
+ ctx.Config().HostToolPath(ctx, "build_license_metadata").String())
+ ctx.Strict("HTMLNOTICE", ctx.Config().HostToolPath(ctx, "htmlnotice").String())
+ ctx.Strict("XMLNOTICE", ctx.Config().HostToolPath(ctx, "xmlnotice").String())
+ ctx.Strict("TEXTNOTICE", ctx.Config().HostToolPath(ctx, "textnotice").String())
+}
diff --git a/android/licenses_test.go b/android/licenses_test.go
index d05b0a3..8a81e12 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -90,9 +90,9 @@
"libother": []string{"shownotice"},
},
effectiveNotices: map[string][]string{
- "libexample1": []string{"top/LICENSE", "top/NOTICE"},
- "libnested": []string{"top/LICENSE", "top/NOTICE"},
- "libother": []string{"top/LICENSE", "top/NOTICE"},
+ "libexample1": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+ "libnested": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+ "libother": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
},
},
@@ -779,9 +779,6 @@
base := m.base()
m.AddProperties(&base.nameProperties, &m.properties)
- base.generalProperties = m.GetProperties()
- base.customizableProperties = m.GetProperties()
-
// The default_visibility property needs to be checked and parsed by the visibility module during
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(m, "visibility", &m.properties.Visibility)
diff --git a/android/makevars.go b/android/makevars.go
index 40c0ccd..ece7091 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -17,6 +17,8 @@
import (
"bytes"
"fmt"
+ "path/filepath"
+ "runtime"
"sort"
"strings"
@@ -140,15 +142,19 @@
var singletonMakeVarsProvidersKey = NewOnceKey("singletonMakeVarsProvidersKey")
+func getSingletonMakevarsProviders(config Config) *[]makeVarsProvider {
+ return config.Once(singletonMakeVarsProvidersKey, func() interface{} {
+ return &[]makeVarsProvider{}
+ }).(*[]makeVarsProvider)
+}
+
// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to
// the list of MakeVarsProviders to run.
func registerSingletonMakeVarsProvider(config Config, singleton SingletonMakeVarsProvider) {
// Singletons are registered on the Context and may be different between different Contexts,
// for example when running multiple tests. Store the SingletonMakeVarsProviders in the
// Config so they are attached to the Context.
- singletonMakeVarsProviders := config.Once(singletonMakeVarsProvidersKey, func() interface{} {
- return &[]makeVarsProvider{}
- }).(*[]makeVarsProvider)
+ singletonMakeVarsProviders := getSingletonMakevarsProviders(config)
*singletonMakeVarsProviders = append(*singletonMakeVarsProviders,
makeVarsProvider{pctx, singletonMakeVarsProviderAdapter(singleton)})
@@ -173,7 +179,9 @@
return &makeVarsSingleton{}
}
-type makeVarsSingleton struct{}
+type makeVarsSingleton struct {
+ installsForTesting []byte
+}
type makeVarsProvider struct {
pctx PackageContext
@@ -222,6 +230,9 @@
lateOutFile := absolutePath(PathForOutput(ctx,
"late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
+ installsFile := absolutePath(PathForOutput(ctx,
+ "installs"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
+
if ctx.Failed() {
return
}
@@ -229,9 +240,11 @@
var vars []makeVarsVariable
var dists []dist
var phonies []phony
+ var katiInstalls []katiInstall
+ var katiSymlinks []katiInstall
providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
- providers = append(providers, *ctx.Config().Get(singletonMakeVarsProvidersKey).(*[]makeVarsProvider)...)
+ providers = append(providers, *getSingletonMakevarsProviders(ctx.Config())...)
for _, provider := range providers {
mctx := &makeVarsContext{
@@ -258,6 +271,11 @@
phonies = append(phonies, mctx.phonies...)
dists = append(dists, mctx.dists...)
}
+
+ if m.ExportedToMake() {
+ katiInstalls = append(katiInstalls, m.base().katiInstalls...)
+ katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...)
+ }
})
if ctx.Failed() {
@@ -297,6 +315,12 @@
ctx.Errorf(err.Error())
}
+ installsBytes := s.writeInstalls(katiInstalls, katiSymlinks)
+ if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
+ ctx.Errorf(err.Error())
+ }
+
+ s.installsForTesting = installsBytes
}
func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
@@ -398,6 +422,7 @@
fmt.Fprintln(buf)
for _, dist := range dists {
+ fmt.Fprintf(buf, ".PHONY: %s\n", strings.Join(dist.goals, " "))
fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n",
strings.Join(dist.goals, " "), strings.Join(dist.paths, " "))
}
@@ -405,6 +430,94 @@
return buf.Bytes()
}
+// writeInstalls writes the list of install rules generated by Soong to a makefile. The rules
+// are exported to Make instead of written directly to the ninja file so that main.mk can add
+// the dependencies from the `required` property that are hard to resolve in Soong.
+func (s *makeVarsSingleton) writeInstalls(installs, symlinks []katiInstall) []byte {
+ buf := &bytes.Buffer{}
+
+ fmt.Fprint(buf, `# Autogenerated file
+
+# Values written by Soong to generate install rules that can be amended by Kati.
+
+
+`)
+
+ preserveSymlinksFlag := "-d"
+ if runtime.GOOS == "darwin" {
+ preserveSymlinksFlag = "-R"
+ }
+
+ for _, install := range installs {
+ // Write a rule for each install request in the form:
+ // to: from [ deps ] [ | order only deps ]
+ // cp -f -d $< $@ [ && chmod +x $@ ]
+ fmt.Fprintf(buf, "%s: %s", install.to.String(), install.from.String())
+ for _, dep := range install.implicitDeps {
+ fmt.Fprintf(buf, " %s", dep.String())
+ }
+ if extraFiles := install.extraFiles; extraFiles != nil {
+ fmt.Fprintf(buf, " %s", extraFiles.zip.String())
+ }
+ if len(install.orderOnlyDeps) > 0 {
+ fmt.Fprintf(buf, " |")
+ }
+ for _, dep := range install.orderOnlyDeps {
+ fmt.Fprintf(buf, " %s", dep.String())
+ }
+ fmt.Fprintln(buf)
+ 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")
+ }
+ if extraFiles := install.extraFiles; extraFiles != nil {
+ fmt.Fprintf(buf, "\tunzip -qDD -d '%s' '%s'\n", extraFiles.dir.String(), extraFiles.zip.String())
+ }
+ fmt.Fprintln(buf)
+ }
+
+ for _, symlink := range symlinks {
+ fmt.Fprintf(buf, "%s:", symlink.to.String())
+ if symlink.from != nil {
+ // The symlink doesn't need updating when the target is modified, but we sometimes
+ // have a dependency on a symlink to a binary instead of to the binary directly, and
+ // the mtime of the symlink must be updated when the binary is modified, so use a
+ // normal dependency here instead of an order-only dependency.
+ fmt.Fprintf(buf, " %s", symlink.from.String())
+ }
+ for _, dep := range symlink.implicitDeps {
+ fmt.Fprintf(buf, " %s", dep.String())
+ }
+ if len(symlink.orderOnlyDeps) > 0 {
+ fmt.Fprintf(buf, " |")
+ }
+ for _, dep := range symlink.orderOnlyDeps {
+ fmt.Fprintf(buf, " %s", dep.String())
+ }
+ fmt.Fprintln(buf)
+
+ fromStr := ""
+ if symlink.from != nil {
+ rel, err := filepath.Rel(filepath.Dir(symlink.to.String()), symlink.from.String())
+ if err != nil {
+ panic(fmt.Errorf("failed to find relative path for symlink from %q to %q: %w",
+ symlink.from.String(), symlink.to.String(), err))
+ }
+ fromStr = rel
+ } else {
+ fromStr = symlink.absFrom
+ }
+
+ fmt.Fprintln(buf, "\t@echo \"Symlink $@\"")
+ fmt.Fprintf(buf, "\trm -f $@ && ln -sfn %s $@", fromStr)
+ fmt.Fprintln(buf)
+ fmt.Fprintln(buf)
+ }
+
+ return buf.Bytes()
+}
+
func (c *makeVarsContext) DeviceConfig() DeviceConfig {
return DeviceConfig{c.Config().deviceConfig}
}
diff --git a/android/module.go b/android/module.go
index 327e6ae..03d3f80 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,10 +16,13 @@
import (
"fmt"
+ "net/url"
"os"
"path"
"path/filepath"
+ "reflect"
"regexp"
+ "sort"
"strings"
"text/scanner"
@@ -266,7 +269,7 @@
//
// The Modules passed to the visit function should not be retained outside of the visit function, they may be
// invalidated by future mutators.
- WalkDeps(visit func(Module, Module) bool)
+ WalkDeps(visit func(child, parent Module) bool)
// WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency
// tree in top down order. visit may be called multiple times for the same (child, parent)
@@ -320,6 +323,9 @@
// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
AddUnconvertedBp2buildDep(dep string)
+ // AddMissingBp2buildDep stores the module name of a direct dependency that was not found.
+ AddMissingBp2buildDep(dep string)
+
Target() Target
TargetPrimary() bool
@@ -381,6 +387,16 @@
// for which IsInstallDepNeeded returns true.
InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
+ // InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath
+ // directory, and also unzip a zip file containing extra files to install into the same
+ // directory.
+ //
+ // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
+ // installed file will be returned by PackagingSpecs() on this module or by
+ // TransitivePackagingSpecs() on modules that depend on this module through dependency tags
+ // for which IsInstallDepNeeded returns true.
+ InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...Path) InstallPath
+
// InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath
// directory.
//
@@ -409,7 +425,6 @@
PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec
CheckbuildFile(srcPath Path)
- TidyFile(srcPath Path)
InstallInData() bool
InstallInTestcases() bool
@@ -420,7 +435,6 @@
InstallInRecovery() bool
InstallInRoot() bool
InstallInVendor() bool
- InstallBypassMake() bool
InstallForceOS() (*OsType, *ArchType)
RequiredModuleNames() []string
@@ -487,7 +501,6 @@
InstallInRecovery() bool
InstallInRoot() bool
InstallInVendor() bool
- InstallBypassMake() bool
InstallForceOS() (*OsType, *ArchType)
HideFromMake()
IsHideFromMake() bool
@@ -509,6 +522,7 @@
// Bp2buildTargets returns the target(s) generated for Bazel via bp2build for this module
Bp2buildTargets() []bp2buildInfo
GetUnconvertedBp2buildDeps() []string
+ GetMissingBp2buildDeps() []string
BuildParamsForTests() []BuildParams
RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
@@ -604,6 +618,53 @@
Tag *string `android:"arch_variant"`
}
+// NamedPath associates a path with a name. e.g. a license text path with a package name
+type NamedPath struct {
+ Path Path
+ Name string
+}
+
+// String returns an escaped string representing the `NamedPath`.
+func (p NamedPath) String() string {
+ if len(p.Name) > 0 {
+ return p.Path.String() + ":" + url.QueryEscape(p.Name)
+ }
+ return p.Path.String()
+}
+
+// NamedPaths describes a list of paths each associated with a name.
+type NamedPaths []NamedPath
+
+// Strings returns a list of escaped strings representing each `NamedPath` in the list.
+func (l NamedPaths) Strings() []string {
+ result := make([]string, 0, len(l))
+ for _, p := range l {
+ result = append(result, p.String())
+ }
+ return result
+}
+
+// SortedUniqueNamedPaths modifies `l` in place to return the sorted unique subset.
+func SortedUniqueNamedPaths(l NamedPaths) NamedPaths {
+ if len(l) == 0 {
+ return l
+ }
+ sort.Slice(l, func(i, j int) bool {
+ return l[i].String() < l[j].String()
+ })
+ k := 0
+ for i := 1; i < len(l); i++ {
+ if l[i].String() == l[k].String() {
+ continue
+ }
+ k++
+ if k < i {
+ l[k] = l[i]
+ }
+ }
+ return l[:k+1]
+}
+
type nameProperties struct {
// The name of the module. Must be unique across all modules.
Name *string
@@ -672,7 +733,7 @@
// Override of module name when reporting licenses
Effective_package_name *string `blueprint:"mutated"`
// Notice files
- Effective_license_text Paths `blueprint:"mutated"`
+ Effective_license_text NamedPaths `blueprint:"mutated"`
// License names
Effective_license_kinds []string `blueprint:"mutated"`
// License conditions
@@ -850,6 +911,9 @@
// 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"`
}
// CommonAttributes represents the common Bazel attributes from which properties
@@ -862,6 +926,13 @@
Data bazel.LabelListAttribute
}
+// constraintAttributes represents Bazel attributes pertaining to build constraints,
+// which make restrict building a Bazel target for some set of platforms.
+type constraintAttributes struct {
+ // Constraint values this target can be built for.
+ Target_compatible_with bazel.LabelListAttribute
+}
+
type distProperties struct {
// configuration to distribute output files from this module to the distribution
// directory (default: $OUT/dist, configurable with $DIST_DIR)
@@ -1018,9 +1089,6 @@
initProductVariableModule(m)
- base.generalProperties = m.GetProperties()
- base.customizableProperties = m.GetProperties()
-
// The default_visibility property needs to be checked and parsed by the visibility module during
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(m, "visibility", &base.commonProperties.Visibility)
@@ -1082,7 +1150,8 @@
m.base().commonProperties.CreateCommonOSVariant = true
}
-func (attrs *CommonAttributes) fillCommonBp2BuildModuleAttrs(ctx *topDownMutatorContext) {
+func (attrs *CommonAttributes) fillCommonBp2BuildModuleAttrs(ctx *topDownMutatorContext,
+ enabledPropertyOverrides bazel.BoolAttribute) constraintAttributes {
// Assert passed-in attributes include Name
name := attrs.Name
if len(name) == 0 {
@@ -1100,14 +1169,101 @@
required := depsToLabelList(props.Required)
archVariantProps := mod.GetArchVariantProperties(ctx, &commonProperties{})
+
+ var enabledProperty bazel.BoolAttribute
+ if props.Enabled != nil {
+ enabledProperty.Value = props.Enabled
+ }
+
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 enabledPropertyOverrides.Value != nil {
+ enabledProperty.Value = enabledPropertyOverrides.Value
+ }
+ for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() {
+ configToBools := enabledPropertyOverrides.ConfigurableValues[axis]
+ for cfg, val := range configToBools {
+ enabledProperty.SetSelectValue(axis, cfg, &val)
+ }
+ }
+
+ productConfigEnabledLabels := []bazel.Label{}
+ if !proptools.BoolDefault(enabledProperty.Value, true) {
+ // If the module is not enabled by default, then we can check if a
+ // product variable enables it
+ productConfigEnabledLabels = productVariableConfigEnableLabels(ctx)
+
+ if len(productConfigEnabledLabels) > 0 {
+ // In this case, an existing product variable configuration overrides any
+ // module-level `enable: false` definition
+ newValue := true
+ enabledProperty.Value = &newValue
+ }
+ }
+
+ productConfigEnabledAttribute := bazel.MakeLabelListAttribute(bazel.LabelList{
+ 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{}, nil})
+ if err != nil {
+ ctx.ModuleErrorf("Error processing platform enabled attribute: %s", err)
+ }
+
data.Append(required)
+
+ constraints := constraintAttributes{}
+ moduleEnableConstraints := bazel.LabelListAttribute{}
+ moduleEnableConstraints.Append(platformEnabledAttribute)
+ moduleEnableConstraints.Append(productConfigEnabledAttribute)
+ constraints.Target_compatible_with = moduleEnableConstraints
+
+ return constraints
+}
+
+// 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)
+ productConfigEnablingTargets := []bazel.Label{}
+ const propName = "Enabled"
+ if productConfigProps, exists := productVariableProps[propName]; exists {
+ for productConfigProp, prop := range productConfigProps {
+ flag, ok := prop.(*bool)
+ if !ok {
+ ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
+ }
+
+ if *flag {
+ axis := productConfigProp.ConfigurationAxis()
+ targetLabel := axis.SelectKey(productConfigProp.SelectKey())
+ productConfigEnablingTargets = append(productConfigEnablingTargets, bazel.Label{
+ Label: targetLabel,
+ })
+ } else {
+ // TODO(b/210546943): handle negative case where `enabled: false`
+ ctx.ModuleErrorf("`enabled: false` is not currently supported for configuration variables. See b/210546943", proptools.PropertyNameForField(propName))
+ }
+ }
+ }
+
+ return productConfigEnablingTargets
}
// A ModuleBase object contains the properties that are common to all Android
@@ -1138,7 +1294,7 @@
// }
// }
//
-// func NewMyModule() android.Module) {
+// func NewMyModule() android.Module {
// m := &myModule{}
// m.AddProperties(&m.properties)
// android.InitAndroidModule(m)
@@ -1162,17 +1318,14 @@
distProperties distProperties
variableProperties interface{}
hostAndDeviceProperties hostAndDeviceProperties
- generalProperties []interface{}
- // Arch specific versions of structs in generalProperties. The outer index
- // has the same order as generalProperties as initialized in
- // InitAndroidArchModule, and the inner index chooses the props specific to
- // the architecture. The interface{} value is an archPropRoot that is
- // filled with arch specific values by the arch mutator.
+ // Arch specific versions of structs in GetProperties() prior to
+ // initialization in InitAndroidArchModule, lets call it `generalProperties`.
+ // The outer index has the same order as generalProperties and the inner index
+ // chooses the props specific to the architecture. The interface{} value is an
+ // archPropRoot that is filled with arch specific values by the arch mutator.
archProperties [][]interface{}
- customizableProperties []interface{}
-
// Properties specific to the Blueprint to BUILD migration.
bazelTargetModuleProperties bazel.BazelTargetModuleProperties
@@ -1190,11 +1343,13 @@
installFiles InstallPaths
installFilesDepSet *installPathsDepSet
checkbuildFiles Paths
- tidyFiles Paths
packagingSpecs []PackagingSpec
packagingSpecsDepSet *packagingSpecsDepSet
noticeFiles Paths
- phonies map[string]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
+ katiSymlinks katiInstalls
// The files to copy to the dist as explicitly specified in the .bp file.
distFiles TaggedDistFiles
@@ -1203,7 +1358,6 @@
// Only set on the final variant of each module
installTarget WritablePath
checkbuildTarget WritablePath
- tidyTarget WritablePath
blueprintDir string
hooks hooks
@@ -1217,14 +1371,22 @@
initRcPaths Paths
vintfFragmentsPaths Paths
+
+ // set of dependency module:location mappings used to populate the license metadata for
+ // apex containers.
+ licenseInstallMap []string
+
+ // The path to the generated license metadata file for the module.
+ licenseMetadataFile WritablePath
}
// A struct containing all relevant information about a Bazel target converted via bp2build.
type bp2buildInfo struct {
- Dir string
- BazelProps bazel.BazelTargetModuleProperties
- CommonAttrs CommonAttributes
- Attrs interface{}
+ Dir string
+ BazelProps bazel.BazelTargetModuleProperties
+ CommonAttrs CommonAttributes
+ ConstraintAttrs constraintAttributes
+ Attrs interface{}
}
// TargetName returns the Bazel target name of a bp2build converted target.
@@ -1250,7 +1412,7 @@
// BazelAttributes returns the Bazel attributes of a bp2build converted target.
func (b bp2buildInfo) BazelAttributes() []interface{} {
- return []interface{}{&b.CommonAttrs, b.Attrs}
+ return []interface{}{&b.CommonAttrs, &b.ConstraintAttrs, b.Attrs}
}
func (m *ModuleBase) addBp2buildInfo(info bp2buildInfo) {
@@ -1273,20 +1435,90 @@
*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 = 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 m.commonProperties.UnconvertedBp2buildDeps
+ return FirstUniqueStrings(m.commonProperties.UnconvertedBp2buildDeps)
+}
+
+// GetMissingBp2buildDeps eturns the list of module names that were not found in Android.bp files.
+func (m *ModuleBase) GetMissingBp2buildDeps() []string {
+ return FirstUniqueStrings(m.commonProperties.MissingBp2buildDeps)
}
func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
- (*d)["Android"] = map[string]interface{}{}
+ (*d)["Android"] = map[string]interface{}{
+ // Properties set in Blueprint or in blueprint of a defaults modules
+ "SetProperties": m.propertiesWithValues(),
+ }
+}
+
+type propInfo struct {
+ Name string
+ Type string
+}
+
+func (m *ModuleBase) propertiesWithValues() []propInfo {
+ var info []propInfo
+ props := m.GetProperties()
+
+ var propsWithValues func(name string, v reflect.Value)
+ propsWithValues = func(name string, v reflect.Value) {
+ kind := v.Kind()
+ switch kind {
+ case reflect.Ptr, reflect.Interface:
+ if v.IsNil() {
+ return
+ }
+ propsWithValues(name, v.Elem())
+ case reflect.Struct:
+ if v.IsZero() {
+ return
+ }
+ for i := 0; i < v.NumField(); i++ {
+ namePrefix := name
+ sTyp := v.Type().Field(i)
+ if proptools.ShouldSkipProperty(sTyp) {
+ continue
+ }
+ if name != "" && !strings.HasSuffix(namePrefix, ".") {
+ namePrefix += "."
+ }
+ if !proptools.IsEmbedded(sTyp) {
+ namePrefix += sTyp.Name
+ }
+ sVal := v.Field(i)
+ propsWithValues(namePrefix, sVal)
+ }
+ case reflect.Array, reflect.Slice:
+ if v.IsNil() {
+ return
+ }
+ elKind := v.Type().Elem().Kind()
+ info = append(info, propInfo{name, elKind.String() + " " + kind.String()})
+ default:
+ info = append(info, propInfo{name, kind.String()})
+ }
+ }
+
+ for _, p := range props {
+ propsWithValues("", reflect.ValueOf(p).Elem())
+ }
+ return info
}
func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
+// AddProperties "registers" the provided props
+// each value in props MUST be a pointer to a struct
func (m *ModuleBase) AddProperties(props ...interface{}) {
m.registerProps = append(m.registerProps, props...)
}
@@ -1618,7 +1850,11 @@
}
func (m *ModuleBase) EffectiveLicenseFiles() Paths {
- return m.commonProperties.Effective_license_text
+ result := make(Paths, 0, len(m.commonProperties.Effective_license_text))
+ for _, p := range m.commonProperties.Effective_license_text {
+ result = append(result, p.Path)
+ }
+ return result
}
// computeInstallDeps finds the installed paths of all dependencies that have a dependency
@@ -1688,10 +1924,6 @@
return false
}
-func (m *ModuleBase) InstallBypassMake() bool {
- return false
-}
-
func (m *ModuleBase) InstallForceOS() (*OsType, *ArchType) {
return nil, nil
}
@@ -1761,15 +1993,29 @@
return append(Paths{}, m.vintfFragmentsPaths...)
}
+func (m *ModuleBase) CompileMultilib() *string {
+ return m.base().commonProperties.Compile_multilib
+}
+
+// SetLicenseInstallMap stores the set of dependency module:location mappings for files in an
+// apex container for use when generation the license metadata file.
+func (m *ModuleBase) SetLicenseInstallMap(installMap []string) {
+ m.licenseInstallMap = append(m.licenseInstallMap, installMap...)
+}
+
func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
var allInstalledFiles InstallPaths
var allCheckbuildFiles Paths
- var allTidyFiles Paths
ctx.VisitAllModuleVariants(func(module Module) {
a := module.base()
allInstalledFiles = append(allInstalledFiles, a.installFiles...)
- allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
- allTidyFiles = append(allTidyFiles, a.tidyFiles...)
+ // A module's -checkbuild phony targets should
+ // not be created if the module is not exported to make.
+ // Those could depend on the build target and fail to compile
+ // for the current build target.
+ if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(a) {
+ allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
+ }
})
var deps Paths
@@ -1793,13 +2039,6 @@
deps = append(deps, m.checkbuildTarget)
}
- if len(allTidyFiles) > 0 {
- name := namespacePrefix + ctx.ModuleName() + "-tidy"
- ctx.Phony(name, allTidyFiles...)
- m.tidyTarget = PathForPhony(ctx, name)
- deps = append(deps, m.tidyTarget)
- }
-
if len(deps) > 0 {
suffix := ""
if ctx.Config().KatiEnabled() {
@@ -1898,6 +2137,8 @@
variables: make(map[string]string),
}
+ m.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic")
+
dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx)
// set m.installFilesDepSet to only the transitive dependencies to be used as the dependencies
// of installed files of this module. It will be replaced by a depset including the installed
@@ -2008,11 +2249,9 @@
m.installFiles = append(m.installFiles, ctx.installFiles...)
m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
- m.tidyFiles = append(m.tidyFiles, ctx.tidyFiles...)
m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
- for k, v := range ctx.phonies {
- m.phonies[k] = append(m.phonies[k], v...)
- }
+ m.katiInstalls = append(m.katiInstalls, ctx.katiInstalls...)
+ m.katiSymlinks = append(m.katiSymlinks, ctx.katiSymlinks...)
} else if ctx.Config().AllowMissingDependencies() {
// If the module is not enabled it will not create any build rules, nothing will call
// ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
@@ -2031,6 +2270,8 @@
m.installFilesDepSet = newInstallPathsDepSet(m.installFiles, dependencyInstallFiles)
m.packagingSpecsDepSet = newPackagingSpecsDepSet(m.packagingSpecs, dependencyPackagingSpecs)
+ buildLicenseMetadata(ctx, m.licenseMetadataFile)
+
m.buildParams = ctx.buildParams
m.ruleParams = ctx.ruleParams
m.variables = ctx.variables
@@ -2077,18 +2318,18 @@
return GlobFiles(e, globPattern, excludes)
}
-func (b *earlyModuleContext) IsSymlink(path Path) bool {
- fileInfo, err := b.config.fs.Lstat(path.String())
+func (e *earlyModuleContext) IsSymlink(path Path) bool {
+ fileInfo, err := e.config.fs.Lstat(path.String())
if err != nil {
- b.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err)
+ e.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err)
}
return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink
}
-func (b *earlyModuleContext) Readlink(path Path) string {
- dest, err := b.config.fs.Readlink(path.String())
+func (e *earlyModuleContext) Readlink(path Path) string {
+ dest, err := e.config.fs.Readlink(path.String())
if err != nil {
- b.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err)
+ e.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err)
}
return dest
}
@@ -2207,16 +2448,61 @@
packagingSpecs []PackagingSpec
installFiles InstallPaths
checkbuildFiles Paths
- tidyFiles Paths
module Module
phonies map[string]Paths
+ katiInstalls []katiInstall
+ katiSymlinks []katiInstall
+
// For tests
buildParams []BuildParams
ruleParams map[blueprint.Rule]blueprint.RuleParams
variables map[string]string
}
+// katiInstall stores a request from Soong to Make to create an install rule.
+type katiInstall struct {
+ from Path
+ to InstallPath
+ implicitDeps Paths
+ orderOnlyDeps Paths
+ executable bool
+ extraFiles *extraFilesZip
+
+ absFrom string
+}
+
+type extraFilesZip struct {
+ zip Path
+ dir InstallPath
+}
+
+type katiInstalls []katiInstall
+
+// BuiltInstalled returns the katiInstalls in the form used by $(call copy-many-files) in Make, a
+// space separated list of from:to tuples.
+func (installs katiInstalls) BuiltInstalled() string {
+ sb := strings.Builder{}
+ for i, install := range installs {
+ if i != 0 {
+ sb.WriteRune(' ')
+ }
+ sb.WriteString(install.from.String())
+ sb.WriteRune(':')
+ sb.WriteString(install.to.String())
+ }
+ return sb.String()
+}
+
+// InstallPaths returns the install path of each entry.
+func (installs katiInstalls) InstallPaths() InstallPaths {
+ paths := make(InstallPaths, 0, len(installs))
+ for _, install := range installs {
+ paths = append(paths, install.to)
+ }
+ return paths
+}
+
func (m *moduleContext) ninjaError(params BuildParams, err error) (PackageContext, BuildParams) {
return pctx, BuildParams{
Rule: ErrorRule,
@@ -2397,7 +2683,7 @@
}
if aModule == nil {
- b.ModuleErrorf("module %q not an android module", b.OtherModuleName(module))
+ b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag)
return nil
}
@@ -2519,8 +2805,8 @@
func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
b.bp.VisitDirectDeps(func(module blueprint.Module) {
- if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
- if b.bp.OtherModuleDependencyTag(aModule) == tag {
+ if b.bp.OtherModuleDependencyTag(module) == tag {
+ if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
visit(aModule)
}
}
@@ -2765,10 +3051,6 @@
return m.module.InstallInRoot()
}
-func (m *moduleContext) InstallBypassMake() bool {
- return m.module.InstallBypassMake()
-}
-
func (m *moduleContext) InstallForceOS() (*OsType, *ArchType) {
return m.module.InstallForceOS()
}
@@ -2793,23 +3075,25 @@
return true
}
- if m.Device() {
- if m.Config().KatiEnabled() && !m.InstallBypassMake() {
- return true
- }
- }
-
return false
}
func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
deps ...Path) InstallPath {
- return m.installFile(installPath, name, srcPath, deps, false)
+ return m.installFile(installPath, name, srcPath, deps, false, nil)
}
func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
deps ...Path) InstallPath {
- return m.installFile(installPath, name, srcPath, deps, true)
+ return m.installFile(installPath, name, srcPath, deps, true, nil)
+}
+
+func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path,
+ extraZip Path, deps ...Path) InstallPath {
+ return m.installFile(installPath, name, srcPath, deps, false, &extraFilesZip{
+ zip: extraZip,
+ dir: installPath,
+ })
}
func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec {
@@ -2818,17 +3102,20 @@
}
func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
+ licenseFiles := m.Module().EffectiveLicenseFiles()
spec := PackagingSpec{
- relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
- srcPath: srcPath,
- symlinkTarget: "",
- executable: executable,
+ relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+ srcPath: srcPath,
+ symlinkTarget: "",
+ executable: executable,
+ effectiveLicenseFiles: &licenseFiles,
}
m.packagingSpecs = append(m.packagingSpecs, spec)
return spec
}
-func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path, executable bool) InstallPath {
+func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path,
+ executable bool, extraZip *extraFilesZip) InstallPath {
fullInstallPath := installPath.Join(m, name)
m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
@@ -2846,20 +3133,44 @@
orderOnlyDeps = deps
}
- rule := Cp
- if executable {
- rule = CpExecutable
- }
+ if m.Config().KatiEnabled() {
+ // When creating the install rule in Soong but embedding in Make, write the rule to a
+ // makefile instead of directly to the ninja file so that main.mk can add the
+ // dependencies from the `required` property that are hard to resolve in Soong.
+ m.katiInstalls = append(m.katiInstalls, katiInstall{
+ from: srcPath,
+ to: fullInstallPath,
+ implicitDeps: implicitDeps,
+ orderOnlyDeps: orderOnlyDeps,
+ executable: executable,
+ extraFiles: extraZip,
+ })
+ } else {
+ rule := Cp
+ if executable {
+ rule = CpExecutable
+ }
- m.Build(pctx, BuildParams{
- Rule: rule,
- Description: "install " + fullInstallPath.Base(),
- Output: fullInstallPath,
- Input: srcPath,
- Implicits: implicitDeps,
- OrderOnly: orderOnlyDeps,
- Default: !m.Config().KatiEnabled(),
- })
+ extraCmds := ""
+ if extraZip != nil {
+ extraCmds += fmt.Sprintf(" && unzip -qDD -d '%s' '%s'",
+ extraZip.dir.String(), extraZip.zip.String())
+ implicitDeps = append(implicitDeps, extraZip.zip)
+ }
+
+ m.Build(pctx, BuildParams{
+ Rule: rule,
+ Description: "install " + fullInstallPath.Base(),
+ Output: fullInstallPath,
+ Input: srcPath,
+ Implicits: implicitDeps,
+ OrderOnly: orderOnlyDeps,
+ Default: !m.Config().KatiEnabled(),
+ Args: map[string]string{
+ "extraCmds": extraCmds,
+ },
+ })
+ }
m.installFiles = append(m.installFiles, fullInstallPath)
}
@@ -2881,16 +3192,30 @@
}
if !m.skipInstall() {
- m.Build(pctx, BuildParams{
- Rule: Symlink,
- Description: "install symlink " + fullInstallPath.Base(),
- Output: fullInstallPath,
- Input: srcPath,
- Default: !m.Config().KatiEnabled(),
- Args: map[string]string{
- "fromPath": relPath,
- },
- })
+ if m.Config().KatiEnabled() {
+ // When creating the symlink rule in Soong but embedding in Make, write the rule to a
+ // makefile instead of directly to the ninja file so that main.mk can add the
+ // dependencies from the `required` property that are hard to resolve in Soong.
+ m.katiSymlinks = append(m.katiSymlinks, katiInstall{
+ from: srcPath,
+ to: fullInstallPath,
+ })
+ } else {
+ // The symlink doesn't need updating when the target is modified, but we sometimes
+ // have a dependency on a symlink to a binary instead of to the binary directly, and
+ // the mtime of the symlink must be updated when the binary is modified, so use a
+ // normal dependency here instead of an order-only dependency.
+ m.Build(pctx, BuildParams{
+ Rule: Symlink,
+ Description: "install symlink " + fullInstallPath.Base(),
+ Output: fullInstallPath,
+ Input: srcPath,
+ Default: !m.Config().KatiEnabled(),
+ Args: map[string]string{
+ "fromPath": relPath,
+ },
+ })
+ }
m.installFiles = append(m.installFiles, fullInstallPath)
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
@@ -2913,15 +3238,25 @@
m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
if !m.skipInstall() {
- m.Build(pctx, BuildParams{
- Rule: Symlink,
- Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
- Output: fullInstallPath,
- Default: !m.Config().KatiEnabled(),
- Args: map[string]string{
- "fromPath": absPath,
- },
- })
+ if m.Config().KatiEnabled() {
+ // When creating the symlink rule in Soong but embedding in Make, write the rule to a
+ // makefile instead of directly to the ninja file so that main.mk can add the
+ // dependencies from the `required` property that are hard to resolve in Soong.
+ m.katiSymlinks = append(m.katiSymlinks, katiInstall{
+ absFrom: absPath,
+ to: fullInstallPath,
+ })
+ } else {
+ m.Build(pctx, BuildParams{
+ Rule: Symlink,
+ Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
+ Output: fullInstallPath,
+ Default: !m.Config().KatiEnabled(),
+ Args: map[string]string{
+ "fromPath": absPath,
+ },
+ })
+ }
m.installFiles = append(m.installFiles, fullInstallPath)
}
@@ -2940,10 +3275,6 @@
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
}
-func (m *moduleContext) TidyFile(srcPath Path) {
- m.tidyFiles = append(m.tidyFiles, srcPath)
-}
-
func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
return m.bp
}
@@ -3202,9 +3533,10 @@
type buildTargetSingleton struct{}
-func addAncestors(ctx SingletonContext, dirMap map[string]Paths, mmName func(string) string) []string {
+func AddAncestors(ctx SingletonContext, dirMap map[string]Paths, mmName func(string) string) ([]string, []string) {
// 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)
for _, dir := range dirs {
dir := parentDir(dir)
@@ -3217,34 +3549,31 @@
}
}
dirs = SortedStringKeys(dirMap)
+ var topDirs []string
for _, dir := range dirs {
p := parentDir(dir)
if p != "." && p != "/" {
dirMap[p] = append(dirMap[p], PathForPhony(ctx, mmName(dir)))
+ } else if dir != "." && dir != "/" && dir != "" {
+ topDirs = append(topDirs, dir)
}
}
- return SortedStringKeys(dirMap)
+ return SortedStringKeys(dirMap), topDirs
}
func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) {
var checkbuildDeps Paths
- var tidyDeps Paths
mmTarget := func(dir string) string {
return "MODULES-IN-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
}
- mmTidyTarget := func(dir string) string {
- return "tidy-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
- }
modulesInDir := make(map[string]Paths)
- tidyModulesInDir := make(map[string]Paths)
ctx.VisitAllModules(func(module Module) {
blueprintDir := module.base().blueprintDir
installTarget := module.base().installTarget
checkbuildTarget := module.base().checkbuildTarget
- tidyTarget := module.base().tidyTarget
if checkbuildTarget != nil {
checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
@@ -3254,16 +3583,6 @@
if installTarget != nil {
modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget)
}
-
- if tidyTarget != nil {
- tidyDeps = append(tidyDeps, tidyTarget)
- // tidyTarget is in modulesInDir so it will be built with "mm".
- modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], tidyTarget)
- // tidyModulesInDir contains tidyTarget but not checkbuildTarget
- // or installTarget, so tidy targets in a directory can be built
- // without other checkbuild or install targets.
- tidyModulesInDir[blueprintDir] = append(tidyModulesInDir[blueprintDir], tidyTarget)
- }
})
suffix := ""
@@ -3274,24 +3593,12 @@
// Create a top-level checkbuild target that depends on all modules
ctx.Phony("checkbuild"+suffix, checkbuildDeps...)
- // Create a top-level tidy target that depends on all modules
- ctx.Phony("tidy"+suffix, tidyDeps...)
-
- dirs := addAncestors(ctx, tidyModulesInDir, mmTidyTarget)
-
- // Kati does not generate tidy-* phony targets yet.
- // Create a tidy-<directory> target that depends on all subdirectories
- // and modules in the directory.
- for _, dir := range dirs {
- ctx.Phony(mmTidyTarget(dir), tidyModulesInDir[dir]...)
- }
-
// Make will generate the MODULES-IN-* targets
if ctx.Config().KatiEnabled() {
return
}
- dirs = addAncestors(ctx, modulesInDir, mmTarget)
+ dirs, _ := AddAncestors(ctx, modulesInDir, mmTarget)
// Create a MODULES-IN-<directory> target that depends on all modules in a directory, and
// depends on the MODULES-IN-* targets of all of its subdirectories that contain Android.bp
diff --git a/android/module_test.go b/android/module_test.go
index 9e2b0ca..a1bab6d 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -15,7 +15,12 @@
package android
import (
+ "bytes"
+ "path/filepath"
+ "runtime"
"testing"
+
+ mkparser "android/soong/androidmk/parser"
)
func TestSrcIsModule(t *testing.T) {
@@ -200,16 +205,23 @@
}
func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ outputFile := PathForModuleOut(ctx, ctx.ModuleName())
+ ctx.Build(pctx, BuildParams{
+ Rule: Touch,
+ Output: outputFile,
+ })
+ installFile := ctx.InstallFile(PathForModuleInstall(ctx), ctx.ModuleName(), outputFile)
+ ctx.InstallSymlink(PathForModuleInstall(ctx, "symlinks"), ctx.ModuleName(), installFile)
}
func (m *depsModule) DepsMutator(ctx BottomUpMutatorContext) {
- ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+ ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
}
func depsModuleFactory() Module {
m := &depsModule{}
m.AddProperties(&m.props)
- InitAndroidModule(m)
+ InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
return m
}
@@ -320,3 +332,604 @@
ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)).
RunTestWithBp(t, bp)
}
+
+func TestInstall(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("requires linux")
+ }
+ bp := `
+ deps {
+ name: "foo",
+ deps: ["bar"],
+ }
+
+ deps {
+ name: "bar",
+ deps: ["baz", "qux"],
+ }
+
+ deps {
+ name: "baz",
+ deps: ["qux"],
+ }
+
+ deps {
+ name: "qux",
+ }
+ `
+
+ result := GroupFixturePreparers(
+ prepareForModuleTests,
+ PrepareForTestWithArchMutator,
+ ).RunTestWithBp(t, bp)
+
+ module := func(name string, host bool) TestingModule {
+ variant := "android_common"
+ if host {
+ variant = result.Config.BuildOSCommonTarget.String()
+ }
+ return result.ModuleForTests(name, variant)
+ }
+
+ outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
+
+ installRule := func(name string) TestingBuildParams {
+ return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system", name))
+ }
+
+ symlinkRule := func(name string) TestingBuildParams {
+ return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system/symlinks", name))
+ }
+
+ hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
+
+ hostInstallRule := func(name string) TestingBuildParams {
+ return module(name, true).Output(filepath.Join("out/soong/host/linux-x86", name))
+ }
+
+ hostSymlinkRule := func(name string) TestingBuildParams {
+ return module(name, true).Output(filepath.Join("out/soong/host/linux-x86/symlinks", name))
+ }
+
+ assertInputs := func(params TestingBuildParams, inputs ...Path) {
+ t.Helper()
+ AssertArrayString(t, "expected inputs", Paths(inputs).Strings(),
+ append(PathsIfNonNil(params.Input), params.Inputs...).Strings())
+ }
+
+ assertImplicits := func(params TestingBuildParams, implicits ...Path) {
+ t.Helper()
+ AssertArrayString(t, "expected implicit dependencies", Paths(implicits).Strings(),
+ append(PathsIfNonNil(params.Implicit), params.Implicits...).Strings())
+ }
+
+ assertOrderOnlys := func(params TestingBuildParams, orderonlys ...Path) {
+ t.Helper()
+ AssertArrayString(t, "expected orderonly dependencies", Paths(orderonlys).Strings(),
+ params.OrderOnly.Strings())
+ }
+
+ // Check host install rule dependencies
+ assertInputs(hostInstallRule("foo"), hostOutputRule("foo").Output)
+ assertImplicits(hostInstallRule("foo"),
+ hostInstallRule("bar").Output,
+ hostSymlinkRule("bar").Output,
+ hostInstallRule("baz").Output,
+ hostSymlinkRule("baz").Output,
+ hostInstallRule("qux").Output,
+ hostSymlinkRule("qux").Output,
+ )
+ assertOrderOnlys(hostInstallRule("foo"))
+
+ // Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an
+ // order-only dependency, so that the tool gets updated when the symlink is depended on.
+ assertInputs(hostSymlinkRule("foo"), hostInstallRule("foo").Output)
+ assertImplicits(hostSymlinkRule("foo"))
+ assertOrderOnlys(hostSymlinkRule("foo"))
+
+ // Check device install rule dependencies
+ assertInputs(installRule("foo"), outputRule("foo").Output)
+ assertImplicits(installRule("foo"))
+ assertOrderOnlys(installRule("foo"),
+ installRule("bar").Output,
+ symlinkRule("bar").Output,
+ installRule("baz").Output,
+ symlinkRule("baz").Output,
+ installRule("qux").Output,
+ symlinkRule("qux").Output,
+ )
+
+ // Check device symlink rule dependencies. Device symlinks could use an order-only dependency,
+ // but the current implementation uses a normal dependency.
+ assertInputs(symlinkRule("foo"), installRule("foo").Output)
+ assertImplicits(symlinkRule("foo"))
+ assertOrderOnlys(symlinkRule("foo"))
+}
+
+func TestInstallKatiEnabled(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("requires linux")
+ }
+ bp := `
+ deps {
+ name: "foo",
+ deps: ["bar"],
+ }
+
+ deps {
+ name: "bar",
+ deps: ["baz", "qux"],
+ }
+
+ deps {
+ name: "baz",
+ deps: ["qux"],
+ }
+
+ deps {
+ name: "qux",
+ }
+ `
+
+ result := GroupFixturePreparers(
+ prepareForModuleTests,
+ PrepareForTestWithArchMutator,
+ FixtureModifyConfig(SetKatiEnabledForTests),
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+ }),
+ ).RunTestWithBp(t, bp)
+
+ installs := result.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
+ buf := bytes.NewBuffer(append([]byte(nil), installs...))
+ parser := mkparser.NewParser("makevars", buf)
+
+ nodes, errs := parser.Parse()
+ if len(errs) > 0 {
+ t.Fatalf("error parsing install rules: %s", errs[0])
+ }
+
+ rules := parseMkRules(t, result.Config, nodes)
+
+ module := func(name string, host bool) TestingModule {
+ variant := "android_common"
+ if host {
+ variant = result.Config.BuildOSCommonTarget.String()
+ }
+ return result.ModuleForTests(name, variant)
+ }
+
+ outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
+
+ ruleForOutput := func(output string) installMakeRule {
+ for _, rule := range rules {
+ if rule.target == output {
+ return rule
+ }
+ }
+ t.Fatalf("no make install rule for %s", output)
+ return installMakeRule{}
+ }
+
+ installRule := func(name string) installMakeRule {
+ return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
+ }
+
+ symlinkRule := func(name string) installMakeRule {
+ return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
+ }
+
+ hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
+
+ hostInstallRule := func(name string) installMakeRule {
+ return ruleForOutput(filepath.Join("out/host/linux-x86", name))
+ }
+
+ hostSymlinkRule := func(name string) installMakeRule {
+ return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
+ }
+
+ assertDeps := func(rule installMakeRule, deps ...string) {
+ t.Helper()
+ AssertArrayString(t, "expected inputs", deps, rule.deps)
+ }
+
+ assertOrderOnlys := func(rule installMakeRule, orderonlys ...string) {
+ t.Helper()
+ AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.orderOnlyDeps)
+ }
+
+ // Check host install rule dependencies
+ assertDeps(hostInstallRule("foo"),
+ hostOutputRule("foo").Output.String(),
+ hostInstallRule("bar").target,
+ hostSymlinkRule("bar").target,
+ hostInstallRule("baz").target,
+ hostSymlinkRule("baz").target,
+ hostInstallRule("qux").target,
+ hostSymlinkRule("qux").target,
+ )
+ assertOrderOnlys(hostInstallRule("foo"))
+
+ // Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an
+ // order-only dependency, so that the tool gets updated when the symlink is depended on.
+ assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").target)
+ assertOrderOnlys(hostSymlinkRule("foo"))
+
+ // Check device install rule dependencies
+ assertDeps(installRule("foo"), outputRule("foo").Output.String())
+ assertOrderOnlys(installRule("foo"),
+ installRule("bar").target,
+ symlinkRule("bar").target,
+ installRule("baz").target,
+ symlinkRule("baz").target,
+ installRule("qux").target,
+ symlinkRule("qux").target,
+ )
+
+ // Check device symlink rule dependencies. Device symlinks could use an order-only dependency,
+ // but the current implementation uses a normal dependency.
+ assertDeps(symlinkRule("foo"), installRule("foo").target)
+ assertOrderOnlys(symlinkRule("foo"))
+}
+
+type installMakeRule struct {
+ target string
+ deps []string
+ orderOnlyDeps []string
+}
+
+func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []installMakeRule {
+ var rules []installMakeRule
+ for _, node := range nodes {
+ if mkParserRule, ok := node.(*mkparser.Rule); ok {
+ var rule installMakeRule
+
+ if targets := mkParserRule.Target.Words(); len(targets) == 0 {
+ t.Fatalf("no targets for rule %s", mkParserRule.Dump())
+ } else if len(targets) > 1 {
+ t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
+ } else if !targets[0].Const() {
+ t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
+ } else {
+ rule.target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
+ }
+
+ prereqList := &rule.deps
+ for _, prereq := range mkParserRule.Prerequisites.Words() {
+ if !prereq.Const() {
+ t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
+ }
+
+ if prereq.Value(nil) == "|" {
+ prereqList = &rule.orderOnlyDeps
+ continue
+ }
+
+ *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
+ }
+
+ rules = append(rules, rule)
+ }
+ }
+
+ return rules
+}
+
+type PropsTestModuleEmbedded struct {
+ Embedded_prop *string
+}
+
+type propsTestModule struct {
+ ModuleBase
+ DefaultableModuleBase
+ props struct {
+ A string `android:"arch_variant"`
+ B *bool
+ C []string
+ }
+ otherProps struct {
+ PropsTestModuleEmbedded
+
+ D *int64
+ Nested struct {
+ E *string
+ }
+ F *string `blueprint:"mutated"`
+ }
+}
+
+func propsTestModuleFactory() Module {
+ module := &propsTestModule{}
+ module.AddProperties(&module.props, &module.otherProps)
+ InitAndroidArchModule(module, HostAndDeviceSupported, MultilibBoth)
+ InitDefaultableModule(module)
+ return module
+}
+
+type propsTestModuleDefaults struct {
+ ModuleBase
+ DefaultsModuleBase
+}
+
+func propsTestModuleDefaultsFactory() Module {
+ defaults := &propsTestModuleDefaults{}
+ module := propsTestModule{}
+ defaults.AddProperties(&module.props, &module.otherProps)
+ InitDefaultsModule(defaults)
+ return defaults
+}
+
+func (p *propsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ str := "abc"
+ p.otherProps.F = &str
+}
+
+func TestUsedProperties(t *testing.T) {
+ testCases := []struct {
+ desc string
+ bp string
+ expectedProps []propInfo
+ }{
+ {
+ desc: "only name",
+ bp: `test {
+ name: "foo",
+ }
+ `,
+ expectedProps: []propInfo{
+ propInfo{"Name", "string"},
+ },
+ },
+ {
+ desc: "some props",
+ bp: `test {
+ name: "foo",
+ a: "abc",
+ b: true,
+ d: 123,
+ }
+ `,
+ expectedProps: []propInfo{
+ propInfo{"A", "string"},
+ propInfo{"B", "bool"},
+ propInfo{"D", "int64"},
+ propInfo{"Name", "string"},
+ },
+ },
+ {
+ desc: "unused non-pointer prop",
+ bp: `test {
+ name: "foo",
+ b: true,
+ d: 123,
+ }
+ `,
+ expectedProps: []propInfo{
+ // for non-pointer cannot distinguish between unused and intentionally set to empty
+ propInfo{"A", "string"},
+ propInfo{"B", "bool"},
+ propInfo{"D", "int64"},
+ propInfo{"Name", "string"},
+ },
+ },
+ {
+ desc: "nested props",
+ bp: `test {
+ name: "foo",
+ nested: {
+ e: "abc",
+ }
+ }
+ `,
+ expectedProps: []propInfo{
+ propInfo{"Nested.E", "string"},
+ propInfo{"Name", "string"},
+ },
+ },
+ {
+ desc: "arch props",
+ bp: `test {
+ name: "foo",
+ arch: {
+ x86_64: {
+ a: "abc",
+ },
+ }
+ }
+ `,
+ expectedProps: []propInfo{
+ propInfo{"Name", "string"},
+ propInfo{"Arch.X86_64.A", "string"},
+ },
+ },
+ {
+ desc: "embedded props",
+ bp: `test {
+ name: "foo",
+ embedded_prop: "a",
+ }
+ `,
+ expectedProps: []propInfo{
+ propInfo{"Embedded_prop", "string"},
+ propInfo{"Name", "string"},
+ },
+ },
+ {
+ desc: "defaults",
+ bp: `
+test_defaults {
+ name: "foo_defaults",
+ a: "a",
+ b: true,
+ embedded_prop:"a",
+ arch: {
+ x86_64: {
+ a: "a",
+ },
+ },
+}
+test {
+ name: "foo",
+ defaults: ["foo_defaults"],
+ c: ["a"],
+ nested: {
+ e: "d",
+ },
+ target: {
+ linux: {
+ a: "a",
+ },
+ },
+}
+ `,
+ expectedProps: []propInfo{
+ propInfo{"A", "string"},
+ propInfo{"B", "bool"},
+ propInfo{"C", "string slice"},
+ propInfo{"Embedded_prop", "string"},
+ propInfo{"Nested.E", "string"},
+ propInfo{"Name", "string"},
+ propInfo{"Arch.X86_64.A", "string"},
+ propInfo{"Target.Linux.A", "string"},
+ propInfo{"Defaults", "string slice"},
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ result := GroupFixturePreparers(
+ PrepareForTestWithAllowMissingDependencies,
+ PrepareForTestWithDefaults,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("test", propsTestModuleFactory)
+ ctx.RegisterModuleType("test_defaults", propsTestModuleDefaultsFactory)
+ }),
+ FixtureWithRootAndroidBp(tc.bp),
+ ).RunTest(t)
+
+ foo := result.ModuleForTests("foo", "").Module().base()
+
+ AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues())
+
+ })
+ }
+}
+
+func TestSortedUniqueNamedPaths(t *testing.T) {
+ type np struct {
+ path, name string
+ }
+ makePaths := func(l []np) NamedPaths {
+ result := make(NamedPaths, 0, len(l))
+ for _, p := range l {
+ result = append(result, NamedPath{PathForTesting(p.path), p.name})
+ }
+ return result
+ }
+
+ tests := []struct {
+ name string
+ in []np
+ expectedOut []np
+ }{
+ {
+ name: "empty",
+ in: []np{},
+ expectedOut: []np{},
+ },
+ {
+ name: "all_same",
+ in: []np{
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ },
+ expectedOut: []np{
+ {"a.txt", "A"},
+ },
+ },
+ {
+ name: "same_path_different_names",
+ in: []np{
+ {"a.txt", "C"},
+ {"a.txt", "A"},
+ {"a.txt", "D"},
+ {"a.txt", "B"},
+ {"a.txt", "E"},
+ },
+ expectedOut: []np{
+ {"a.txt", "A"},
+ {"a.txt", "B"},
+ {"a.txt", "C"},
+ {"a.txt", "D"},
+ {"a.txt", "E"},
+ },
+ },
+ {
+ name: "different_paths_same_name",
+ in: []np{
+ {"b/b.txt", "A"},
+ {"a/a.txt", "A"},
+ {"a/txt", "A"},
+ {"b", "A"},
+ {"a/b/d", "A"},
+ },
+ expectedOut: []np{
+ {"a/a.txt", "A"},
+ {"a/b/d", "A"},
+ {"a/txt", "A"},
+ {"b/b.txt", "A"},
+ {"b", "A"},
+ },
+ },
+ {
+ name: "all_different",
+ in: []np{
+ {"b/b.txt", "A"},
+ {"a/a.txt", "B"},
+ {"a/txt", "D"},
+ {"b", "C"},
+ {"a/b/d", "E"},
+ },
+ expectedOut: []np{
+ {"a/a.txt", "B"},
+ {"a/b/d", "E"},
+ {"a/txt", "D"},
+ {"b/b.txt", "A"},
+ {"b", "C"},
+ },
+ },
+ {
+ name: "some_different",
+ in: []np{
+ {"b/b.txt", "A"},
+ {"a/a.txt", "B"},
+ {"a/txt", "D"},
+ {"a/b/d", "E"},
+ {"b", "C"},
+ {"a/a.txt", "B"},
+ {"a/b/d", "E"},
+ },
+ expectedOut: []np{
+ {"a/a.txt", "B"},
+ {"a/b/d", "E"},
+ {"a/txt", "D"},
+ {"b/b.txt", "A"},
+ {"b", "C"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ actual := SortedUniqueNamedPaths(makePaths(tt.in))
+ expected := makePaths(tt.expectedOut)
+ t.Logf("actual: %v", actual)
+ t.Logf("expected: %v", expected)
+ AssertDeepEquals(t, "SortedUniqueNamedPaths ", expected, actual)
+ })
+ }
+}
diff --git a/android/mutator.go b/android/mutator.go
index 4b37377..739e4ee 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -16,7 +16,6 @@
import (
"reflect"
- "sync"
"android/soong/bazel"
@@ -34,12 +33,12 @@
// continue on to GenerateAndroidBuildActions
// RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
-func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators, bp2buildMutators []RegisterMutatorFunc) {
+func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
mctx := ®isterMutatorsContext{
bazelConversionMode: true,
}
- bp2buildPreArchMutators = append([]RegisterMutatorFunc{
+ bp2buildMutators := append([]RegisterMutatorFunc{
RegisterNamespaceMutator,
RegisterDefaultsPreArchMutators,
// TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should
@@ -47,10 +46,7 @@
RegisterPrebuiltsPreArchMutators,
},
preArchMutators...)
-
- for _, f := range bp2buildPreArchMutators {
- f(mctx)
- }
+ bp2buildMutators = append(bp2buildMutators, registerBp2buildConversionMutator)
// Register bp2build mutators
for _, f := range bp2buildMutators {
@@ -216,24 +212,12 @@
}
var bp2buildPreArchMutators = []RegisterMutatorFunc{}
-var bp2buildMutators = map[string]RegisterMutatorFunc{}
-// See http://b/192523357
-var bp2buildLock sync.Mutex
+// A minimal context for Bp2build conversion
+type Bp2buildMutatorContext interface {
+ BazelConversionPathContext
-// RegisterBp2BuildMutator registers specially crafted mutators for
-// converting Blueprint/Android modules into special modules that can
-// be code-generated into Bazel BUILD targets.
-//
-// TODO(b/178068862): bring this into TestContext.
-func RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
- f := func(ctx RegisterMutatorsContext) {
- ctx.TopDown(moduleType, m)
- }
- // Use a lock to avoid a concurrent map write if RegisterBp2BuildMutator is called in parallel
- bp2buildLock.Lock()
- defer bp2buildLock.Unlock()
- bp2buildMutators[moduleType] = f
+ CreateBazelTargetModule(bazel.BazelTargetModuleProperties, CommonAttributes, interface{})
}
// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
@@ -270,6 +254,14 @@
// BazelTargetModuleProperties containing additional metadata for the
// bp2build codegenerator.
CreateBazelTargetModule(bazel.BazelTargetModuleProperties, CommonAttributes, interface{})
+
+ // CreateBazelTargetModuleWithRestrictions creates a BazelTargetModule by calling the
+ // factory method, just like in CreateModule, but also requires
+ // BazelTargetModuleProperties containing additional metadata for the
+ // bp2build codegenerator. The generated target is restricted to only be buildable for certain
+ // 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)
}
type topDownMutatorContext struct {
@@ -331,13 +323,13 @@
// AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
// argument to select which variant of the dependency to use. It returns a slice of modules for
// each dependency (some entries may be nil). A variant of the dependency must exist that matches
- // the all of the non-local variations of the current module, plus the variations argument.
+ // all the non-local variations of the current module, plus the variations argument.
//
// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
// new dependencies have had the current mutator called on them. If the mutator is not
// parallel this method does not affect the ordering of the current mutator pass, but will
// be ordered correctly for all future mutator passes.
- AddVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []blueprint.Module
+ AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module
// AddFarVariationDependencies adds deps as dependencies of the current module, but uses the
// variations argument to select which variant of the dependency to use. It returns a slice of
@@ -518,45 +510,34 @@
bazelProps bazel.BazelTargetModuleProperties,
commonAttrs CommonAttributes,
attrs interface{}) {
- commonAttrs.fillCommonBp2BuildModuleAttrs(t)
+ t.createBazelTargetModule(bazelProps, commonAttrs, attrs, bazel.BoolAttribute{})
+}
+
+func (t *topDownMutatorContext) CreateBazelTargetModuleWithRestrictions(
+ bazelProps bazel.BazelTargetModuleProperties,
+ commonAttrs CommonAttributes,
+ attrs interface{},
+ enabledProperty bazel.BoolAttribute) {
+ t.createBazelTargetModule(bazelProps, commonAttrs, attrs, enabledProperty)
+}
+
+func (t *topDownMutatorContext) createBazelTargetModule(
+ bazelProps bazel.BazelTargetModuleProperties,
+ commonAttrs CommonAttributes,
+ attrs interface{},
+ enabledProperty bazel.BoolAttribute) {
+ constraintAttributes := commonAttrs.fillCommonBp2BuildModuleAttrs(t, enabledProperty)
mod := t.Module()
info := bp2buildInfo{
- Dir: t.OtherModuleDir(mod),
- BazelProps: bazelProps,
- CommonAttrs: commonAttrs,
- Attrs: attrs,
+ Dir: t.OtherModuleDir(mod),
+ BazelProps: bazelProps,
+ CommonAttrs: commonAttrs,
+ ConstraintAttrs: constraintAttributes,
+ Attrs: attrs,
}
mod.base().addBp2buildInfo(info)
}
-func (t *topDownMutatorContext) AppendProperties(props ...interface{}) {
- for _, p := range props {
- err := proptools.AppendMatchingProperties(t.Module().base().customizableProperties,
- p, nil)
- if err != nil {
- if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
- t.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
- } else {
- panic(err)
- }
- }
- }
-}
-
-func (t *topDownMutatorContext) PrependProperties(props ...interface{}) {
- for _, p := range props {
- err := proptools.PrependMatchingProperties(t.Module().base().customizableProperties,
- p, nil)
- if err != nil {
- if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
- t.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
- } else {
- panic(err)
- }
- }
- }
-}
-
// android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that
// has an overridden version in android.BaseModuleContext has to be manually forwarded to BaseModuleContext to avoid
// ambiguous method errors, or it has to store a blueprint.TopDownMutatorContext non-embedded, in which case every
diff --git a/android/namespace.go b/android/namespace.go
index 4f727e1..fc7bc29 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -74,7 +74,7 @@
// A NameResolver implements blueprint.NameInterface, and implements the logic to
// find a module from namespaces based on a query string.
-// A query string can be a module name or can be be "//namespace_path:module_path"
+// A query string can be a module name or can be "//namespace_path:module_path"
type NameResolver struct {
rootNamespace *Namespace
diff --git a/android/neverallow.go b/android/neverallow.go
index a91d523..6f9ae58 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -15,6 +15,7 @@
package android
import (
+ "fmt"
"path/filepath"
"reflect"
"regexp"
@@ -27,7 +28,7 @@
// "neverallow" rules for the build system.
//
// This allows things which aren't related to the build system and are enforced
-// for sanity, in progress code refactors, or policy to be expressed in a
+// against assumptions, in progress code refactors, or policy to be expressed in a
// straightforward away disjoint from implementations and tests which should
// work regardless of these restrictions.
//
@@ -133,7 +134,6 @@
NeverAllow().
Without("name", "libhidlbase-combined-impl").
Without("name", "libhidlbase").
- Without("name", "libhidlbase_pgo").
With("product_variables.enforce_vintf_manifest.cflags", "*").
Because("manifest enforcement should be independent of ."),
@@ -150,6 +150,7 @@
func createJavaDeviceForHostRules() []Rule {
javaDeviceForHostProjectsAllowedList := []string{
+ "development/build",
"external/guava",
"external/robolectric-shadows",
"frameworks/layoutlib",
@@ -175,6 +176,7 @@
"tools/test/graphicsbenchmark/apps/sample_app",
"tools/test/graphicsbenchmark/functional_tests/java",
"vendor/xts/gts-tests/hostsidetests/gamedevicecert/apps/javatests",
+ "external/libtextclassifier/native",
}
platformVariantPropertiesAllowedList := []string{
@@ -247,7 +249,7 @@
continue
}
- if !n.appliesToProperties(properties) {
+ if !n.appliesToProperties(ctx, properties) {
continue
}
@@ -267,8 +269,12 @@
}
}
+type ValueMatcherContext interface {
+ Config() Config
+}
+
type ValueMatcher interface {
- Test(string) bool
+ Test(ValueMatcherContext, string) bool
String() string
}
@@ -276,7 +282,7 @@
expected string
}
-func (m *equalMatcher) Test(value string) bool {
+func (m *equalMatcher) Test(ctx ValueMatcherContext, value string) bool {
return m.expected == value
}
@@ -287,7 +293,7 @@
type anyMatcher struct {
}
-func (m *anyMatcher) Test(value string) bool {
+func (m *anyMatcher) Test(ctx ValueMatcherContext, value string) bool {
return true
}
@@ -301,7 +307,7 @@
prefix string
}
-func (m *startsWithMatcher) Test(value string) bool {
+func (m *startsWithMatcher) Test(ctx ValueMatcherContext, value string) bool {
return strings.HasPrefix(value, m.prefix)
}
@@ -313,7 +319,7 @@
re *regexp.Regexp
}
-func (m *regexMatcher) Test(value string) bool {
+func (m *regexMatcher) Test(ctx ValueMatcherContext, value string) bool {
return m.re.MatchString(value)
}
@@ -325,7 +331,7 @@
allowed []string
}
-func (m *notInListMatcher) Test(value string) bool {
+func (m *notInListMatcher) Test(ctx ValueMatcherContext, value string) bool {
return !InList(value, m.allowed)
}
@@ -335,7 +341,7 @@
type isSetMatcher struct{}
-func (m *isSetMatcher) Test(value string) bool {
+func (m *isSetMatcher) Test(ctx ValueMatcherContext, value string) bool {
return value != ""
}
@@ -345,11 +351,38 @@
var isSetMatcherInstance = &isSetMatcher{}
+type sdkVersionMatcher struct {
+ condition func(ctx ValueMatcherContext, spec SdkSpec) bool
+ description string
+}
+
+func (m *sdkVersionMatcher) Test(ctx ValueMatcherContext, value string) bool {
+ return m.condition(ctx, SdkSpecFromWithConfig(ctx.Config(), value))
+}
+
+func (m *sdkVersionMatcher) String() string {
+ return ".sdk-version(" + m.description + ")"
+}
+
type ruleProperty struct {
fields []string // e.x.: Vndk.Enabled
matcher ValueMatcher
}
+func (r *ruleProperty) String() string {
+ return fmt.Sprintf("%q matches: %s", strings.Join(r.fields, "."), r.matcher)
+}
+
+type ruleProperties []ruleProperty
+
+func (r ruleProperties) String() string {
+ var s []string
+ for _, r := range r {
+ s = append(s, r.String())
+ }
+ return strings.Join(s, " ")
+}
+
// A NeverAllow rule.
type Rule interface {
In(path ...string) Rule
@@ -391,8 +424,8 @@
moduleTypes []string
unlessModuleTypes []string
- props []ruleProperty
- unlessProps []ruleProperty
+ props ruleProperties
+ unlessProps ruleProperties
onlyBootclasspathJar bool
}
@@ -402,16 +435,19 @@
return &rule{directDeps: make(map[string]bool)}
}
+// In adds path(s) where this rule applies.
func (r *rule) In(path ...string) Rule {
r.paths = append(r.paths, cleanPaths(path)...)
return r
}
+// NotIn adds path(s) to that this rule does not apply to.
func (r *rule) NotIn(path ...string) Rule {
r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
return r
}
+// InDirectDeps adds dep(s) that are not allowed with this rule.
func (r *rule) InDirectDeps(deps ...string) Rule {
for _, d := range deps {
r.directDeps[d] = true
@@ -419,25 +455,30 @@
return r
}
+// WithOsClass adds osClass(es) that this rule applies to.
func (r *rule) WithOsClass(osClasses ...OsClass) Rule {
r.osClasses = append(r.osClasses, osClasses...)
return r
}
+// ModuleType adds type(s) that this rule applies to.
func (r *rule) ModuleType(types ...string) Rule {
r.moduleTypes = append(r.moduleTypes, types...)
return r
}
+// NotModuleType adds type(s) that this rule does not apply to..
func (r *rule) NotModuleType(types ...string) Rule {
r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
return r
}
+// With specifies property/value combinations that are restricted for this rule.
func (r *rule) With(properties, value string) Rule {
return r.WithMatcher(properties, selectMatcher(value))
}
+// WithMatcher specifies property/matcher combinations that are restricted for this rule.
func (r *rule) WithMatcher(properties string, matcher ValueMatcher) Rule {
r.props = append(r.props, ruleProperty{
fields: fieldNamesForProperties(properties),
@@ -446,10 +487,12 @@
return r
}
+// Without specifies property/value combinations that this rule does not apply to.
func (r *rule) Without(properties, value string) Rule {
return r.WithoutMatcher(properties, selectMatcher(value))
}
+// Without specifies property/matcher combinations that this rule does not apply to.
func (r *rule) WithoutMatcher(properties string, matcher ValueMatcher) Rule {
r.unlessProps = append(r.unlessProps, ruleProperty{
fields: fieldNamesForProperties(properties),
@@ -465,49 +508,54 @@
return &equalMatcher{expected: expected}
}
+// Because specifies a reason for this rule.
func (r *rule) Because(reason string) Rule {
r.reason = reason
return r
}
+// BootclasspathJar whether this rule only applies to Jars in the Bootclasspath
func (r *rule) BootclasspathJar() Rule {
r.onlyBootclasspathJar = true
return r
}
func (r *rule) String() string {
- s := "neverallow"
- for _, v := range r.paths {
- s += " dir:" + v + "*"
+ s := []string{"neverallow requirements. Not allowed:"}
+ if len(r.paths) > 0 {
+ s = append(s, fmt.Sprintf("in dirs: %q", r.paths))
}
- for _, v := range r.unlessPaths {
- s += " -dir:" + v + "*"
+ if len(r.moduleTypes) > 0 {
+ s = append(s, fmt.Sprintf("module types: %q", r.moduleTypes))
}
- for _, v := range r.moduleTypes {
- s += " type:" + v
+ if len(r.props) > 0 {
+ s = append(s, fmt.Sprintf("properties matching: %s", r.props))
}
- for _, v := range r.unlessModuleTypes {
- s += " -type:" + v
+ if len(r.directDeps) > 0 {
+ s = append(s, fmt.Sprintf("dep(s): %q", SortedStringKeys(r.directDeps)))
}
- for _, v := range r.props {
- s += " " + strings.Join(v.fields, ".") + v.matcher.String()
- }
- for _, v := range r.unlessProps {
- s += " -" + strings.Join(v.fields, ".") + v.matcher.String()
- }
- for k := range r.directDeps {
- s += " deps:" + k
- }
- for _, v := range r.osClasses {
- s += " os:" + v.String()
+ if len(r.osClasses) > 0 {
+ s = append(s, fmt.Sprintf("os class(es): %q", r.osClasses))
}
if r.onlyBootclasspathJar {
- s += " inBcp"
+ s = append(s, "in bootclasspath jar")
+ }
+ if len(r.unlessPaths) > 0 {
+ s = append(s, fmt.Sprintf("EXCEPT in dirs: %q", r.unlessPaths))
+ }
+ if len(r.unlessModuleTypes) > 0 {
+ s = append(s, fmt.Sprintf("EXCEPT module types: %q", r.unlessModuleTypes))
+ }
+ if len(r.unlessProps) > 0 {
+ s = append(s, fmt.Sprintf("EXCEPT properties matching: %q", r.unlessProps))
}
if len(r.reason) != 0 {
- s += " which is restricted because " + r.reason
+ s = append(s, " which is restricted because "+r.reason)
}
- return s
+ if len(s) == 1 {
+ s[0] = "neverallow requirements (empty)"
+ }
+ return strings.Join(s, "\n\t")
}
func (r *rule) appliesToPath(dir string) bool {
@@ -558,9 +606,10 @@
return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
}
-func (r *rule) appliesToProperties(properties []interface{}) bool {
- includeProps := hasAllProperties(properties, r.props)
- excludeProps := hasAnyProperty(properties, r.unlessProps)
+func (r *rule) appliesToProperties(ctx ValueMatcherContext,
+ properties []interface{}) bool {
+ includeProps := hasAllProperties(ctx, properties, r.props)
+ excludeProps := hasAnyProperty(ctx, properties, r.unlessProps)
return includeProps && !excludeProps
}
@@ -580,6 +629,16 @@
return ¬InListMatcher{allowed}
}
+func LessThanSdkVersion(sdk string) ValueMatcher {
+ return &sdkVersionMatcher{
+ condition: func(ctx ValueMatcherContext, spec SdkSpec) bool {
+ return spec.ApiLevel.LessThan(
+ SdkSpecFromWithConfig(ctx.Config(), sdk).ApiLevel)
+ },
+ description: "lessThan=" + sdk,
+ }
+}
+
// assorted utils
func cleanPaths(paths []string) []string {
@@ -598,25 +657,28 @@
return names
}
-func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
+func hasAnyProperty(ctx ValueMatcherContext, properties []interface{},
+ props []ruleProperty) bool {
for _, v := range props {
- if hasProperty(properties, v) {
+ if hasProperty(ctx, properties, v) {
return true
}
}
return false
}
-func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
+func hasAllProperties(ctx ValueMatcherContext, properties []interface{},
+ props []ruleProperty) bool {
for _, v := range props {
- if !hasProperty(properties, v) {
+ if !hasProperty(ctx, properties, v) {
return false
}
}
return true
}
-func hasProperty(properties []interface{}, prop ruleProperty) bool {
+func hasProperty(ctx ValueMatcherContext, properties []interface{},
+ prop ruleProperty) bool {
for _, propertyStruct := range properties {
propertiesValue := reflect.ValueOf(propertyStruct).Elem()
for _, v := range prop.fields {
@@ -630,7 +692,7 @@
}
check := func(value string) bool {
- return prop.matcher.Test(value)
+ return prop.matcher.Test(ctx, value)
}
if matchValue(propertiesValue, check) {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 35aadd8..59016d4 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -15,6 +15,7 @@
package android
import (
+ "regexp"
"testing"
"github.com/google/blueprint"
@@ -55,7 +56,37 @@
}`),
},
expectedErrors: []string{
- `module "libother": violates neverallow deps:not_allowed_in_direct_deps`,
+ regexp.QuoteMeta("module \"libother\": violates neverallow requirements. Not allowed:\n\tdep(s): [\"not_allowed_in_direct_deps\"]"),
+ },
+ },
+ {
+ name: "multiple constraints",
+ rules: []Rule{
+ NeverAllow().
+ InDirectDeps("not_allowed_in_direct_deps").
+ In("other").
+ ModuleType("cc_library").
+ NotIn("top").
+ NotModuleType("cc_binary"),
+ },
+ fs: map[string][]byte{
+ "top/Android.bp": []byte(`
+ cc_library {
+ name: "not_allowed_in_direct_deps",
+ }`),
+ "other/Android.bp": []byte(`
+ cc_library {
+ name: "libother",
+ static_libs: ["not_allowed_in_direct_deps"],
+ }`),
+ },
+ expectedErrors: []string{
+ regexp.QuoteMeta(`module "libother": violates neverallow requirements. Not allowed:
+ in dirs: ["other/"]
+ module types: ["cc_library"]
+ dep(s): ["not_allowed_in_direct_deps"]
+ EXCEPT in dirs: ["top/"]
+ EXCEPT module types: ["cc_binary"]`),
},
},
@@ -296,6 +327,48 @@
"Only boot images may be imported as a makefile goal.",
},
},
+ {
+ name: "min_sdk too low",
+ fs: map[string][]byte{
+ "Android.bp": []byte(`
+ java_library {
+ name: "min_sdk_too_low",
+ min_sdk_version: "30",
+ }`),
+ },
+ rules: []Rule{
+ NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
+ },
+ expectedErrors: []string{
+ "module \"min_sdk_too_low\": violates neverallow",
+ },
+ },
+ {
+ name: "min_sdk high enough",
+ fs: map[string][]byte{
+ "Android.bp": []byte(`
+ java_library {
+ name: "min_sdk_high_enough",
+ min_sdk_version: "31",
+ }`),
+ },
+ rules: []Rule{
+ NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
+ },
+ },
+ {
+ name: "current min_sdk high enough",
+ fs: map[string][]byte{
+ "Android.bp": []byte(`
+ java_library {
+ name: "current_min_sdk_high_enough",
+ min_sdk_version: "current",
+ }`),
+ },
+ rules: []Rule{
+ NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
+ },
+ },
}
var prepareForNeverAllowTest = GroupFixturePreparers(
@@ -379,9 +452,10 @@
}
type mockJavaLibraryProperties struct {
- Libs []string
- Sdk_version *string
- Uncompress_dex *bool
+ Libs []string
+ Min_sdk_version *string
+ Sdk_version *string
+ Uncompress_dex *bool
}
type mockJavaLibraryModule struct {
diff --git a/android/notices.go b/android/notices.go
index 07cf3e4..194a734 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -16,6 +16,7 @@
import (
"path/filepath"
+ "strings"
"github.com/google/blueprint"
)
@@ -100,3 +101,16 @@
HtmlGzOutput: OptionalPathForPath(htmlGzOutput),
}
}
+
+// BuildNoticeTextOutputFromLicenseMetadata writes out a notice text file based on the module's
+// generated license metadata file.
+func BuildNoticeTextOutputFromLicenseMetadata(ctx ModuleContext, outputFile WritablePath) {
+ depsFile := outputFile.ReplaceExtension(ctx, strings.TrimPrefix(outputFile.Ext()+".d", "."))
+ rule := NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("textnotice").
+ FlagWithOutput("-o ", outputFile).
+ FlagWithDepFile("-d ", depsFile).
+ Input(ctx.Module().base().licenseMetadataFile)
+ rule.Build("container_notice", "container notice file")
+}
diff --git a/android/package_ctx.go b/android/package_ctx.go
index c19debb..f354db8 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -19,6 +19,7 @@
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/remoteexec"
)
@@ -173,7 +174,7 @@
// package-scoped variable's initialization.
func (p PackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
return p.VariableFunc(name, func(ctx PackageVarContext) string {
- return ctx.Config().HostToolPath(ctx, path).String()
+ return proptools.NinjaAndShellEscape(ctx.Config().HostToolPath(ctx, path).String())
})
}
@@ -183,7 +184,7 @@
// package-scoped variable's initialization.
func (p PackageContext) HostJNIToolVariable(name, path string) blueprint.Variable {
return p.VariableFunc(name, func(ctx PackageVarContext) string {
- return ctx.Config().HostJNIToolPath(ctx, path).String()
+ return proptools.NinjaAndShellEscape(ctx.Config().HostJNIToolPath(ctx, path).String())
})
}
@@ -193,7 +194,7 @@
// part of a package-scoped variable's initialization.
func (p PackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
return p.VariableFunc(name, func(ctx PackageVarContext) string {
- return ctx.Config().HostJavaToolPath(ctx, path).String()
+ return proptools.NinjaAndShellEscape(ctx.Config().HostJavaToolPath(ctx, path).String())
})
}
diff --git a/android/package_test.go b/android/package_test.go
index 7ea10a4..65c4240 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -11,7 +11,7 @@
}{
// Package default_visibility handling is tested in visibility_test.go
{
- name: "package must not accept visibility and name properties",
+ name: "package must not accept visibility, name or licenses properties",
fs: map[string][]byte{
"top/Android.bp": []byte(`
package {
@@ -48,8 +48,7 @@
default_visibility: ["//visibility:private"],
default_applicable_licenses: ["license"],
}
-
- package {
+ package {
}`),
},
expectedErrors: []string{
diff --git a/android/packaging.go b/android/packaging.go
index 9065826..e3a0b54 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -38,6 +38,8 @@
// Whether relPathInPackage should be marked as executable or not
executable bool
+
+ effectiveLicenseFiles *Paths
}
// Get file name of installed package
@@ -54,6 +56,17 @@
return p.relPathInPackage
}
+func (p *PackagingSpec) SetRelPathInPackage(relPathInPackage string) {
+ p.relPathInPackage = relPathInPackage
+}
+
+func (p *PackagingSpec) EffectiveLicenseFiles() Paths {
+ if p.effectiveLicenseFiles == nil {
+ return Paths{}
+ }
+ return *p.effectiveLicenseFiles
+}
+
type PackageModule interface {
Module
packagingBase() *PackagingBase
@@ -214,15 +227,9 @@
return m
}
-// See PackageModule.CopyDepsToZip
-func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
- m := p.GatherPackagingSpecs(ctx)
- builder := NewRuleBuilder(pctx, ctx)
-
- dir := PathForModuleOut(ctx, ".zip")
- builder.Command().Text("rm").Flag("-rf").Text(dir.String())
- builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
-
+// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
+// entries into the specified directory.
+func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, m map[string]PackagingSpec, dir ModuleOutPath) (entries []string) {
seenDir := make(map[string]bool)
for _, k := range SortedStringKeys(m) {
ps := m[k]
@@ -243,6 +250,19 @@
}
}
+ return entries
+}
+
+// See PackageModule.CopyDepsToZip
+func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
+ m := p.GatherPackagingSpecs(ctx)
+ builder := NewRuleBuilder(pctx, ctx)
+
+ dir := PathForModuleOut(ctx, ".zip")
+ builder.Command().Text("rm").Flag("-rf").Text(dir.String())
+ builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
+ entries = p.CopySpecsToDir(ctx, builder, m, dir)
+
builder.Command().
BuiltTool("soong_zip").
FlagWithOutput("-o ", zipOut).
diff --git a/android/path_properties.go b/android/path_properties.go
index 3976880..fdc4d91 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -33,7 +33,7 @@
// The pathDepsMutator automatically adds dependencies on any module that is listed with the
// ":module" module reference syntax in a property that is tagged with `android:"path"`.
func pathDepsMutator(ctx BottomUpMutatorContext) {
- props := ctx.Module().base().generalProperties
+ props := ctx.Module().base().GetProperties()
addPathDepsForProps(ctx, props)
}
diff --git a/android/paths.go b/android/paths.go
index 2e378ba..4c69de7 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -112,7 +112,6 @@
InstallInDebugRamdisk() bool
InstallInRecovery() bool
InstallInRoot() bool
- InstallBypassMake() bool
InstallForceOS() (*OsType, *ArchType)
}
@@ -462,6 +461,13 @@
return ret
}
+// PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module.
+func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path {
+ goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin", false)
+ rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath())
+ return goBinaryInstallDir.Join(ctx, rel)
+}
+
// Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax.
// If the dependency is not found, a missingErrorDependency is returned.
// If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned.
@@ -482,11 +488,8 @@
} else if tag != "" {
return nil, fmt.Errorf("path dependency %q is not an output file producing module", path)
} else if goBinary, ok := module.(bootstrap.GoBinaryTool); ok {
- if rel, err := filepath.Rel(PathForOutput(ctx).String(), goBinary.InstallPath()); err == nil {
- return Paths{PathForOutput(ctx, rel).WithoutRel()}, nil
- } else {
- return nil, fmt.Errorf("cannot find output path for %q: %w", goBinary.InstallPath(), err)
- }
+ goBinaryPath := PathForGoBinary(ctx, goBinary)
+ return Paths{goBinaryPath}, nil
} else if srcProducer, ok := module.(SourceFileProducer); ok {
return srcProducer.Srcs(), nil
} else {
@@ -1571,6 +1574,8 @@
// For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules.
partitionDir string
+ partition string
+
// makePath indicates whether this path is for Soong (false) or Make (true).
makePath bool
}
@@ -1637,8 +1642,8 @@
return p
}
-// ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's,
-// i.e. out/ instead of out/soong/.
+// Deprecated: ToMakePath is a noop, PathForModuleInstall always returns Make paths when building
+// embedded in Make.
func (p InstallPath) ToMakePath() InstallPath {
p.makePath = true
return p
@@ -1652,6 +1657,12 @@
return makePathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
}
+// PathForHostDexInstall returns an InstallPath representing the install path for the
+// module appended with paths...
+func PathForHostDexInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
+ return makePathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", ctx.Debug(), pathComponents...)
+}
+
// PathForModuleInPartitionInstall is similar to PathForModuleInstall but partition is provided by the caller
func PathForModuleInPartitionInstall(ctx ModuleInstallPathContext, partition string, pathComponents ...string) InstallPath {
os, arch := osAndArch(ctx)
@@ -1673,9 +1684,6 @@
func makePathForInstall(ctx ModuleInstallPathContext, os OsType, arch ArchType, partition string, debug bool, pathComponents ...string) InstallPath {
ret := pathForInstall(ctx, os, arch, partition, debug, pathComponents...)
- if ctx.InstallBypassMake() && ctx.Config().KatiEnabled() {
- ret = ret.ToMakePath()
- }
return ret
}
@@ -1716,7 +1724,11 @@
basePath: basePath{partionPath, ""},
soongOutDir: ctx.Config().soongOutDir,
partitionDir: partionPath,
- makePath: false,
+ partition: partition,
+ }
+
+ if ctx.Config().KatiEnabled() {
+ base.makePath = true
}
return base.Join(ctx, pathComponents...)
@@ -1741,8 +1753,7 @@
}
func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
- rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String())
-
+ rel := Rel(ctx, strings.TrimSuffix(path.PartitionDir(), path.partition), path.String())
return "/" + rel
}
@@ -1993,10 +2004,6 @@
return m.inRoot
}
-func (m testModuleInstallPathContext) InstallBypassMake() bool {
- return false
-}
-
func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
return m.forceOS, m.forceArch
}
@@ -2049,7 +2056,12 @@
// 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.
func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
- return ioutil.WriteFile(absolutePath(path.String()), data, perm)
+ absPath := absolutePath(path.String())
+ err := os.MkdirAll(filepath.Dir(absPath), 0777)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(absPath, data, perm)
}
func RemoveAllOutputDir(path WritablePath) error {
@@ -2137,3 +2149,23 @@
}
return false
}
+
+// PathsDepSet is a thin type-safe wrapper around the generic depSet. It always uses
+// topological order.
+type PathsDepSet struct {
+ depSet
+}
+
+// newPathsDepSet returns an immutable PathsDepSet with the given direct and
+// transitive contents.
+func newPathsDepSet(direct Paths, transitive []*PathsDepSet) *PathsDepSet {
+ return &PathsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
+}
+
+// ToList returns the PathsDepSet flattened to a list in topological order.
+func (d *PathsDepSet) ToList() Paths {
+ if d == nil {
+ return nil
+ }
+ return d.depSet.ToList().(Paths)
+}
diff --git a/android/paths_test.go b/android/paths_test.go
index 3f4625d..2f87977 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -993,7 +993,7 @@
{
name: "in out dir",
buildDir: "out",
- src: "out/a/b/c",
+ src: "out/soong/a/b/c",
err: "is in output",
},
}
@@ -1486,7 +1486,8 @@
AssertPathRelativeToTopEquals(t, "install path for soong", "out/soong/target/product/test_device/system/install/path", p)
})
t.Run("install for make", func(t *testing.T) {
- p := PathForModuleInstall(ctx, "install/path").ToMakePath()
+ p := PathForModuleInstall(ctx, "install/path")
+ p.makePath = true
AssertPathRelativeToTopEquals(t, "install path for make", "out/target/product/test_device/system/install/path", p)
})
t.Run("output", func(t *testing.T) {
@@ -1500,14 +1501,12 @@
t.Run("mixture", func(t *testing.T) {
paths := Paths{
PathForModuleInstall(ctx, "install/path"),
- PathForModuleInstall(ctx, "install/path").ToMakePath(),
PathForOutput(ctx, "output/path"),
PathForSource(ctx, "source/path"),
}
expected := []string{
"out/soong/target/product/test_device/system/install/path",
- "out/target/product/test_device/system/install/path",
"out/soong/output/path",
"source/path",
}
@@ -1525,7 +1524,7 @@
fmt.Println(p.Rel(), p2.Rel())
// Output:
- // out/system/framework/boot.art out/system/framework/boot.oat
+ // out/soong/system/framework/boot.art out/soong/system/framework/boot.oat
// boot.art boot.oat
}
@@ -1539,7 +1538,7 @@
fmt.Println(p.Rel(), p2.Rel())
// Output:
- // out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex
+ // out/soong/system/framework/boot.art out/soong/system/framework/oat/arm/boot.vdex
// boot.art oat/arm/boot.vdex
}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index e189892..5843487 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -97,7 +97,10 @@
type Prebuilt struct {
properties PrebuiltProperties
- srcsSupplier PrebuiltSrcsSupplier
+ // nil if the prebuilt has no srcs property at all. See InitPrebuiltModuleWithoutSrcs.
+ srcsSupplier PrebuiltSrcsSupplier
+
+ // "-" if the prebuilt has no srcs property at all. See InitPrebuiltModuleWithoutSrcs.
srcsPropertyName string
}
@@ -177,6 +180,22 @@
// Return the src value or nil if it is not available.
type PrebuiltSrcsSupplier func(ctx BaseModuleContext, prebuilt Module) []string
+func initPrebuiltModuleCommon(module PrebuiltInterface) *Prebuilt {
+ p := module.Prebuilt()
+ module.AddProperties(&p.properties)
+ return p
+}
+
+// Initialize the module as a prebuilt module that has no dedicated property that lists its
+// sources. SingleSourcePathFromSupplier should not be called for this module.
+//
+// This is the case e.g. for header modules, which provides the headers in source form
+// regardless whether they are prebuilt or not.
+func InitPrebuiltModuleWithoutSrcs(module PrebuiltInterface) {
+ p := initPrebuiltModuleCommon(module)
+ p.srcsPropertyName = "-"
+}
+
// Initialize the module as a prebuilt module that uses the provided supplier to access the
// prebuilt sources of the module.
//
@@ -190,10 +209,6 @@
// The provided property name is used to provide helpful error messages in the event that
// a problem arises, e.g. calling SingleSourcePath() when more than one source is provided.
func InitPrebuiltModuleWithSrcSupplier(module PrebuiltInterface, srcsSupplier PrebuiltSrcsSupplier, srcsPropertyName string) {
- p := module.Prebuilt()
- module.AddProperties(&p.properties)
- module.base().customizableProperties = module.GetProperties()
-
if srcsSupplier == nil {
panic(fmt.Errorf("srcsSupplier must not be nil"))
}
@@ -201,6 +216,7 @@
panic(fmt.Errorf("srcsPropertyName must not be empty"))
}
+ p := initPrebuiltModuleCommon(module)
p.srcsSupplier = srcsSupplier
p.srcsPropertyName = srcsPropertyName
}
@@ -293,6 +309,54 @@
return nil
}
+// PrebuiltGetPreferred returns the module that is preferred for the given
+// module. That is either the module itself or the prebuilt counterpart that has
+// taken its place. The given module must be a direct dependency of the current
+// context module, and it must be the source module if both source and prebuilt
+// exist.
+//
+// This function is for use on dependencies after PrebuiltPostDepsMutator has
+// run - any dependency that is registered before that will already reference
+// the right module. This function is only safe to call after all mutators that
+// may call CreateVariations, e.g. in GenerateAndroidBuildActions.
+func PrebuiltGetPreferred(ctx BaseModuleContext, module Module) Module {
+ if !module.IsReplacedByPrebuilt() {
+ return module
+ }
+ if IsModulePrebuilt(module) {
+ // If we're given a prebuilt then assume there's no source module around.
+ return module
+ }
+
+ sourceModDepFound := false
+ var prebuiltMod Module
+
+ ctx.WalkDeps(func(child, parent Module) bool {
+ if prebuiltMod != nil {
+ return false
+ }
+ if parent == ctx.Module() {
+ // First level: Only recurse if the module is found as a direct dependency.
+ sourceModDepFound = child == module
+ return sourceModDepFound
+ }
+ // Second level: Follow PrebuiltDepTag to the prebuilt.
+ if t := ctx.OtherModuleDependencyTag(child); t == PrebuiltDepTag {
+ prebuiltMod = child
+ }
+ return false
+ })
+
+ if prebuiltMod == nil {
+ if !sourceModDepFound {
+ panic(fmt.Errorf("Failed to find source module as a direct dependency: %s", module))
+ } else {
+ panic(fmt.Errorf("Failed to find prebuilt for source module: %s", module))
+ }
+ }
+ return prebuiltMod
+}
+
func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
}
@@ -336,7 +400,7 @@
func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
m := ctx.Module()
if p := GetEmbeddedPrebuilt(m); p != nil {
- if p.srcsSupplier == nil {
+ if p.srcsSupplier == nil && p.srcsPropertyName == "" {
panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
}
if !p.properties.SourceExists {
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index a1f8e63..fa40d1f 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -510,9 +510,9 @@
ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
ctx.RegisterModuleType("source", newSourceModule)
ctx.RegisterModuleType("override_source", newOverrideSourceModule)
- ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
- ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
- ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
}
type prebuiltModule struct {
diff --git a/android/proto.go b/android/proto.go
index 0be7893..f466261 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -15,12 +15,17 @@
package android
import (
+ "android/soong/bazel"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
+const (
+ canonicalPathFromRootDefault = true
+)
+
// 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
@@ -90,7 +95,7 @@
Flags: flags,
Deps: deps,
OutTypeFlag: protoOutFlag,
- CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, true),
+ CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, canonicalPathFromRootDefault),
Dir: PathForModuleGen(ctx, "proto"),
SubDir: PathForModuleGen(ctx, "proto", ctx.ModuleDir()),
}
@@ -146,3 +151,57 @@
rule.Command().
BuiltTool("dep_fixer").Flag(depFile.String())
}
+
+// Bp2buildProtoInfo contains information necessary to pass on to language specific conversion.
+type Bp2buildProtoInfo struct {
+ Type *string
+ Name string
+}
+
+type protoAttrs struct {
+ Srcs bazel.LabelListAttribute
+ Strip_import_prefix *string
+}
+
+// Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
+// information necessary for language-specific handling.
+func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, module Module, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
+ var info Bp2buildProtoInfo
+ if srcs.IsEmpty() {
+ return info, false
+ }
+ m := module.base()
+
+ info.Name = m.Name() + "_proto"
+ attrs := protoAttrs{
+ Srcs: srcs,
+ }
+
+ 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.")
+ }
+ }
+ }
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
+ CommonAttributes{Name: info.Name},
+ &attrs)
+
+ return info, true
+}
diff --git a/android/register.go b/android/register.go
index 5984862..10e14e0 100644
--- a/android/register.go
+++ b/android/register.go
@@ -31,7 +31,7 @@
type sortableComponent interface {
// componentName returns the name of the component.
//
- // Uniquely identifies the components within the set of components used at runtimr and during
+ // Uniquely identifies the components within the set of components used at runtime and during
// tests.
componentName() string
@@ -161,6 +161,10 @@
return ctx
}
+func (ctx *Context) SetRunningAsBp2build() {
+ ctx.config.runningAsBp2Build = true
+}
+
// RegisterForBazelConversion registers an alternate shadow pipeline of
// singletons, module types and mutators to register for converting Blueprint
// files to semantically equivalent BUILD files.
@@ -174,13 +178,7 @@
t.register(ctx)
}
- bp2buildMutatorList := []RegisterMutatorFunc{}
- for t, f := range bp2buildMutators {
- ctx.config.bp2buildModuleTypeConfig[t] = true
- bp2buildMutatorList = append(bp2buildMutatorList, f)
- }
-
- RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildMutatorList)
+ RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators)
}
// Register the pipeline of singletons, module types, and mutators for
@@ -192,15 +190,6 @@
t.register(ctx)
}
- if ctx.config.BazelContext.BazelEnabled() {
- // Hydrate the configuration of bp2build-enabled module types. This is
- // required as a signal to identify which modules should be deferred to
- // Bazel in mixed builds, if it is enabled.
- for t, _ := range bp2buildMutators {
- ctx.config.bp2buildModuleTypeConfig[t] = true
- }
- }
-
mutators := collateGloballyRegisteredMutators()
mutators.registerAll(ctx)
diff --git a/android/rule_builder.go b/android/rule_builder.go
index c9a9ddd..098c1fc 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -470,7 +470,7 @@
func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
return r.Command().
- BuiltTool("dep_fixer").
+ builtToolWithoutDeps("dep_fixer").
Inputs(depFiles.Paths())
}
@@ -638,7 +638,7 @@
}
sboxCmd.Text("rm -rf").Output(r.outDir)
sboxCmd.Text("&&")
- sboxCmd.BuiltTool("sbox").
+ sboxCmd.builtToolWithoutDeps("sbox").
Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
Flag("--manifest").Input(r.sboxManifestPath)
@@ -833,10 +833,11 @@
func sboxPathForToolRel(ctx BuilderContext, path Path) string {
// Errors will be handled in RuleBuilder.Build where we have a context to report them
- relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
- if isRelOut {
- // The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
- return filepath.Join(sboxToolsSubDir, "out", relOut)
+ toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", false)
+ relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
+ if isRelOutSoong {
+ // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
+ return filepath.Join(sboxToolsSubDir, "out", relOutSoong)
}
// The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
return filepath.Join(sboxToolsSubDir, "src", path.String())
@@ -1039,6 +1040,19 @@
// It is equivalent to:
// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
+ if c.rule.ctx.Config().UseHostMusl() {
+ // If the host is using musl, assume that the tool was built against musl libc and include
+ // libc_musl.so in the sandbox.
+ // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
+ // this could be a dependency + TransitivePackagingSpecs.
+ c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
+ }
+ return c.builtToolWithoutDeps(tool)
+}
+
+// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used
+// internally by RuleBuilder for helper tools that are known to be compiled statically.
+func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index feee90f..3766bb0 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -64,10 +64,10 @@
fmt.Printf("outputs: %q\n", rule.Outputs())
// Output:
- // commands: "ld a.o b.o -o out/linked && echo success"
+ // commands: "ld a.o b.o -o out/soong/linked && echo success"
// tools: ["ld"]
// inputs: ["a.o" "b.o"]
- // outputs: ["out/linked"]
+ // outputs: ["out/soong/linked"]
}
func ExampleRuleBuilder_SymlinkOutputs() {
@@ -79,7 +79,7 @@
Tool(PathForSource(ctx, "ln")).
FlagWithInput("-s ", PathForTesting("a.o")).
SymlinkOutput(PathForOutput(ctx, "a"))
- rule.Command().Text("cp out/a out/b").
+ rule.Command().Text("cp out/soong/a out/soong/b").
ImplicitSymlinkOutput(PathForOutput(ctx, "b"))
fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
@@ -89,11 +89,11 @@
fmt.Printf("symlink_outputs: %q\n", rule.SymlinkOutputs())
// Output:
- // commands: "ln -s a.o out/a && cp out/a out/b"
+ // commands: "ln -s a.o out/soong/a && cp out/soong/a out/soong/b"
// tools: ["ln"]
// inputs: ["a.o"]
- // outputs: ["out/a" "out/b"]
- // symlink_outputs: ["out/a" "out/b"]
+ // outputs: ["out/soong/a" "out/soong/b"]
+ // symlink_outputs: ["out/soong/a" "out/soong/b"]
}
func ExampleRuleBuilder_Temporary() {
@@ -117,10 +117,10 @@
fmt.Printf("outputs: %q\n", rule.Outputs())
// Output:
- // commands: "cp a out/b && cp out/b out/c"
+ // commands: "cp a out/soong/b && cp out/soong/b out/soong/c"
// tools: ["cp"]
// inputs: ["a"]
- // outputs: ["out/c"]
+ // outputs: ["out/soong/c"]
}
func ExampleRuleBuilder_DeleteTemporaryFiles() {
@@ -145,10 +145,10 @@
fmt.Printf("outputs: %q\n", rule.Outputs())
// Output:
- // commands: "cp a out/b && cp out/b out/c && rm -f out/b"
+ // commands: "cp a out/soong/b && cp out/soong/b out/soong/c && rm -f out/soong/b"
// tools: ["cp"]
// inputs: ["a"]
- // outputs: ["out/c"]
+ // outputs: ["out/soong/c"]
}
func ExampleRuleBuilder_Installs() {
@@ -168,7 +168,7 @@
fmt.Printf("rule.Installs().String() = %q\n", rule.Installs().String())
// Output:
- // rule.Installs().String() = "out/linked:/bin/linked out/linked:/sbin/linked"
+ // rule.Installs().String() = "out/soong/linked:/bin/linked out/soong/linked:/sbin/linked"
}
func ExampleRuleBuilderCommand() {
@@ -271,7 +271,7 @@
FlagWithRspFileInputList("@", PathForOutput(ctx, "foo.rsp"), PathsForTesting("a.java", "b.java")).
String())
// Output:
- // javac @out/foo.rsp
+ // javac @out/soong/foo.rsp
}
func ExampleRuleBuilderCommand_String() {
@@ -371,15 +371,15 @@
addCommands(rule)
wantCommands := []string{
- "out_local/module/DepFile Flag FlagWithArg=arg FlagWithDepFile=out_local/module/depfile " +
- "FlagWithInput=input FlagWithOutput=out_local/module/output FlagWithRspFileInputList=out_local/rsp " +
- "Input out_local/module/Output out_local/module/SymlinkOutput Text Tool after command2 old cmd",
- "command2 out_local/module/depfile2 input2 out_local/module/output2 tool2",
- "command3 input3 out_local/module/output2 out_local/module/output3 input3 out_local/module/output2",
+ "out_local/soong/module/DepFile Flag FlagWithArg=arg FlagWithDepFile=out_local/soong/module/depfile " +
+ "FlagWithInput=input FlagWithOutput=out_local/soong/module/output FlagWithRspFileInputList=out_local/soong/rsp " +
+ "Input out_local/soong/module/Output out_local/soong/module/SymlinkOutput Text Tool after command2 old cmd",
+ "command2 out_local/soong/module/depfile2 input2 out_local/soong/module/output2 tool2",
+ "command3 input3 out_local/soong/module/output2 out_local/soong/module/output3 input3 out_local/soong/module/output2",
}
- wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer " +
- "out_local/module/DepFile out_local/module/depfile out_local/module/ImplicitDepFile out_local/module/depfile2"
+ wantDepMergerCommand := "out_local/soong/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer " +
+ "out_local/soong/module/DepFile out_local/soong/module/depfile out_local/soong/module/ImplicitDepFile out_local/soong/module/depfile2"
AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
@@ -403,13 +403,13 @@
wantCommands := []string{
"__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
"FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
- "FlagWithRspFileInputList=out_local/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
+ "FlagWithRspFileInputList=out_local/soong/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
"__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text Tool after command2 old cmd",
"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 tool2",
"command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
}
- wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
+ wantDepMergerCommand := "out_local/soong/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
@@ -433,7 +433,7 @@
wantCommands := []string{
"__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
"FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
- "FlagWithRspFileInputList=out_local/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
+ "FlagWithRspFileInputList=out_local/soong/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
"__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
"command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
diff --git a/android/sdk_version.go b/android/sdk_version.go
index c6c75a3..2004c92 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -117,7 +117,7 @@
return false
}
-// PrebuiltSdkAvailableForUnbundledBuilt tells whether this SdkSpec can have a prebuilt SDK
+// PrebuiltSdkAvailableForUnbundledBuild tells whether this SdkSpec can have a prebuilt SDK
// that can be used for unbundled builds.
func (s SdkSpec) PrebuiltSdkAvailableForUnbundledBuild() bool {
// "", "none", and "core_platform" are not available for unbundled build
@@ -157,7 +157,7 @@
return ctx.Config().AlwaysUsePrebuiltSdks()
} else if !s.ApiLevel.IsPreview() {
// validation check
- if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && s.Kind != SdkModule {
+ if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && s.Kind != SdkModule && s.Kind != SdkSystemServer {
panic(fmt.Errorf("prebuilt SDK is not not available for SdkKind=%q", s.Kind))
return false
}
@@ -212,6 +212,10 @@
)
func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec {
+ return SdkSpecFromWithConfig(ctx.Config(), str)
+}
+
+func SdkSpecFromWithConfig(config Config, str string) SdkSpec {
switch str {
// special cases first
case "":
@@ -252,7 +256,7 @@
return SdkSpec{SdkInvalid, NoneApiLevel, str}
}
- apiLevel, err := ApiLevelFromUser(ctx, versionString)
+ apiLevel, err := ApiLevelFromUserWithConfig(config, versionString)
if err != nil {
return SdkSpec{SdkInvalid, apiLevel, str}
}
diff --git a/android/sdk_version_test.go b/android/sdk_version_test.go
new file mode 100644
index 0000000..ec81782
--- /dev/null
+++ b/android/sdk_version_test.go
@@ -0,0 +1,89 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "testing"
+)
+
+func TestSdkSpecFrom(t *testing.T) {
+ testCases := []struct {
+ input string
+ expected string
+ }{
+ {
+ input: "",
+ expected: "private_current",
+ },
+ {
+ input: "none",
+ expected: "none_(no version)",
+ },
+ {
+ input: "core_platform",
+ expected: "core_platform_current",
+ },
+ {
+ input: "_",
+ expected: "invalid_(no version)",
+ },
+ {
+ input: "_31",
+ expected: "invalid_(no version)",
+ },
+ {
+ input: "system_R",
+ expected: "system_30",
+ },
+ {
+ input: "test_31",
+ expected: "test_31",
+ },
+ {
+ input: "module_current",
+ expected: "module-lib_current",
+ },
+ {
+ input: "31",
+ expected: "public_31",
+ },
+ {
+ input: "S",
+ expected: "public_31",
+ },
+ {
+ input: "current",
+ expected: "public_current",
+ },
+ {
+ input: "Tiramisu",
+ expected: "public_Tiramisu",
+ },
+ }
+
+ config := NullConfig("", "")
+
+ config.productVariables = productVariables{
+ Platform_sdk_version: intPtr(31),
+ Platform_sdk_codename: stringPtr("Tiramisu"),
+ Platform_version_active_codenames: []string{"Tiramisu"},
+ }
+
+ for _, tc := range testCases {
+ if got := SdkSpecFromWithConfig(config, tc.input).String(); tc.expected != got {
+ t.Errorf("Expected %v, got %v", tc.expected, got)
+ }
+ }
+}
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 17f6d66..bd73645 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -31,10 +31,10 @@
)
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)
+ 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)
}
type soongConfigModuleTypeImport struct {
@@ -153,7 +153,7 @@
// Then libacme_foo would build with cflags:
// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
-func soongConfigModuleTypeImportFactory() Module {
+func SoongConfigModuleTypeImportFactory() Module {
module := &soongConfigModuleTypeImport{}
module.AddProperties(&module.properties)
@@ -179,6 +179,7 @@
type soongConfigModuleTypeModule struct {
ModuleBase
+ BazelModuleBase
properties soongconfig.ModuleTypeProperties
}
@@ -262,7 +263,7 @@
// SOONG_CONFIG_acme_width := 200
//
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
-func soongConfigModuleTypeFactory() Module {
+func SoongConfigModuleTypeFactory() Module {
module := &soongConfigModuleTypeModule{}
module.AddProperties(&module.properties)
@@ -296,7 +297,7 @@
// soong_config_string_variable defines a variable and a set of possible string values for use
// in a soong_config_module_type definition.
-func soongConfigStringVariableDummyFactory() Module {
+func SoongConfigStringVariableDummyFactory() Module {
module := &soongConfigStringVariableDummyModule{}
module.AddProperties(&module.properties, &module.stringProperties)
initAndroidModuleBase(module)
@@ -305,7 +306,7 @@
// soong_config_string_variable defines a variable with true or false values for use
// in a soong_config_module_type definition.
-func soongConfigBoolVariableDummyFactory() Module {
+func SoongConfigBoolVariableDummyFactory() Module {
module := &soongConfigBoolVariableDummyModule{}
module.AddProperties(&module.properties)
initAndroidModuleBase(module)
@@ -324,6 +325,9 @@
func (*soongConfigBoolVariableDummyModule) Nameless() {}
func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+// importModuleTypes registers the module factories for a list of module types defined
+// in an Android.bp file. These module factories are scoped for the current Android.bp
+// file only.
func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
from = filepath.Clean(from)
if filepath.Ext(from) != ".bp" {
@@ -374,8 +378,12 @@
ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
return (map[string]blueprint.ModuleFactory)(nil)
}
+ defer r.Close()
mtDef, errs := soongconfig.Parse(r, from)
+ if ctx.Config().runningAsBp2Build {
+ ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(*mtDef)
+ }
if len(errs) > 0 {
reportErrors(ctx, from, errs...)
@@ -389,7 +397,7 @@
for name, moduleType := range mtDef.ModuleTypes {
factory := globalModuleTypes[moduleType.BaseModuleType]
if factory != nil {
- factories[name] = soongConfigModuleFactory(factory, moduleType)
+ factories[name] = configModuleFactory(factory, moduleType, ctx.Config().runningAsBp2Build)
} else {
reportErrors(ctx, from,
fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
@@ -404,20 +412,39 @@
}).(map[string]blueprint.ModuleFactory)
}
-// soongConfigModuleFactory takes an existing soongConfigModuleFactory and a ModuleType and returns
-// a new soongConfigModuleFactory that wraps the existing soongConfigModuleFactory and adds conditional on Soong config
-// variables.
-func soongConfigModuleFactory(factory blueprint.ModuleFactory,
- moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
-
+// 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 func() (blueprint.Module, []interface{}) {
- module, props := factory()
+ if !conditionalFactoryProps.IsValid() {
+ return factory
+ }
- conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
- props = append(props, conditionalProps.Interface())
+ return func() (blueprint.Module, []interface{}) {
+ module, props := factory()
+ conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
+ props = append(props, conditionalProps.Interface())
+ if bp2build {
+ // The loadhook is different for bp2build, since we don't want to set a specific
+ // set of property values based on a vendor var -- we want __all of them__ to
+ // generate select statements, so we put the entire soong_config_variables
+ // struct, together with the namespace representing those variables, while
+ // creating the custom module with the factory.
+ AddLoadHook(module, func(ctx LoadHookContext) {
+ if m, ok := module.(Bazelable); ok {
+ m.SetBaseModuleType(moduleType.BaseModuleType)
+ // Instead of applying all properties, keep the entire conditionalProps struct as
+ // part of the custom module so dependent modules can create the selects accordingly
+ m.setNamespacedVariableProps(namespacedVariableProperties{
+ moduleType.ConfigNamespace: []interface{}{conditionalProps.Interface()},
+ })
+ }
+ })
+ } else {
+ // Regular Soong operation wraps the existing module factory with a
+ // conditional on Soong config variables by reading the product
+ // config variables from Make.
AddLoadHook(module, func(ctx LoadHookContext) {
config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
@@ -429,10 +456,7 @@
ctx.AppendProperties(ps)
}
})
-
- return module, props
}
- } else {
- return factory
+ return module, props
}
}
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index b2f8eaa..ceb8e45 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -60,7 +60,7 @@
module_type: "test",
config_namespace: "acme",
variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
- bool_variables: ["feature2", "unused_feature"],
+ bool_variables: ["feature2", "unused_feature", "always_true"],
value_variables: ["size", "unused_size"],
properties: ["cflags", "srcs", "defaults"],
}
@@ -148,6 +148,11 @@
cflags: ["DEFAULT_B"],
}
+ test_defaults {
+ name: "foo_defaults_always_true",
+ cflags: ["DEFAULT_ALWAYS_TRUE"],
+ }
+
acme_test {
name: "foo_with_defaults",
cflags: ["-DGENERIC"],
@@ -176,6 +181,15 @@
FEATURE3: {
cflags: ["-DFEATURE3"],
},
+ always_true: {
+ defaults: ["foo_defaults_always_true"],
+ conditions_default: {
+ // verify that conditions_default is skipped if the
+ // soong config variable is true by specifying a
+ // non-existent module in conditions_default
+ defaults: ["//nonexistent:defaults"],
+ }
+ },
},
}
`
@@ -205,6 +219,7 @@
"unused_feature": "true", // unused
"unused_size": "1", // unused
"unused_string_var": "a", // unused
+ "always_true": "true",
},
}),
fooExpectedFlags: []string{
@@ -217,6 +232,7 @@
},
fooDefaultsExpectedFlags: []string{
"DEFAULT_A",
+ "DEFAULT_ALWAYS_TRUE",
"DEFAULT",
"-DGENERIC",
"-DSIZE=42",
@@ -227,7 +243,10 @@
{
name: "empty_prop_for_string_var",
preparer: fixtureForVendorVars(map[string]map[string]string{
- "acme": {"board": "soc_c"}}),
+ "acme": {
+ "board": "soc_c",
+ "always_true": "true",
+ }}),
fooExpectedFlags: []string{
"DEFAULT",
"-DGENERIC",
@@ -236,6 +255,7 @@
"-DF1_CONDITIONS_DEFAULT",
},
fooDefaultsExpectedFlags: []string{
+ "DEFAULT_ALWAYS_TRUE",
"DEFAULT",
"-DGENERIC",
},
@@ -243,7 +263,10 @@
{
name: "unused_string_var",
preparer: fixtureForVendorVars(map[string]map[string]string{
- "acme": {"board": "soc_d"}}),
+ "acme": {
+ "board": "soc_d",
+ "always_true": "true",
+ }}),
fooExpectedFlags: []string{
"DEFAULT",
"-DGENERIC",
@@ -253,14 +276,18 @@
"-DF1_CONDITIONS_DEFAULT",
},
fooDefaultsExpectedFlags: []string{
+ "DEFAULT_ALWAYS_TRUE",
"DEFAULT",
"-DGENERIC",
},
},
{
- name: "conditions_default",
- preparer: fixtureForVendorVars(map[string]map[string]string{}),
+ name: "conditions_default",
+ preparer: fixtureForVendorVars(map[string]map[string]string{
+ "acme": {
+ "always_true": "true",
+ }}),
fooExpectedFlags: []string{
"DEFAULT",
"-DGENERIC",
@@ -270,6 +297,7 @@
"-DF1_CONDITIONS_DEFAULT",
},
fooDefaultsExpectedFlags: []string{
+ "DEFAULT_ALWAYS_TRUE",
"DEFAULT",
"-DGENERIC",
},
@@ -282,10 +310,10 @@
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("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)
}),
@@ -344,10 +372,10 @@
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("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)
}),
@@ -358,6 +386,46 @@
})).RunTest(t)
}
+func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) {
+ bp := `
+ soong_config_string_variable {
+ name: "board",
+ values: ["soc_a", "soc_b", "soc_c", "soc_a"],
+ }
+
+ soong_config_module_type {
+ name: "acme_test",
+ module_type: "test",
+ config_namespace: "acme",
+ variables: ["board"],
+ properties: ["cflags", "srcs", "defaults"],
+ }
+ `
+
+ fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+ return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.VendorVars = vars
+ })
+ }
+
+ GroupFixturePreparers(
+ fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
+ PrepareForTestWithDefaults,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
+ ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
+ ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+ ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+ // TODO(b/171232169): improve the error message for non-existent properties
+ `Android.bp: soong_config_string_variable: values property error: duplicate value: "soc_a"`,
+ })).RunTest(t)
+}
+
func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
config := TestConfig(buildDir, nil, bp, fs)
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index e7fa5a0..8fe1ff1 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -9,6 +9,8 @@
"blueprint",
"blueprint-parser",
"blueprint-proptools",
+ "soong-bazel",
+ "soong-starlark-format",
],
srcs: [
"config.go",
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 34b180d..212b752 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -20,15 +20,18 @@
"reflect"
"sort"
"strings"
+ "sync"
"github.com/google/blueprint"
"github.com/google/blueprint/parser"
"github.com/google/blueprint/proptools"
+
+ "android/soong/starlark_fmt"
)
const conditionsDefault = "conditions_default"
-var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
+var SoongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
// result so each file is only parsed once.
@@ -176,10 +179,14 @@
return []error{fmt.Errorf("values property must be set")}
}
+ vals := make(map[string]bool, len(stringProps.Values))
for _, name := range stringProps.Values {
if err := checkVariableName(name); err != nil {
return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+ } else if _, ok := vals[name]; ok {
+ return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
}
+ vals[name] = true
}
v.variables[base.variable] = &stringVariable{
@@ -230,6 +237,105 @@
variables map[string]soongConfigVariable
}
+// Bp2BuildSoongConfigDefinition keeps a global record of all soong config
+// 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
+ BoolVars map[string]bool
+ ValueVars map[string]bool
+}
+
+var bp2buildSoongConfigVarsLock sync.Mutex
+
+// 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) {
+ // 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
+ bp2buildSoongConfigVarsLock.Lock()
+ defer bp2buildSoongConfigVarsLock.Unlock()
+
+ if defs.StringVars == nil {
+ defs.StringVars = make(map[string][]string)
+ }
+ if defs.BoolVars == nil {
+ defs.BoolVars = make(map[string]bool)
+ }
+ if defs.ValueVars == nil {
+ defs.ValueVars = make(map[string]bool)
+ }
+ if defs.varCache == nil {
+ defs.varCache = make(map[string]bool)
+ }
+ for _, moduleType := range mtDef.ModuleTypes {
+ for _, v := range moduleType.Variables {
+ key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
+
+ // The same variable may be used in multiple module types (for example, if need support
+ // for cc_default and java_default), only need to process once
+ if _, keyInCache := defs.varCache[key]; keyInCache {
+ continue
+ } else {
+ defs.varCache[key] = true
+ }
+
+ if strVar, ok := v.(*stringVariable); ok {
+ for _, value := range strVar.values {
+ defs.StringVars[key] = append(defs.StringVars[key], value)
+ }
+ } else if _, ok := v.(*boolVariable); ok {
+ defs.BoolVars[key] = true
+ } else if _, ok := v.(*valueVariable); ok {
+ defs.ValueVars[key] = true
+ } else {
+ panic(fmt.Errorf("Unsupported variable type: %+v", v))
+ }
+ }
+ }
+}
+
+// This is a copy of the one available in soong/android/util.go, but depending
+// on the android package causes a cyclic dependency. A refactoring here is to
+// extract common utils out from android/utils.go for other packages like this.
+func sortedStringKeys(m interface{}) []string {
+ v := reflect.ValueOf(m)
+ if v.Kind() != reflect.Map {
+ panic(fmt.Sprintf("%#v is not a map", m))
+ }
+ keys := v.MapKeys()
+ s := make([]string, 0, len(keys))
+ for _, key := range keys {
+ s = append(s, key.String())
+ }
+ sort.Strings(s)
+ return s
+}
+
+// String emits the Soong config variable definitions as Starlark dictionaries.
+func (defs Bp2BuildSoongConfigDefinitions) String() string {
+ ret := ""
+ ret += "soong_config_bool_variables = "
+ ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
+ ret += "\n\n"
+
+ ret += "soong_config_value_variables = "
+ ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
+ ret += "\n\n"
+
+ ret += "soong_config_string_variables = "
+ ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
+
+ return ret
+}
+
// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
// property layout for the Soong config variables, with each possible value an interface{} that
// contains a nil pointer to another newly constructed type that contains the affectable properties.
@@ -271,12 +377,12 @@
}
typ := reflect.StructOf([]reflect.StructField{{
- Name: soongConfigProperty,
+ Name: SoongConfigProperty,
Type: reflect.StructOf(fields),
}})
props := reflect.New(typ)
- structConditions := props.Elem().FieldByName(soongConfigProperty)
+ structConditions := props.Elem().FieldByName(SoongConfigProperty)
for i, c := range moduleType.Variables {
c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
@@ -415,7 +521,7 @@
// soong_config_variables are expected to be in the same order as moduleType.Variables.
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
var ret []interface{}
- props = props.Elem().FieldByName(soongConfigProperty)
+ props = props.Elem().FieldByName(SoongConfigProperty)
for i, c := range moduleType.Variables {
if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
return nil, err
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index 48cdfe7..a7800e8 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -364,3 +364,116 @@
}
}
}
+
+func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
+ testCases := []struct {
+ desc string
+ defs Bp2BuildSoongConfigDefinitions
+ expected string
+ }{
+ {
+ desc: "all empty",
+ defs: Bp2BuildSoongConfigDefinitions{},
+ expected: `soong_config_bool_variables = {}
+
+soong_config_value_variables = {}
+
+soong_config_string_variables = {}`}, {
+ desc: "only bool",
+ defs: Bp2BuildSoongConfigDefinitions{
+ BoolVars: map[string]bool{
+ "bool_var": true,
+ },
+ },
+ expected: `soong_config_bool_variables = {
+ "bool_var": True,
+}
+
+soong_config_value_variables = {}
+
+soong_config_string_variables = {}`}, {
+ desc: "only value vars",
+ defs: Bp2BuildSoongConfigDefinitions{
+ ValueVars: map[string]bool{
+ "value_var": true,
+ },
+ },
+ expected: `soong_config_bool_variables = {}
+
+soong_config_value_variables = {
+ "value_var": True,
+}
+
+soong_config_string_variables = {}`}, {
+ desc: "only string vars",
+ defs: Bp2BuildSoongConfigDefinitions{
+ StringVars: map[string][]string{
+ "string_var": []string{
+ "choice1",
+ "choice2",
+ "choice3",
+ },
+ },
+ },
+ expected: `soong_config_bool_variables = {}
+
+soong_config_value_variables = {}
+
+soong_config_string_variables = {
+ "string_var": [
+ "choice1",
+ "choice2",
+ "choice3",
+ ],
+}`}, {
+ desc: "all vars",
+ defs: Bp2BuildSoongConfigDefinitions{
+ BoolVars: map[string]bool{
+ "bool_var_one": true,
+ },
+ ValueVars: map[string]bool{
+ "value_var_one": true,
+ "value_var_two": true,
+ },
+ StringVars: map[string][]string{
+ "string_var_one": []string{
+ "choice1",
+ "choice2",
+ "choice3",
+ },
+ "string_var_two": []string{
+ "foo",
+ "bar",
+ },
+ },
+ },
+ expected: `soong_config_bool_variables = {
+ "bool_var_one": True,
+}
+
+soong_config_value_variables = {
+ "value_var_one": True,
+ "value_var_two": True,
+}
+
+soong_config_string_variables = {
+ "string_var_one": [
+ "choice1",
+ "choice2",
+ "choice3",
+ ],
+ "string_var_two": [
+ "foo",
+ "bar",
+ ],
+}`},
+ }
+ for _, test := range testCases {
+ t.Run(test.desc, func(t *testing.T) {
+ actual := test.defs.String()
+ if actual != test.expected {
+ t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
+ }
+ })
+ }
+}
diff --git a/android/test_suites.go b/android/test_suites.go
index 22f6cf2..55e1da7 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -60,7 +60,7 @@
for _, module := range SortedStringKeys(files) {
installedPaths = append(installedPaths, files[module]...)
}
- testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases", false).ToMakePath()
+ testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases", false)
outputFile := PathForOutput(ctx, "packaging", "robolectric-tests.zip")
rule := NewRuleBuilder(pctx, ctx)
diff --git a/android/testing.go b/android/testing.go
index b9d8fa8..39864e1 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -208,16 +208,6 @@
ctx.config.bp2buildPackageConfig = config
}
-// RegisterBp2BuildMutator registers a BazelTargetModule mutator for converting a module
-// type to the equivalent Bazel target.
-func (ctx *TestContext) RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
- f := func(ctx RegisterMutatorsContext) {
- ctx.TopDown(moduleType, m)
- }
- ctx.config.bp2buildModuleTypeConfig[moduleType] = true
- ctx.bp2buildMutators = append(ctx.bp2buildMutators, f)
-}
-
// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
// into Bazel BUILD targets that should run prior to deps and conversion.
func (ctx *TestContext) PreArchBp2BuildMutators(f RegisterMutatorFunc) {
@@ -458,7 +448,8 @@
// RegisterForBazelConversion prepares a test context for bp2build conversion.
func (ctx *TestContext) RegisterForBazelConversion() {
- RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch, ctx.bp2buildMutators)
+ ctx.SetRunningAsBp2build()
+ RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch)
}
func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
@@ -790,19 +781,21 @@
return p
}
-func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) TestingBuildParams {
+func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) (TestingBuildParams, []string) {
+ var searchedDescriptions []string
for _, p := range b.provider.BuildParamsForTests() {
+ searchedDescriptions = append(searchedDescriptions, p.Description)
if strings.Contains(p.Description, desc) {
- return b.newTestingBuildParams(p)
+ return b.newTestingBuildParams(p), searchedDescriptions
}
}
- return TestingBuildParams{}
+ return TestingBuildParams{}, searchedDescriptions
}
func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams {
- p := b.maybeBuildParamsFromDescription(desc)
+ p, searchedDescriptions := b.maybeBuildParamsFromDescription(desc)
if p.Rule == nil {
- panic(fmt.Errorf("couldn't find description %q", desc))
+ panic(fmt.Errorf("couldn't find description %q\nall descriptions:\n%s", desc, strings.Join(searchedDescriptions, "\n")))
}
return p
}
@@ -869,7 +862,8 @@
// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
// BuildParams if no rule is found.
func (b baseTestingComponent) MaybeDescription(desc string) TestingBuildParams {
- return b.maybeBuildParamsFromDescription(desc)
+ p, _ := b.maybeBuildParamsFromDescription(desc)
+ return p
}
// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
diff --git a/android/util.go b/android/util.go
index a0394f6..47c4583 100644
--- a/android/util.go
+++ b/android/util.go
@@ -65,52 +65,55 @@
return buf.String()
}
-// SortedIntKeys returns the keys of the given integer-keyed map in the ascending order
-// TODO(asmundak): once Go has generics, combine this with SortedStringKeys below.
-func SortedIntKeys(m interface{}) []int {
- v := reflect.ValueOf(m)
- if v.Kind() != reflect.Map {
- panic(fmt.Sprintf("%#v is not a map", m))
- }
- keys := v.MapKeys()
- s := make([]int, 0, len(keys))
- for _, key := range keys {
- s = append(s, int(key.Int()))
- }
- sort.Ints(s)
- return s
-}
-
-// SorterStringKeys returns the keys of the given string-keyed map in the ascending order
+// SorterStringKeys returns the keys of the given string-keyed map in the ascending order.
func SortedStringKeys(m interface{}) []string {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
panic(fmt.Sprintf("%#v is not a map", m))
}
- keys := v.MapKeys()
- s := make([]string, 0, len(keys))
- for _, key := range keys {
- s = append(s, key.String())
+ if v.Len() == 0 {
+ return nil
+ }
+ iter := v.MapRange()
+ s := make([]string, 0, v.Len())
+ for iter.Next() {
+ s = append(s, iter.Key().String())
}
sort.Strings(s)
return s
}
-// SortedStringMapValues returns the values of the string-values map in the ascending order
-func SortedStringMapValues(m interface{}) []string {
+// stringValues returns the values of the given string-valued map in randomized map order.
+func stringValues(m interface{}) []string {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
panic(fmt.Sprintf("%#v is not a map", m))
}
- keys := v.MapKeys()
- s := make([]string, 0, len(keys))
- for _, key := range keys {
- s = append(s, v.MapIndex(key).String())
+ if v.Len() == 0 {
+ return nil
}
+ iter := v.MapRange()
+ s := make([]string, 0, v.Len())
+ for iter.Next() {
+ s = append(s, iter.Value().String())
+ }
+ return s
+}
+
+// SortedStringValues returns the values of the given string-valued map in the ascending order.
+func SortedStringValues(m interface{}) []string {
+ s := stringValues(m)
sort.Strings(s)
return s
}
+// SortedUniqueStringValues returns the values of the given string-valued map in the ascending order
+// with duplicates removed.
+func SortedUniqueStringValues(m interface{}) []string {
+ s := stringValues(m)
+ return SortedUniqueStrings(s)
+}
+
// IndexList returns the index of the first occurrence of the given string in the list or -1
func IndexList(s string, list []string) int {
for i, l := range list {
diff --git a/android/util_test.go b/android/util_test.go
index 09bec01..9b9253b 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -640,3 +640,117 @@
})
}
}
+
+func TestSortedStringKeys(t *testing.T) {
+ testCases := []struct {
+ name string
+ in interface{}
+ expected []string
+ }{
+ {
+ name: "nil",
+ in: map[string]string(nil),
+ expected: nil,
+ },
+ {
+ name: "empty",
+ in: map[string]string{},
+ expected: nil,
+ },
+ {
+ name: "simple",
+ in: map[string]string{"a": "foo", "b": "bar"},
+ expected: []string{"a", "b"},
+ },
+ {
+ name: "interface values",
+ in: map[string]interface{}{"a": nil, "b": nil},
+ expected: []string{"a", "b"},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ got := SortedStringKeys(tt.in)
+ if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted %q, got %q", w, g)
+ }
+ })
+ }
+}
+
+func TestSortedStringValues(t *testing.T) {
+ testCases := []struct {
+ name string
+ in interface{}
+ expected []string
+ }{
+ {
+ name: "nil",
+ in: map[string]string(nil),
+ expected: nil,
+ },
+ {
+ name: "empty",
+ in: map[string]string{},
+ expected: nil,
+ },
+ {
+ name: "simple",
+ in: map[string]string{"foo": "a", "bar": "b"},
+ expected: []string{"a", "b"},
+ },
+ {
+ name: "duplicates",
+ in: map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+ expected: []string{"a", "b", "b"},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ got := SortedStringValues(tt.in)
+ if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted %q, got %q", w, g)
+ }
+ })
+ }
+}
+
+func TestSortedUniqueStringValues(t *testing.T) {
+ testCases := []struct {
+ name string
+ in interface{}
+ expected []string
+ }{
+ {
+ name: "nil",
+ in: map[string]string(nil),
+ expected: nil,
+ },
+ {
+ name: "empty",
+ in: map[string]string{},
+ expected: nil,
+ },
+ {
+ name: "simple",
+ in: map[string]string{"foo": "a", "bar": "b"},
+ expected: []string{"a", "b"},
+ },
+ {
+ name: "duplicates",
+ in: map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+ expected: []string{"a", "b"},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ got := SortedUniqueStringValues(tt.in)
+ if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted %q, got %q", w, g)
+ }
+ })
+ }
+}
diff --git a/android/variable.go b/android/variable.go
index c35a323..59ae919 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -20,6 +20,9 @@
"runtime"
"strings"
+ "android/soong/android/soongconfig"
+ "android/soong/bazel"
+
"github.com/google/blueprint/proptools"
)
@@ -40,6 +43,7 @@
Platform_sdk_version struct {
Asflags []string
Cflags []string
+ Cmd *string
}
Platform_sdk_version_or_codename struct {
@@ -50,6 +54,10 @@
Cmd *string
}
+ Platform_version_name struct {
+ Base_dir *string
+ }
+
// unbundled_build is a catch-all property to annotate modules that don't build in one or
// more unbundled branches, usually due to dependencies missing from the manifest.
Unbundled_build struct {
@@ -116,9 +124,12 @@
Shared_libs []string
Cmdline []string
+
+ Srcs []string
+ Exclude_srcs []string
}
- // eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
+ // eng is true for -eng builds, and can be used to turn on additional heavyweight debugging
// features.
Eng struct {
Cflags []string
@@ -145,6 +156,7 @@
Arc struct {
Cflags []string `android:"arch_variant"`
Exclude_srcs []string `android:"arch_variant"`
+ Header_libs []string `android:"arch_variant"`
Include_dirs []string `android:"arch_variant"`
Shared_libs []string `android:"arch_variant"`
Static_libs []string `android:"arch_variant"`
@@ -237,30 +249,30 @@
AppsDefaultVersionName *string `json:",omitempty"`
- Allow_missing_dependencies *bool `json:",omitempty"`
- Unbundled_build *bool `json:",omitempty"`
- Unbundled_build_apps *bool `json:",omitempty"`
- Unbundled_build_image *bool `json:",omitempty"`
- Always_use_prebuilt_sdks *bool `json:",omitempty"`
- Skip_boot_jars_check *bool `json:",omitempty"`
- Malloc_not_svelte *bool `json:",omitempty"`
- Malloc_zero_contents *bool `json:",omitempty"`
- Malloc_pattern_fill_contents *bool `json:",omitempty"`
- Safestack *bool `json:",omitempty"`
- HostStaticBinaries *bool `json:",omitempty"`
- Binder32bit *bool `json:",omitempty"`
- UseGoma *bool `json:",omitempty"`
- UseRBE *bool `json:",omitempty"`
- UseRBEJAVAC *bool `json:",omitempty"`
- UseRBER8 *bool `json:",omitempty"`
- UseRBED8 *bool `json:",omitempty"`
- Debuggable *bool `json:",omitempty"`
- Eng *bool `json:",omitempty"`
- Treble_linker_namespaces *bool `json:",omitempty"`
- Enforce_vintf_manifest *bool `json:",omitempty"`
- Uml *bool `json:",omitempty"`
- Arc *bool `json:",omitempty"`
- MinimizeJavaDebugInfo *bool `json:",omitempty"`
+ Allow_missing_dependencies *bool `json:",omitempty"`
+ Unbundled_build *bool `json:",omitempty"`
+ Unbundled_build_apps []string `json:",omitempty"`
+ Unbundled_build_image *bool `json:",omitempty"`
+ Always_use_prebuilt_sdks *bool `json:",omitempty"`
+ Skip_boot_jars_check *bool `json:",omitempty"`
+ Malloc_not_svelte *bool `json:",omitempty"`
+ Malloc_zero_contents *bool `json:",omitempty"`
+ Malloc_pattern_fill_contents *bool `json:",omitempty"`
+ Safestack *bool `json:",omitempty"`
+ HostStaticBinaries *bool `json:",omitempty"`
+ Binder32bit *bool `json:",omitempty"`
+ UseGoma *bool `json:",omitempty"`
+ UseRBE *bool `json:",omitempty"`
+ UseRBEJAVAC *bool `json:",omitempty"`
+ UseRBER8 *bool `json:",omitempty"`
+ UseRBED8 *bool `json:",omitempty"`
+ Debuggable *bool `json:",omitempty"`
+ Eng *bool `json:",omitempty"`
+ Treble_linker_namespaces *bool `json:",omitempty"`
+ Enforce_vintf_manifest *bool `json:",omitempty"`
+ Uml *bool `json:",omitempty"`
+ Arc *bool `json:",omitempty"`
+ MinimizeJavaDebugInfo *bool `json:",omitempty"`
Check_elf_files *bool `json:",omitempty"`
@@ -290,8 +302,6 @@
ClangTidy *bool `json:",omitempty"`
TidyChecks *string `json:",omitempty"`
- SamplingPGO *bool `json:",omitempty"`
-
JavaCoveragePaths []string `json:",omitempty"`
JavaCoverageExcludePaths []string `json:",omitempty"`
@@ -320,7 +330,8 @@
NamespacesToExport []string `json:",omitempty"`
- PgoAdditionalProfileDirs []string `json:",omitempty"`
+ AfdoAdditionalProfileDirs []string `json:",omitempty"`
+ PgoAdditionalProfileDirs []string `json:",omitempty"`
VndkUseCoreVariant *bool `json:",omitempty"`
VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
@@ -337,17 +348,27 @@
RecoverySnapshotDirsIncluded []string `json:",omitempty"`
HostFakeSnapshotEnabled bool `json:",omitempty"`
- BoardVendorSepolicyDirs []string `json:",omitempty"`
- BoardOdmSepolicyDirs []string `json:",omitempty"`
- BoardReqdMaskPolicy []string `json:",omitempty"`
- SystemExtPublicSepolicyDirs []string `json:",omitempty"`
- SystemExtPrivateSepolicyDirs []string `json:",omitempty"`
- BoardSepolicyM4Defs []string `json:",omitempty"`
+ BoardVendorSepolicyDirs []string `json:",omitempty"`
+ BoardOdmSepolicyDirs []string `json:",omitempty"`
+ BoardReqdMaskPolicy []string `json:",omitempty"`
+ BoardPlatVendorPolicy []string `json:",omitempty"`
+ BoardSystemExtPublicPrebuiltDirs []string `json:",omitempty"`
+ BoardSystemExtPrivatePrebuiltDirs []string `json:",omitempty"`
+ BoardProductPublicPrebuiltDirs []string `json:",omitempty"`
+ BoardProductPrivatePrebuiltDirs []string `json:",omitempty"`
+ SystemExtPublicSepolicyDirs []string `json:",omitempty"`
+ SystemExtPrivateSepolicyDirs []string `json:",omitempty"`
+ BoardSepolicyM4Defs []string `json:",omitempty"`
BoardSepolicyVers *string `json:",omitempty"`
PlatformSepolicyVersion *string `json:",omitempty"`
TotSepolicyVersion *string `json:",omitempty"`
+ SystemExtSepolicyPrebuiltApiDir *string `json:",omitempty"`
+ ProductSepolicyPrebuiltApiDir *string `json:",omitempty"`
+
+ PlatformSepolicyCompatVersions []string `json:",omitempty"`
+
VendorVars map[string]map[string]string `json:",omitempty"`
Ndk_abis *bool `json:",omitempty"`
@@ -413,6 +434,8 @@
SepolicyFreezeTestExtraDirs []string `json:",omitempty"`
SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"`
+
+ GenerateAidlNdkPlatformBackend bool `json:",omitempty"`
}
func boolPtr(v bool) *bool {
@@ -479,53 +502,418 @@
// ProductConfigProperty contains the information for a single property (may be a struct) paired
// with the appropriate ProductConfigVariable.
type ProductConfigProperty struct {
- ProductConfigVariable string
- FullConfig string
- Property interface{}
+ // The name of the product variable, e.g. "safestack", "malloc_not_svelte",
+ // "board"
+ Name string
+
+ // Namespace of the variable, if this is a soong_config_module_type variable
+ // e.g. "acme", "ANDROID", "vendor_name"
+ Namespace string
+
+ // Unique configuration to identify this product config property (i.e. a
+ // primary key), as just using the product variable name is not sufficient.
+ //
+ // For product variables, this is the product variable name + optional
+ // archvariant information. e.g.
+ //
+ // product_variables: {
+ // foo: {
+ // cflags: ["-Dfoo"],
+ // },
+ // },
+ //
+ // FullConfig would be "foo".
+ //
+ // target: {
+ // android: {
+ // product_variables: {
+ // foo: {
+ // cflags: ["-Dfoo-android"],
+ // },
+ // },
+ // },
+ // },
+ //
+ // FullConfig would be "foo-android".
+ //
+ // For soong config variables, this is the namespace + product variable name
+ // + value of the variable, if applicable. The value can also be
+ // conditions_default.
+ //
+ // e.g.
+ //
+ // soong_config_variables: {
+ // feature1: {
+ // conditions_default: {
+ // cflags: ["-DDEFAULT1"],
+ // },
+ // cflags: ["-DFEATURE1"],
+ // },
+ // }
+ //
+ // where feature1 is created in the "acme" namespace, so FullConfig would be
+ // "acme__feature1" and "acme__feature1__conditions_default".
+ //
+ // e.g.
+ //
+ // soong_config_variables: {
+ // board: {
+ // soc_a: {
+ // cflags: ["-DSOC_A"],
+ // },
+ // soc_b: {
+ // cflags: ["-DSOC_B"],
+ // },
+ // soc_c: {},
+ // conditions_default: {
+ // cflags: ["-DSOC_DEFAULT"]
+ // },
+ // },
+ // }
+ //
+ // where board is created in the "acme" namespace, so FullConfig would be
+ // "acme__board__soc_a", "acme__board__soc_b", and
+ // "acme__board__conditions_default"
+ FullConfig string
}
-// ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that
-// all it all product variable-specific versions of a property are easily accessed together
-type ProductConfigProperties map[string]map[string]ProductConfigProperty
+func (p *ProductConfigProperty) AlwaysEmit() bool {
+ return p.Namespace != ""
+}
+
+func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
+ if p.Namespace == "" {
+ return bazel.ProductVariableConfigurationAxis(p.FullConfig)
+ } 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)
+ }
+}
+
+// SelectKey returns the literal string that represents this variable in a BUILD
+// select statement.
+func (p *ProductConfigProperty) SelectKey() string {
+ if p.Namespace == "" {
+ return strings.ToLower(p.FullConfig)
+ }
+
+ if p.FullConfig == bazel.ConditionsDefaultConfigKey {
+ return bazel.ConditionsDefaultConfigKey
+ }
+
+ value := p.FullConfig
+ if value == p.Name {
+ value = ""
+ }
+
+ // e.g. acme__feature1, android__board__soc_a
+ selectKey := strings.ToLower(strings.Join([]string{p.Namespace, p.Name}, "__"))
+ if value != "" {
+ selectKey = strings.ToLower(strings.Join([]string{selectKey, value}, "__"))
+ }
+
+ return selectKey
+}
+
+// ProductConfigProperties is a map of maps to group property values according
+// their property name and the product config variable they're set under.
+//
+// The outer map key is the name of the property, like "cflags".
+//
+// The inner map key is a ProductConfigProperty, which is a struct of product
+// variable name, namespace, and the "full configuration" of the product
+// variable.
+//
+// e.g. product variable name: board, namespace: acme, full config: vendor_chip_foo
+//
+// The value of the map is the interface{} representing the value of the
+// property, like ["-DDEFINES"] for cflags.
+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 BaseMutatorContext) ProductConfigProperties {
+func ProductVariableProperties(ctx BazelConversionPathContext) ProductConfigProperties {
module := ctx.Module()
moduleBase := module.base()
productConfigProperties := ProductConfigProperties{}
- if moduleBase.variableProperties == nil {
- return productConfigProperties
- }
+ if moduleBase.variableProperties != nil {
+ productVariablesProperty := proptools.FieldNameForProperty("product_variables")
+ productVariableValues(
+ productVariablesProperty,
+ moduleBase.variableProperties,
+ "",
+ "",
+ &productConfigProperties)
- productVariableValues(moduleBase.variableProperties, "", &productConfigProperties)
-
- for _, 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
- productVariableValues(props, config, &productConfigProperties)
+ for _, 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
+ productVariableValues(
+ productVariablesProperty,
+ props,
+ "",
+ config,
+ &productConfigProperties)
+ }
}
}
+ if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil {
+ for namespace, namespacedVariableProps := range m.namespacedVariableProps() {
+ for _, namespacedVariableProp := range namespacedVariableProps {
+ productVariableValues(
+ soongconfig.SoongConfigProperty,
+ namespacedVariableProp,
+ namespace,
+ "",
+ &productConfigProperties)
+ }
+ }
+ }
+
+ productConfigProperties.zeroValuesForNamespacedVariables()
+
return productConfigProperties
}
-func productVariableValues(variableProps interface{}, suffix string, productConfigProperties *ProductConfigProperties) {
- if suffix != "" {
- suffix = "-" + suffix
+// 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
+ }
}
- variableValues := reflect.ValueOf(variableProps).Elem().FieldByName("Product_variables")
+
+ 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{}) {
+ if (*p)[propertyName] == nil {
+ (*p)[propertyName] = make(map[ProductConfigProperty]interface{})
+ }
+
+ productConfigProp := ProductConfigProperty{
+ 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
+ }
+
+ if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" {
+ switch dst := existing.(type) {
+ case []string:
+ if src, ok := property.([]string); ok {
+ dst = append(dst, src...)
+ (*p)[propertyName][productConfigProp] = dst
+ }
+ default:
+ panic(fmt.Errorf("TODO: handle merging value %s", existing))
+ }
+ } else {
+ (*p)[propertyName][productConfigProp] = property
+ }
+}
+
+var (
+ conditionsDefaultField string = proptools.FieldNameForProperty(bazel.ConditionsDefaultConfigKey)
+)
+
+// maybeExtractConfigVarProp attempts to read this value as a config var struct
+// wrapped by interfaces and ptrs. If it's not the right type, the second return
+// value is false.
+func maybeExtractConfigVarProp(v reflect.Value) (reflect.Value, bool) {
+ if v.Kind() == reflect.Interface {
+ // The conditions_default value can be either
+ // 1) an ptr to an interface of a struct (bool config variables and product variables)
+ // 2) an interface of 1) (config variables with nested structs, like string vars)
+ v = v.Elem()
+ }
+ if v.Kind() != reflect.Ptr {
+ return v, false
+ }
+ v = reflect.Indirect(v)
+ if v.Kind() == reflect.Interface {
+ // Extract the struct from the interface
+ v = v.Elem()
+ }
+
+ if !v.IsValid() {
+ return v, false
+ }
+
+ if v.Kind() != reflect.Struct {
+ return v, false
+ }
+ return v, true
+}
+
+func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value) {
+ // variableValues can either be a product_variables or
+ // soong_config_variables struct.
+ //
+ // Example of product_variables:
+ //
+ // product_variables: {
+ // malloc_not_svelte: {
+ // shared_libs: ["malloc_not_svelte_shared_lib"],
+ // whole_static_libs: ["malloc_not_svelte_whole_static_lib"],
+ // exclude_static_libs: [
+ // "malloc_not_svelte_static_lib_excludes",
+ // "malloc_not_svelte_whole_static_lib_excludes",
+ // ],
+ // },
+ // },
+ //
+ // Example of soong_config_variables:
+ //
+ // soong_config_variables: {
+ // feature1: {
+ // conditions_default: {
+ // ...
+ // },
+ // cflags: ...
+ // },
+ // feature2: {
+ // cflags: ...
+ // conditions_default: {
+ // ...
+ // },
+ // },
+ // board: {
+ // soc_a: {
+ // ...
+ // },
+ // soc_a: {
+ // ...
+ // },
+ // soc_c: {},
+ // conditions_default: {
+ // ...
+ // },
+ // },
+ // }
for i := 0; i < variableValues.NumField(); i++ {
+ // e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
+ productVariableName := variableValues.Type().Field(i).Name
+
variableValue := variableValues.Field(i)
// Check if any properties were set for the module
if variableValue.IsZero() {
+ // e.g. feature1: {}, malloc_not_svelte: {}
continue
}
- // e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
- productVariableName := variableValues.Type().Field(i).Name
+
+ // Unlike product variables, config variables require a few more
+ // indirections to extract the struct from the reflect.Value.
+ if v, ok := maybeExtractConfigVarProp(variableValue); ok {
+ variableValue = v
+ }
+
for j := 0; j < variableValue.NumField(); j++ {
property := variableValue.Field(j)
// If the property wasn't set, no need to pass it along
@@ -535,19 +923,74 @@
// e.g. Asflags, Cflags, Enabled, etc.
propertyName := variableValue.Type().Field(j).Name
- if (*productConfigProperties)[propertyName] == nil {
- (*productConfigProperties)[propertyName] = make(map[string]ProductConfigProperty)
- }
- config := productVariableName + suffix
- (*productConfigProperties)[propertyName][config] = ProductConfigProperty{
- ProductConfigVariable: productVariableName,
- FullConfig: config,
- Property: property.Interface(),
+
+ if v, ok := maybeExtractConfigVarProp(property); ok {
+ // The field is a struct, which is used by:
+ // 1) soong_config_string_variables
+ //
+ // soc_a: {
+ // cflags: ...,
+ // }
+ //
+ // soc_b: {
+ // cflags: ...,
+ // }
+ //
+ // 2) conditions_default structs for all soong config variable types.
+ //
+ // conditions_default: {
+ // cflags: ...,
+ // static_libs: ...
+ // }
+ field := v
+ for k := 0; k < field.NumField(); k++ {
+ // Iterate over fields of this struct prop.
+ if field.Field(k).IsZero() {
+ continue
+ }
+ // config can also be "conditions_default".
+ config := proptools.PropertyNameForField(propertyName)
+ actualPropertyName := field.Type().Field(k).Name
+
+ productConfigProperties.AddProductConfigProperty(
+ actualPropertyName, // e.g. cflags, static_libs
+ namespace, // e.g. acme, android
+ productVariableName, // e.g. size, feature1, FEATURE2, board
+ config,
+ field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"]
+ )
+ }
+ } else if property.Kind() != reflect.Interface {
+ // If not an interface, then this is not a conditions_default or
+ // a struct prop. That is, this is a regular product variable,
+ // or a bool/value config variable.
+ config := productVariableName + suffix
+ productConfigProperties.AddProductConfigProperty(
+ propertyName,
+ namespace,
+ productVariableName,
+ config,
+ property.Interface(),
+ )
}
}
}
}
+// productVariableValues uses reflection to convert a property struct for
+// 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) {
+ 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)
+}
+
func VariableMutator(mctx BottomUpMutatorContext) {
var module Module
var ok bool
@@ -597,7 +1040,7 @@
printfIntoProperties(ctx, prefix, productVariablePropertyValue, variableValue)
- err := proptools.AppendMatchingProperties(m.generalProperties,
+ err := proptools.AppendMatchingProperties(m.GetProperties(),
productVariablePropertyValue.Addr().Interface(), nil)
if err != nil {
if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
diff --git a/android_sdk/Android.bp b/android_sdk/Android.bp
new file mode 100644
index 0000000..e686d59
--- /dev/null
+++ b/android_sdk/Android.bp
@@ -0,0 +1,22 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-android-sdk",
+ pkgPath: "android/soong/android_sdk",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ "soong-cc",
+ "soong-cc-config",
+ ],
+ srcs: [
+ "sdk_repo_host.go",
+ ],
+ testSrcs: [
+ "sdk_repo_host_test.go",
+ ],
+ pluginFor: ["soong_build"],
+}
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
new file mode 100644
index 0000000..f050a2e
--- /dev/null
+++ b/android_sdk/sdk_repo_host.go
@@ -0,0 +1,287 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android_sdk
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/cc/config"
+)
+
+var pctx = android.NewPackageContext("android/soong/android_sdk")
+
+func init() {
+ registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_sdk_repo_host", SdkRepoHostFactory)
+}
+
+type sdkRepoHost struct {
+ android.ModuleBase
+ android.PackagingBase
+
+ properties sdkRepoHostProperties
+
+ outputBaseName string
+ outputFile android.OptionalPath
+}
+
+type remapProperties struct {
+ From string
+ To string
+}
+
+type sdkRepoHostProperties struct {
+ // The top level directory to use for the SDK repo.
+ Base_dir *string
+
+ // List of src:dst mappings to rename files from `deps`.
+ Deps_remap []remapProperties `android:"arch_variant"`
+
+ // List of zip files to merge into the SDK repo.
+ Merge_zips []string `android:"arch_variant,path"`
+
+ // List of sources to include into the SDK repo. These are usually raw files, filegroups,
+ // or genrules, as most built modules should be referenced via `deps`.
+ Srcs []string `android:"arch_variant,path"`
+
+ // List of files to strip. This should be a list of files, not modules. This happens after
+ // `deps_remap` and `merge_zips` are applied, but before the `base_dir` is added.
+ Strip_files []string `android:"arch_variant"`
+}
+
+// android_sdk_repo_host defines an Android SDK repo containing host tools.
+//
+// This implementation is trying to be a faithful reproduction of how these sdk-repos were produced
+// in the Make system, which may explain some of the oddities (like `strip_files` not being
+// automatic)
+func SdkRepoHostFactory() android.Module {
+ return newSdkRepoHostModule()
+}
+
+func newSdkRepoHostModule() *sdkRepoHost {
+ s := &sdkRepoHost{}
+ s.AddProperties(&s.properties)
+ android.InitPackageModule(s)
+ android.InitAndroidMultiTargetsArchModule(s, android.HostSupported, android.MultilibCommon)
+ return s
+}
+
+type dependencyTag struct {
+ blueprint.BaseDependencyTag
+ android.PackagingItemAlwaysDepTag
+}
+
+// TODO(b/201696252): Evaluate whether licenses should be propagated through this dependency.
+func (d dependencyTag) PropagateLicenses() bool {
+ return false
+}
+
+var depTag = dependencyTag{}
+
+func (s *sdkRepoHost) DepsMutator(ctx android.BottomUpMutatorContext) {
+ s.AddDeps(ctx, depTag)
+}
+
+func (s *sdkRepoHost) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ dir := android.PathForModuleOut(ctx, "zip")
+ builder := android.NewRuleBuilder(pctx, ctx).
+ Sbox(dir, android.PathForModuleOut(ctx, "out.sbox.textproto")).
+ SandboxInputs()
+
+ // Get files from modules listed in `deps`
+ packageSpecs := s.GatherPackagingSpecs(ctx)
+
+ // Handle `deps_remap` renames
+ err := remapPackageSpecs(packageSpecs, s.properties.Deps_remap)
+ if err != nil {
+ ctx.PropertyErrorf("deps_remap", "%s", err.Error())
+ }
+
+ s.CopySpecsToDir(ctx, builder, packageSpecs, dir)
+
+ noticeFile := android.PathForModuleOut(ctx, "NOTICES.txt")
+ android.BuildNoticeTextOutputFromLicenseMetadata(ctx, noticeFile)
+ builder.Command().Text("cp").
+ Input(noticeFile).
+ Text(filepath.Join(dir.String(), "NOTICE.txt"))
+
+ // Handle `merge_zips` by extracting their contents into our tmpdir
+ for _, zip := range android.PathsForModuleSrc(ctx, s.properties.Merge_zips) {
+ builder.Command().
+ Text("unzip").
+ Flag("-DD").
+ Flag("-q").
+ FlagWithArg("-d ", dir.String()).
+ Input(zip)
+ }
+
+ // Copy files from `srcs` into our tmpdir
+ for _, src := range android.PathsForModuleSrc(ctx, s.properties.Srcs) {
+ builder.Command().
+ Text("cp").Input(src).Flag(dir.Join(ctx, src.Rel()).String())
+ }
+
+ // Handle `strip_files` by calling the necessary strip commands
+ //
+ // Note: this stripping logic was copied over from the old Make implementation
+ // It's not using the same flags as the regular stripping support, nor does it
+ // support the array of per-module stripping options. It would be nice if we
+ // pulled the stripped versions from the CC modules, but that doesn't exist
+ // for host tools today. (And not all the things we strip are CC modules today)
+ if ctx.Darwin() {
+ macStrip := config.MacStripPath(ctx)
+ for _, strip := range s.properties.Strip_files {
+ builder.Command().
+ Text(macStrip).Flag("-x").
+ Flag(dir.Join(ctx, strip).String())
+ }
+ } else {
+ llvmStrip := config.ClangPath(ctx, "bin/llvm-strip")
+ llvmLib := config.ClangPath(ctx, "lib64/libc++.so.1")
+ for _, strip := range s.properties.Strip_files {
+ cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib)
+ if !ctx.Windows() {
+ cmd.Flag("-x")
+ }
+ cmd.Flag(dir.Join(ctx, strip).String())
+ }
+ }
+
+ // Fix up the line endings of all text files. This also removes executable permissions.
+ builder.Command().
+ Text("find").
+ Flag(dir.String()).
+ Flag("-name '*.aidl' -o -name '*.css' -o -name '*.html' -o -name '*.java'").
+ Flag("-o -name '*.js' -o -name '*.prop' -o -name '*.template'").
+ Flag("-o -name '*.txt' -o -name '*.windows' -o -name '*.xml' -print0").
+ // Using -n 500 for xargs to limit the max number of arguments per call to line_endings
+ // to 500. This avoids line_endings failing with "arguments too long".
+ Text("| xargs -0 -n 500 ").
+ BuiltTool("line_endings").
+ Flag("unix")
+
+ // Exclude some file types (roughly matching sdk.exclude.atree)
+ builder.Command().
+ Text("find").
+ Flag(dir.String()).
+ Flag("'('").
+ Flag("-name '.*' -o -name '*~' -o -name 'Makefile' -o -name 'Android.mk' -o").
+ Flag("-name '.*.swp' -o -name '.DS_Store' -o -name '*.pyc' -o -name 'OWNERS' -o").
+ Flag("-name 'MODULE_LICENSE_*' -o -name '*.ezt' -o -name 'Android.bp'").
+ Flag("')' -print0").
+ Text("| xargs -0 -r rm -rf")
+ builder.Command().
+ Text("find").
+ Flag(dir.String()).
+ Flag("-name '_*' ! -name '__*' -print0").
+ Text("| xargs -0 -r rm -rf")
+
+ if ctx.Windows() {
+ // Fix EOL chars to make window users happy
+ builder.Command().
+ Text("find").
+ Flag(dir.String()).
+ Flag("-maxdepth 2 -name '*.bat' -type f -print0").
+ Text("| xargs -0 -r unix2dos")
+ }
+
+ // Zip up our temporary directory as the sdk-repo
+ outputZipFile := dir.Join(ctx, "output.zip")
+ builder.Command().
+ BuiltTool("soong_zip").
+ FlagWithOutput("-o ", outputZipFile).
+ FlagWithArg("-P ", proptools.StringDefault(s.properties.Base_dir, ".")).
+ FlagWithArg("-C ", dir.String()).
+ FlagWithArg("-D ", dir.String())
+ builder.Command().Text("rm").Flag("-rf").Text(dir.String())
+
+ builder.Build("build_sdk_repo", "Creating sdk-repo-"+s.BaseModuleName())
+
+ osName := ctx.Os().String()
+ if osName == "linux_glibc" {
+ osName = "linux"
+ }
+ name := fmt.Sprintf("sdk-repo-%s-%s", osName, s.BaseModuleName())
+
+ s.outputBaseName = name
+ s.outputFile = android.OptionalPathForPath(outputZipFile)
+ ctx.InstallFile(android.PathForModuleInstall(ctx, "sdk-repo"), name+".zip", outputZipFile)
+}
+
+func (s *sdkRepoHost) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name)
+ fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", strings.Join(s.FilesToInstall().Strings(), " "))
+
+ fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-$(FILE_NAME_TAG).zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
+ },
+ }
+}
+
+func remapPackageSpecs(specs map[string]android.PackagingSpec, remaps []remapProperties) error {
+ for _, remap := range remaps {
+ for path, spec := range specs {
+ if match, err := pathtools.Match(remap.From, path); err != nil {
+ return fmt.Errorf("Error parsing %q: %v", remap.From, err)
+ } else if match {
+ newPath := remap.To
+ if pathtools.IsGlob(remap.From) {
+ rel, err := filepath.Rel(constantPartOfPattern(remap.From), path)
+ if err != nil {
+ return fmt.Errorf("Error handling %q", path)
+ }
+ newPath = filepath.Join(remap.To, rel)
+ }
+ delete(specs, path)
+ spec.SetRelPathInPackage(newPath)
+ specs[newPath] = spec
+ }
+ }
+ }
+ return nil
+}
+
+func constantPartOfPattern(pattern string) string {
+ ret := ""
+ for pattern != "" {
+ var first string
+ first, pattern = splitFirst(pattern)
+ if pathtools.IsGlob(first) {
+ return ret
+ }
+ ret = filepath.Join(ret, first)
+ }
+ return ret
+}
+
+func splitFirst(path string) (string, string) {
+ i := strings.IndexRune(path, filepath.Separator)
+ if i < 0 {
+ return path, ""
+ }
+ return path[:i], path[i+1:]
+}
diff --git a/android_sdk/sdk_repo_host_test.go b/android_sdk/sdk_repo_host_test.go
new file mode 100644
index 0000000..0688921
--- /dev/null
+++ b/android_sdk/sdk_repo_host_test.go
@@ -0,0 +1,124 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android_sdk
+
+import (
+ "fmt"
+ "runtime"
+ "sort"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+
+ "github.com/google/blueprint/pathtools"
+)
+
+var fixture = android.GroupFixturePreparers(
+ android.PrepareForIntegrationTestWithAndroid,
+ cc.PrepareForIntegrationTestWithCc,
+ android.FixtureRegisterWithContext(registerBuildComponents),
+)
+
+func TestSdkRepoHostDeps(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skipf("Skipping sdk_repo_host testing that is only supported on linux not %s", runtime.GOOS)
+ }
+
+ result := fixture.RunTestWithBp(t, `
+ android_sdk_repo_host {
+ name: "platform-tools",
+ }
+ `)
+
+ // produces "sdk-repo-{OS}-platform-tools.zip"
+ result.ModuleForTests("platform-tools", "linux_glibc_common").Output("sdk-repo-linux-platform-tools.zip")
+}
+
+func TestRemapPackageSpecs(t *testing.T) {
+ testcases := []struct {
+ name string
+ input []string
+ remaps []remapProperties
+ output []string
+ err string
+ }{
+ {
+ name: "basic remap",
+ input: []string{"a", "c"},
+ remaps: []remapProperties{
+ {From: "a", To: "b"},
+ },
+ output: []string{"b", "c"},
+ },
+ {
+ name: "non-matching remap",
+ input: []string{"a"},
+ remaps: []remapProperties{
+ {From: "b", To: "c"},
+ },
+ output: []string{"a"},
+ },
+ {
+ name: "glob",
+ input: []string{"bin/d", "liba.so", "libb.so", "lib/c.so"},
+ remaps: []remapProperties{
+ {From: "lib*.so", To: "lib/"},
+ },
+ output: []string{"bin/d", "lib/c.so", "lib/liba.so", "lib/libb.so"},
+ },
+ {
+ name: "bad glob",
+ input: []string{"a"},
+ remaps: []remapProperties{
+ {From: "**", To: "./"},
+ },
+ err: fmt.Sprintf("Error parsing \"**\": %v", pathtools.GlobLastRecursiveErr.Error()),
+ },
+ {
+ name: "globbed dirs",
+ input: []string{"a/b/c"},
+ remaps: []remapProperties{
+ {From: "a/*/c", To: "./"},
+ },
+ output: []string{"b/c"},
+ },
+ }
+
+ for _, test := range testcases {
+ t.Run(test.name, func(t *testing.T) {
+ specs := map[string]android.PackagingSpec{}
+ for _, input := range test.input {
+ spec := android.PackagingSpec{}
+ spec.SetRelPathInPackage(input)
+ specs[input] = spec
+ }
+
+ err := remapPackageSpecs(specs, test.remaps)
+
+ if test.err != "" {
+ android.AssertErrorMessageEquals(t, "", test.err, err)
+ } else {
+ outputs := []string{}
+ for path, spec := range specs {
+ android.AssertStringEquals(t, "path does not match rel path", path, spec.RelPathInPackage())
+ outputs = append(outputs, path)
+ }
+ sort.Strings(outputs)
+ android.AssertArrayString(t, "outputs mismatch", test.output, outputs)
+ }
+ })
+ }
+}
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 80801b2..295b0e5 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -15,11 +15,12 @@
package androidmk
import (
- mkparser "android/soong/androidmk/parser"
"fmt"
"sort"
"strings"
+ mkparser "android/soong/androidmk/parser"
+
bpparser "github.com/google/blueprint/parser"
)
@@ -57,6 +58,7 @@
"LOCAL_MODULE_STEM": stem,
"LOCAL_MODULE_HOST_OS": hostOs,
"LOCAL_RESOURCE_DIR": localizePathList("resource_dirs"),
+ "LOCAL_NOTICE_FILE": localizePathList("android_license_files"),
"LOCAL_SANITIZE": sanitize(""),
"LOCAL_SANITIZE_DIAG": sanitize("diag."),
"LOCAL_STRIP_MODULE": strip(),
@@ -66,6 +68,8 @@
"LOCAL_MODULE_PATH": prebuiltModulePath,
"LOCAL_REPLACE_PREBUILT_APK_INSTALLED": prebuiltPreprocessed,
+ "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG": invert("auto_gen_config"),
+
// composite functions
"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
@@ -110,7 +114,6 @@
"LOCAL_PROTOC_OPTIMIZE_TYPE": "proto.type",
"LOCAL_MODULE_OWNER": "owner",
"LOCAL_RENDERSCRIPT_TARGET_API": "renderscript.target_api",
- "LOCAL_NOTICE_FILE": "notice",
"LOCAL_JAVA_LANGUAGE_VERSION": "java_version",
"LOCAL_INSTRUMENTATION_FOR": "instrumentation_for",
"LOCAL_MANIFEST_FILE": "manifest",
@@ -128,6 +131,8 @@
"LOCAL_STATIC_LIBRARIES": "static_libs",
"LOCAL_WHOLE_STATIC_LIBRARIES": "whole_static_libs",
"LOCAL_SYSTEM_SHARED_LIBRARIES": "system_shared_libs",
+ "LOCAL_USES_LIBRARIES": "uses_libs",
+ "LOCAL_OPTIONAL_USES_LIBRARIES": "optional_uses_libs",
"LOCAL_ASFLAGS": "asflags",
"LOCAL_CLANG_ASFLAGS": "clang_asflags",
"LOCAL_COMPATIBILITY_SUPPORT_FILES": "data",
@@ -182,6 +187,13 @@
"LOCAL_JACK_COVERAGE_EXCLUDE_FILTER": "jacoco.exclude_filter",
"LOCAL_FULL_LIBS_MANIFEST_FILES": "additional_manifests",
+
+ // will be rewrite later to "license_kinds:" by byfix
+ "LOCAL_LICENSE_KINDS": "android_license_kinds",
+ // will be removed later by byfix
+ // TODO: does this property matter in the license module?
+ "LOCAL_LICENSE_CONDITIONS": "android_license_conditions",
+ "LOCAL_GENERATED_SOURCES": "generated_sources",
})
addStandardProperties(bpparser.BoolType,
@@ -220,6 +232,8 @@
"LOCAL_IS_UNIT_TEST": "unit_test",
"LOCAL_ENFORCE_USES_LIBRARIES": "enforce_uses_libs",
+
+ "LOCAL_CHECK_ELF_FILES": "check_elf_files",
})
}
@@ -636,6 +650,12 @@
if len(val.Variables) == 1 && varLiteralName(val.Variables[0]) != "" && len(val.Strings) == 2 && val.Strings[0] == "" {
fixed = val.Strings[1]
varname = val.Variables[0].Name.Strings[0]
+ // TARGET_OUT_OPTIONAL_EXECUTABLES puts the artifact in xbin, which is
+ // deprecated. TARGET_OUT_DATA_APPS install location will be handled
+ // automatically by Soong
+ if varname == "TARGET_OUT_OPTIONAL_EXECUTABLES" || varname == "TARGET_OUT_DATA_APPS" {
+ return nil
+ }
} else if len(val.Variables) == 2 && varLiteralName(val.Variables[0]) == "PRODUCT_OUT" && varLiteralName(val.Variables[1]) == "TARGET_COPY_OUT_VENDOR" &&
len(val.Strings) == 3 && val.Strings[0] == "" && val.Strings[1] == "/" {
fixed = val.Strings[2]
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 02ab89d..e8b6f78 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -699,7 +699,7 @@
expected: `
android_library {
srcs: ["test.java"],
- resource_dirs: ["res"],
+
jacoco: {
include_filter: ["foo.*"],
},
@@ -1121,6 +1121,25 @@
`,
},
{
+ desc: "prebuilt_root_host",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(HOST_OUT)/subdir
+LOCAL_SRC_FILES := foo.txt
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_root_host {
+ name: "foo",
+
+ src: "foo.txt",
+ relative_install_path: "subdir",
+}
+`,
+ },
+ {
desc: "prebuilt_font",
in: `
include $(CLEAR_VARS)
@@ -1439,7 +1458,7 @@
runtime_resource_overlay {
name: "foo",
product_specific: true,
- resource_dirs: ["res"],
+
sdk_version: "current",
theme: "FooTheme",
@@ -1479,6 +1498,183 @@
}
`,
},
+ {
+ desc: "LOCAL_USES_LIBRARIES",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_USES_LIBRARIES := foo.test bar.test baz.test
+include $(BUILD_PACKAGE)
+`,
+ expected: `
+android_app {
+ name: "foo",
+ uses_libs: [
+ "foo.test",
+ "bar.test",
+ "baz.test",
+ ],
+}
+`,
+ },
+ {
+ desc: "LOCAL_OPTIONAL_USES_LIBRARIES",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_OPTIONAL_USES_LIBRARIES := foo.test bar.test baz.test
+include $(BUILD_PACKAGE)
+`,
+ expected: `
+android_app {
+ name: "foo",
+ optional_uses_libs: [
+ "foo.test",
+ "bar.test",
+ "baz.test",
+ ],
+}
+`,
+ },
+ {
+ desc: "Obsolete LOCAL_MODULE_PATH",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_CTS_TEST_PACKAGE := bar
+LOCAL_USE_AAPT2 := blah
+include $(BUILD_PACKAGE)
+`,
+ expected: `
+android_app {
+ name: "foo",
+
+}
+`,
+ },
+ {
+ desc: "LOCAL_LICENSE_KINDS, LOCAL_LICENSE_CONDITIONS, LOCAL_NOTICE_FILE",
+ // When "android_license_files" is valid, the test requires an Android.mk file
+ // outside the current (and an Android.bp file is required as well if the license
+ // files locates directory), thus a mock file system is needed. The integration
+ // test cases for these scenarios have been added in
+ // $(ANDROID_BUILD_TOP)/build/soong/tests/androidmk_test.sh.
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_LICENSE_KINDS := license_kind
+LOCAL_LICENSE_CONDITIONS := license_condition
+LOCAL_NOTICE_FILE := license_notice
+include $(BUILD_PACKAGE)
+`,
+ expected: `
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+}
+
+android_app {
+ name: "foo",
+ // ANDROIDMK TRANSLATION ERROR: Only $(LOCAL_PATH)/.. values are allowed
+ // LOCAL_NOTICE_FILE := license_notice
+
+}
+`,
+ },
+ {
+ desc: "LOCAL_CHECK_ELF_FILES",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_SRC_FILES := test.c
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_CHECK_ELF_FILES := false
+include $(BUILD_PREBUILT)
+ `,
+ expected: `
+cc_prebuilt_library_shared {
+ name: "foo",
+ srcs: ["test.c"],
+
+ check_elf_files: false,
+}
+`,
+ },
+ {
+ desc: "Drop default resource and asset dirs from bp",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+include $(BUILD_PACKAGE)
+`,
+ expected: `
+android_app {
+ name: "foo",
+
+}
+`,
+ },
+ {
+ desc: "LOCAL_GENERATED_SOURCES",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_SRC_FILES := src1, src2, src3
+LOCAL_GENERATED_SOURCES := gen_src1, gen_src2, gen_src3
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+ srcs: [
+ "src1,",
+ "src2,",
+ "src3",
+ ],
+ generated_sources: [
+ "gen_src1,",
+ "gen_src2,",
+ "gen_src3",
+ ],
+}
+`,
+ },
+ {
+ desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is true",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+ auto_gen_config: false,
+}
+`,
+ },
+ {
+ desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is false",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := false
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+ auto_gen_config: true,
+}
+`,
+ },
}
func TestEndToEnd(t *testing.T) {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 94b8116..8cca137 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -103,16 +103,9 @@
return moduleNames
}
- var postInstallCommands []string
- for _, fi := range a.filesInfo {
- if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
- // TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
- linkTarget := filepath.Join("/system", fi.path())
- linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.path())
- mkdirCmd := "mkdir -p " + filepath.Dir(linkPath)
- linkCmd := "ln -sfn " + linkTarget + " " + linkPath
- postInstallCommands = append(postInstallCommands, mkdirCmd, linkCmd)
- }
+ // 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)
@@ -156,7 +149,7 @@
var modulePath string
if apexType == flattenedApex {
// /system/apex/<name>/{lib|framework|...}
- modulePath = filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
+ 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)
@@ -188,6 +181,8 @@
// we will have duplicated notice entries.
fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
}
+ fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", filepath.Join(modulePath, fi.stem()))
+ fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", fi.builtFile.String()+":"+filepath.Join(modulePath, fi.stem()))
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake())
if fi.module != nil {
@@ -258,7 +253,7 @@
if !ok {
panic(fmt.Sprintf("Expected %s to be AndroidAppSet", fi.module))
}
- fmt.Fprintln(w, "LOCAL_APK_SET_INSTALL_FILE :=", as.InstallFile())
+ fmt.Fprintln(w, "LOCAL_APK_SET_INSTALL_FILE :=", as.PackedAdditionalOutputs().String())
fmt.Fprintln(w, "LOCAL_APKCERTS_FILE :=", as.APKCertsFile().String())
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
case nativeSharedLib, nativeExecutable, nativeTest:
@@ -272,7 +267,7 @@
fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String())
}
}
- fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
+ fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk")
default:
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
@@ -297,18 +292,10 @@
if len(patterns) > 0 {
fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
}
- if len(a.compatSymlinks) > 0 {
- // For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
- postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
- }
}
// File_contexts of flattened APEXes should be merged into file_contexts.bin
fmt.Fprintln(w, "LOCAL_FILE_CONTEXTS :=", a.fileContexts)
-
- if len(postInstallCommands) > 0 {
- fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
- }
}
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
}
@@ -322,16 +309,17 @@
return moduleNames
}
-func (a *apexBundle) writeRequiredModules(w io.Writer, apexBundleName string) {
+func (a *apexBundle) writeRequiredModules(w io.Writer) {
var required []string
var targetRequired []string
var hostRequired []string
- installMapSet := make(map[string]bool) // set of dependency module:location mappings
+ required = append(required, a.RequiredModuleNames()...)
+ targetRequired = append(targetRequired, a.TargetRequiredModuleNames()...)
+ hostRequired = append(hostRequired, a.HostRequiredModuleNames()...)
for _, fi := range a.filesInfo {
required = append(required, fi.requiredModuleNames...)
targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
- installMapSet[a.fullModuleName(apexBundleName, &fi)+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
}
if len(required) > 0 {
@@ -343,11 +331,6 @@
if len(hostRequired) > 0 {
fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
}
- if len(installMapSet) > 0 {
- var installs []string
- installs = append(installs, android.SortedStringKeys(installMapSet)...)
- fmt.Fprintln(w, "LOCAL_LICENSE_INSTALL_MAP +=", strings.Join(installs, " "))
- }
}
func (a *apexBundle) androidMkForType() android.AndroidMkData {
@@ -369,7 +352,7 @@
if len(moduleNames) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
}
- a.writeRequiredModules(w, name)
+ a.writeRequiredModules(w)
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
} else {
@@ -379,13 +362,15 @@
data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
- fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.ToMakePath().String())
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.String())
stemSuffix := apexType.suffix()
if a.isCompressed {
stemSuffix = imageCapexSuffix
}
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
+ fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
+ fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
// Because apex writes .mk with Custom(), we need to write manually some common properties
// which are available via data.Entries
@@ -409,17 +394,7 @@
if len(a.requiredDeps) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
}
- a.writeRequiredModules(w, name)
- var postInstallCommands []string
- if a.prebuiltFileToDelete != "" {
- postInstallCommands = append(postInstallCommands, "rm -rf "+
- filepath.Join(a.installDir.ToMakePath().String(), a.prebuiltFileToDelete))
- }
- // For unflattened apexes, compat symlinks are attached to apex package itself as LOCAL_POST_INSTALL_CMD
- postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
- if len(postInstallCommands) > 0 {
- fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
- }
+ a.writeRequiredModules(w)
if a.mergedNotices.Merged.Valid() {
fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
@@ -446,23 +421,18 @@
fmt.Fprintf(w, dist)
}
- if a.apisUsedByModuleFile.String() != "" {
- goal := "apps_only"
- distFile := a.apisUsedByModuleFile.String()
- fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
- " $(call dist-for-goals,%s,%s:ndk_apis_usedby_apex/$(notdir %s))\n"+
- "endif\n",
- goal, distFile, distFile)
- }
-
- if a.apisBackedByModuleFile.String() != "" {
- goal := "apps_only"
- distFile := a.apisBackedByModuleFile.String()
- fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
- " $(call dist-for-goals,%s,%s:ndk_apis_backedby_apex/$(notdir %s))\n"+
- "endif\n",
- goal, distFile, distFile)
- }
+ distCoverageFiles(w, "ndk_apis_usedby_apex", a.nativeApisUsedByModuleFile.String())
+ distCoverageFiles(w, "ndk_apis_backedby_apex", a.nativeApisBackedByModuleFile.String())
+ distCoverageFiles(w, "java_apis_used_by_apex", a.javaApisUsedByModuleFile.String())
}
}}
}
+
+func distCoverageFiles(w io.Writer, dir string, distfile string) {
+ if distfile != "" {
+ goal := "apps_only"
+ fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
+ " $(call dist-for-goals,%s,%s:%s/$(notdir %s))\n"+
+ "endif\n", goal, distfile, dir, distfile)
+ }
+}
diff --git a/apex/apex.go b/apex/apex.go
index 0a2e35b..d12a786 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -54,8 +54,6 @@
ctx.PreArchMutators(registerPreArchMutators)
ctx.PreDepsMutators(RegisterPreDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
-
- android.RegisterBp2BuildMutator("apex", ApexBundleBp2Build)
}
func registerPreArchMutators(ctx android.RegisterMutatorsContext) {
@@ -98,6 +96,14 @@
// /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.
+ Canned_fs_config *string `android:"path"`
+
ApexNativeDependencies
Multilib apexMultilibProperties
@@ -111,6 +117,9 @@
// 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
+
// List of platform_compat_config files that are embedded inside this APEX bundle.
Compat_configs []string
@@ -127,6 +136,13 @@
// symlinking to the system libs. Default is true.
Updatable *bool
+ // Marks that this APEX is designed to be updatable in the future, although it's not
+ // updatable yet. This is used to mimic some of the build behaviors that are applied only to
+ // updatable APEXes. Currently, this disables the size optimization, so that the size of
+ // APEX will not increase when the APEX is actually marked as truly updatable. Default is
+ // false.
+ Future_updatable *bool
+
// Whether this APEX can use platform APIs or not. Can be set to true only when `updatable:
// false`. Default is false.
Platform_apis *bool
@@ -135,21 +151,20 @@
// Default: true.
Installable *bool
- // Whether this APEX can be compressed or not. Setting this property to false means this
- // APEX will never be compressed. When set to true, APEX will be compressed if other
- // conditions, e.g, target device needs to support APEX compression, are also fulfilled.
- // Default: true.
- Compressible *bool
-
- // For native libraries and binaries, use the vendor variant instead of the core (platform)
- // variant. Default is false. DO NOT use this for APEXes that are installed to the system or
- // system_ext partition.
- Use_vendor *bool
-
// If set true, VNDK libs are considered as stable libs and are not included in this APEX.
// 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
+
// List of SDKs that are used to build this APEX. A reference to an SDK should be either
// `name#version` or `name` which is an alias for `name#current`. If left empty,
// `platform#current` is implied. This value affects all modules included in this APEX. In
@@ -163,8 +178,8 @@
// is 'image'.
Payload_type *string
- // The type of filesystem to use when the payload_type is 'image'. Either 'ext4' or 'f2fs'.
- // Default 'ext4'.
+ // The type of filesystem to use when the payload_type is 'image'. Either 'ext4', 'f2fs'
+ // or 'erofs'. Default 'ext4'.
Payload_fs_type *string
// For telling the APEX to ignore special handling for system libraries such as bionic.
@@ -183,6 +198,10 @@
// used in tests.
Test_only_force_compression *bool
+ // Put extra tags (signer=<value>) to apexkeys.txt, so that release tools can sign this apex
+ // with the tool to sign payload contents.
+ Custom_sign_tool *string
+
// Canonical name of this APEX bundle. Used to determine the path to the
// activated APEX on device (i.e. /apex/<apexVariationName>), and used for the
// apex mutator variations. For override_apex modules, this is the name of the
@@ -322,6 +341,12 @@
// certificate and the private key are provided from the android_app_certificate module
// named "module".
Certificate *string
+
+ // Whether this APEX can be compressed or not. Setting this property to false means this
+ // APEX will never be compressed. When set to true, APEX will be compressed if other
+ // conditions, e.g., target device needs to support APEX compression, are also fulfilled.
+ // Default: false.
+ Compressible *bool
}
type apexBundle struct {
@@ -353,7 +378,6 @@
// Flags for special variants of APEX
testApex bool
vndkApex bool
- artApex bool
// Tells whether this variant of the APEX bundle is the primary one or not. Only the primary
// one gets installed to the device.
@@ -401,14 +425,14 @@
// vendor/google/build/build_unbundled_mainline_module.sh for more detail.
bundleModuleFile android.WritablePath
- // Target path to install this APEX. Usually out/target/product/<device>/<partition>/apex.
+ // Target directory to install this APEX. Usually out/target/product/<device>/<partition>/apex.
installDir android.InstallPath
- // List of commands to create symlinks for backward compatibility. These commands will be
- // attached as LOCAL_POST_INSTALL_CMD to apex package itself (for unflattened build) or
- // apex_manifest (for flattened build) so that compat symlinks are always installed
- // regardless of TARGET_FLATTEN_APEX setting.
- compatSymlinks []string
+ // Path where this APEX was installed.
+ installedFile android.InstallPath
+
+ // Installed locations of symlinks for backward compatibility.
+ compatSymlinks android.InstallPaths
// Text file having the list of individual files that are included in this APEX. Used for
// debugging purpose.
@@ -426,8 +450,9 @@
isCompressed bool
// Path of API coverage generate file
- apisUsedByModuleFile android.ModuleOutPath
- apisBackedByModuleFile android.ModuleOutPath
+ nativeApisUsedByModuleFile android.ModuleOutPath
+ nativeApisBackedByModuleFile android.ModuleOutPath
+ javaApisUsedByModuleFile android.ModuleOutPath
// Collect the module directory for IDE info in java/jdeps.go.
modulePaths []string
@@ -605,6 +630,7 @@
sharedLibTag = dependencyTag{name: "sharedLib", payload: true}
testForTag = dependencyTag{name: "test for"}
testTag = dependencyTag{name: "test", payload: true}
+ shBinaryTag = dependencyTag{name: "shBinary", payload: true}
)
// TODO(jiyong): shorten this function signature
@@ -654,10 +680,7 @@
var prefix string
var vndkVersion string
if deviceConfig.VndkVersion() != "" {
- if proptools.Bool(a.properties.Use_vendor) {
- prefix = cc.VendorVariationPrefix
- vndkVersion = deviceConfig.PlatformVndkVersion()
- } else if a.SocSpecific() || a.DeviceSpecific() {
+ if a.SocSpecific() || a.DeviceSpecific() {
prefix = cc.VendorVariationPrefix
vndkVersion = deviceConfig.VndkVersion()
} else if a.ProductSpecific() {
@@ -676,9 +699,6 @@
}
func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
- // TODO(jiyong): move this kind of checks to GenerateAndroidBuildActions?
- checkUseVendorProperty(ctx, a)
-
// apexBundle is a multi-arch targets module. Arch variant of apexBundle is set to 'common'.
// arch-specific targets are enabled by the compile_multilib setting of the apex bundle. For
// each target os/architectures, appropriate dependencies are selected by their
@@ -755,6 +775,10 @@
for _, d := range depsList {
addDependenciesForNativeModules(ctx, d, target, imageVariation)
}
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "os", Variation: target.OsVariation()},
+ {Mutator: "arch", Variation: target.ArchVariation()},
+ }, shBinaryTag, a.properties.Sh_binaries...)
}
// Common-arch dependencies come next
@@ -765,13 +789,6 @@
ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
- if a.artApex {
- // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
- if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
- ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
- }
- }
-
// Marks that this APEX (in fact all the modules in it) has to be built with the given SDKs.
// This field currently isn't used.
// TODO(jiyong): consider dropping this feature
@@ -871,9 +888,18 @@
// APEX, but shared across APEXes via the VNDK APEX.
useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable)
- if !useVndk && proptools.Bool(a.properties.Use_vndk_as_stable) {
- mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
- return
+ if proptools.Bool(a.properties.Use_vndk_as_stable) {
+ if !useVndk {
+ mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
+ }
+ mctx.VisitDirectDepsWithTag(sharedLibTag, func(dep android.Module) {
+ if c, ok := dep.(*cc.Module); ok && c.IsVndk() {
+ mctx.PropertyErrorf("use_vndk_as_stable", "Trying to include a VNDK library(%s) while use_vndk_as_stable is true.", dep.Name())
+ }
+ })
+ if mctx.Failed() {
+ return
+ }
}
continueApexDepsWalk := func(child, parent android.Module) bool {
@@ -1162,8 +1188,9 @@
zipApexType = "zip"
flattenedApexType = "flattened"
- ext4FsType = "ext4"
- f2fsFsType = "f2fs"
+ ext4FsType = "ext4"
+ f2fsFsType = "f2fs"
+ erofsFsType = "erofs"
)
// The suffix for the output "file", not the module
@@ -1240,42 +1267,6 @@
}
}
-// checkUseVendorProperty checks if the use of `use_vendor` property is allowed for the given APEX.
-// When use_vendor is used, native modules are built with __ANDROID_VNDK__ and __ANDROID_APEX__,
-// which may cause compatibility issues. (e.g. libbinder) Even though libbinder restricts its
-// availability via 'apex_available' property and relies on yet another macro
-// __ANDROID_APEX_<NAME>__, we restrict usage of "use_vendor:" from other APEX modules to avoid
-// similar problems.
-func checkUseVendorProperty(ctx android.BottomUpMutatorContext, a *apexBundle) {
- if proptools.Bool(a.properties.Use_vendor) && !android.InList(a.Name(), useVendorAllowList(ctx.Config())) {
- ctx.PropertyErrorf("use_vendor", "not allowed to set use_vendor: true")
- }
-}
-
-var (
- useVendorAllowListKey = android.NewOnceKey("useVendorAllowList")
-)
-
-func useVendorAllowList(config android.Config) []string {
- return config.Once(useVendorAllowListKey, func() interface{} {
- return []string{
- // swcodec uses "vendor" variants for smaller size
- "com.android.media.swcodec",
- "test_com.android.media.swcodec",
- }
- }).([]string)
-}
-
-// setUseVendorAllowListForTest returns a FixturePreparer that overrides useVendorAllowList and
-// must be called before the first call to useVendorAllowList()
-func setUseVendorAllowListForTest(allowList []string) android.FixturePreparer {
- return android.FixtureModifyConfig(func(config android.Config) {
- config.Once(useVendorAllowListKey, func() interface{} {
- return allowList
- })
- })
-}
-
var _ android.DepIsInSameApex = (*apexBundle)(nil)
// Implements android.DepInInSameApex
@@ -1328,11 +1319,15 @@
var _ android.ApexBundleDepsInfoIntf = (*apexBundle)(nil)
-// Implements android.ApexBudleDepsInfoIntf
+// Implements android.ApexBundleDepsInfoIntf
func (a *apexBundle) Updatable() bool {
return proptools.BoolDefault(a.properties.Updatable, true)
}
+func (a *apexBundle) FutureUpdatable() bool {
+ return proptools.BoolDefault(a.properties.Future_updatable, false)
+}
+
func (a *apexBundle) UsePlatformApis() bool {
return proptools.BoolDefault(a.properties.Platform_apis, false)
}
@@ -1508,12 +1503,7 @@
func apexFileForGoBinary(ctx android.BaseModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
dirInApex := "bin"
- s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
- if err != nil {
- ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
- return apexFile{}
- }
- fileToCopy := android.PathForOutput(ctx, s)
+ fileToCopy := android.PathForGoBinary(ctx, gb)
// 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.
@@ -1522,6 +1512,9 @@
func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFile {
dirInApex := filepath.Join("bin", sh.SubDir())
+ if sh.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, sh.Target().NativeBridgeRelativePath)
+ }
fileToCopy := sh.OutputFile()
af := newApexFile(ctx, fileToCopy, sh.BaseModuleName(), dirInApex, shBinary, sh)
af.symlinks = sh.Symlinks()
@@ -1628,8 +1621,8 @@
return af
}
-func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, bpfProgram bpf.BpfModule) apexFile {
- dirInApex := filepath.Join("etc", "bpf")
+func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, apex_sub_dir string, bpfProgram bpf.BpfModule) apexFile {
+ dirInApex := filepath.Join("etc", "bpf", apex_sub_dir)
return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
}
@@ -1672,6 +1665,7 @@
const (
ext4 fsType = iota
f2fs
+ erofs
)
func (f fsType) string() string {
@@ -1680,6 +1674,8 @@
return ext4FsType
case f2fs:
return f2fsFsType
+ case erofs:
+ return erofsFsType
default:
panic(fmt.Errorf("unknown APEX payload type %d", f))
}
@@ -1696,7 +1692,7 @@
// 1) do some validity checks such as apex_available, min_sdk_version, etc.
a.checkApexAvailability(ctx)
a.checkUpdatable(ctx)
- a.checkMinSdkVersion(ctx)
+ a.CheckMinSdkVersion(ctx)
a.checkStaticLinkingToStubLibraries(ctx)
a.checkStaticExecutables(ctx)
if len(a.properties.Tests) > 0 && !a.testApex {
@@ -1726,6 +1722,9 @@
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 {
@@ -1744,6 +1743,7 @@
return true // track transitive dependencies
} else if r, ok := child.(*rust.Module); ok {
fi := apexFileForRustLibrary(ctx, r)
+ fi.isJniLib = isJniLib
filesInfo = append(filesInfo, fi)
} else {
propertyName := "native_shared_libs"
@@ -1756,8 +1756,6 @@
if cc, ok := child.(*cc.Module); ok {
filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
return true // track transitive dependencies
- } else if sh, ok := child.(*sh.ShBinary); ok {
- filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
} 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() {
@@ -1766,17 +1764,27 @@
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, (host) bootstrap_go_binary, nor sh_binary", depName)
+ 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:
{
- if _, ok := child.(*java.BootclasspathFragmentModule); !ok {
+ 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:
@@ -1832,8 +1840,9 @@
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, bpfProgram))
+ filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
}
} else {
ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
@@ -1928,13 +1937,7 @@
// system libraries.
if !am.DirectlyInAnyApex() {
// we need a module name for Make
- name := cc.ImplementationModuleNameForMake(ctx)
-
- if !proptools.Bool(a.properties.Use_vendor) {
- // we don't use subName(.vendor) for a "use_vendor: true" apex
- // which is supposed to be installed in /system
- name += cc.Properties.SubName
- }
+ name := cc.ImplementationModuleNameForMake(ctx) + cc.Properties.SubName
if !android.InList(name, a.requiredDeps) {
a.requiredDeps = append(a.requiredDeps, name)
}
@@ -2028,6 +2031,8 @@
}
} 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)
}
@@ -2109,8 +2114,10 @@
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]", *a.properties.Payload_fs_type)
+ ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs, erofs]", *a.properties.Payload_fs_type)
}
// Optimization. If we are building bundled APEX, for the files that are gathered due to the
@@ -2118,7 +2125,7 @@
// the same library in the system partition, thus effectively sharing the same libraries
// across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
// in the APEX.
- a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable() && !proptools.Bool(a.properties.Use_vendor)
+ a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable()
// APEXes targeting other than system/system_ext partitions use vendor/product variants.
// So we can't link them to /system/lib libs which are core variants.
@@ -2127,10 +2134,11 @@
}
forced := ctx.Config().ForceApexSymlinkOptimization()
+ updatable := a.Updatable() || a.FutureUpdatable()
// We don't need the optimization for updatable APEXes, as it might give false signal
// to the system health when the APEXes are still bundled (b/149805758).
- if !forced && a.Updatable() && a.properties.ApexType == imageApex {
+ if !forced && updatable && a.properties.ApexType == imageApex {
a.linkToSystemLib = false
}
@@ -2139,7 +2147,9 @@
a.linkToSystemLib = false
}
- a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), 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.
@@ -2179,13 +2189,15 @@
var filesToAdd []apexFile
// Add the boot image files, e.g. .art, .oat and .vdex files.
- 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)
+ 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)
+ }
}
}
@@ -2194,6 +2206,40 @@
filesToAdd = append(filesToAdd, *af)
}
+ if pathInApex := bootclasspathFragmentInfo.ProfileInstallPathInApex(); pathInApex != "" {
+ pathOnHost := bootclasspathFragmentInfo.ProfilePathOnHost()
+ tempPath := android.PathForModuleOut(ctx, "boot_image_profile", pathInApex)
+
+ if pathOnHost != nil {
+ // We need to copy the profile to a temporary path with the right filename because the apexer
+ // will take the filename as is.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: pathOnHost,
+ Output: tempPath,
+ })
+ } else {
+ // At this point, the boot image profile cannot be generated. It is probably because the boot
+ // image profile source file does not exist on the branch, or it is not available for the
+ // current build target.
+ // However, we cannot enforce the boot image profile to be generated because some build
+ // targets (such as module SDK) do not need it. It is only needed when the APEX is being
+ // built. Therefore, we create an error rule so that an error will occur at the ninja phase
+ // only if the APEX is being built.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.ErrorRule,
+ Output: tempPath,
+ Args: map[string]string{
+ "error": "Boot image profile cannot be generated",
+ },
+ })
+ }
+
+ androidMkModuleName := filepath.Base(pathInApex)
+ af := newApexFile(ctx, tempPath, androidMkModuleName, filepath.Dir(pathInApex), etc, nil)
+ filesToAdd = append(filesToAdd, af)
+ }
+
return filesToAdd
}
@@ -2247,10 +2293,9 @@
return module
}
-func ApexBundleFactory(testApex bool, artApex bool) android.Module {
+func ApexBundleFactory(testApex bool) android.Module {
bundle := newApexBundle()
bundle.testApex = testApex
- bundle.artApex = artApex
return bundle
}
@@ -2321,22 +2366,28 @@
//
// TODO(jiyong): move these checks to a separate go file.
+var _ android.ModuleWithMinSdkVersionCheck = (*apexBundle)(nil)
+
// Entures 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) {
+func (a *apexBundle) CheckMinSdkVersion(ctx android.ModuleContext) {
if a.testApex || a.vndkApex {
return
}
- // Meaningless to check min_sdk_version when building use_vendor modules against non-Trebleized targets
- if proptools.Bool(a.properties.Use_vendor) && ctx.DeviceConfig().VndkVersion() == "" {
- return
- }
// apexBundle::minSdkVersion reports its own errors.
minSdkVersion := a.minSdkVersion(ctx)
- android.CheckMinSdkVersion(a, ctx, minSdkVersion)
+ android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps)
}
-func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel {
+func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpec{
+ Kind: android.SdkNone,
+ ApiLevel: a.minSdkVersion(ctx),
+ Raw: String(a.properties.Min_sdk_version),
+ }
+}
+
+func (a *apexBundle) minSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
ver := proptools.String(a.properties.Min_sdk_version)
if ver == "" {
return android.NoneApiLevel
@@ -2402,6 +2453,12 @@
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 a.FutureUpdatable() {
+ ctx.PropertyErrorf("future_updatable", "Already updatable. Remove `future_updatable: true:`")
+ }
a.checkJavaStableSdkVersion(ctx)
a.checkClasspathFragments(ctx)
}
@@ -2602,7 +2659,7 @@
//
// Module separator
//
- m["com.android.bluetooth.updatable"] = []string{
+ m["com.android.bluetooth"] = []string{
"android.hardware.audio.common@5.0",
"android.hardware.bluetooth.a2dp@1.0",
"android.hardware.bluetooth.audio@2.0",
@@ -2709,7 +2766,6 @@
"libbuildversion",
"libmath",
"libprocpartition",
- "libsync",
}
//
// Module separator
@@ -2817,7 +2873,6 @@
"libstagefright_metadatautils",
"libstagefright_mpeg2extractor",
"libstagefright_mpeg2support",
- "libsync",
"libui",
"libui_headers",
"libunwindstack",
@@ -2950,7 +3005,6 @@
"libstagefright_amrwbdec",
"libstagefright_amrwbenc",
"libstagefright_bufferpool@2.0.1",
- "libstagefright_bufferqueue_helper",
"libstagefright_enc_common",
"libstagefright_flacdec",
"libstagefright_foundation",
@@ -2959,7 +3013,6 @@
"libstagefright_m4vh263dec",
"libstagefright_m4vh263enc",
"libstagefright_mp3dec",
- "libsync",
"libui",
"libui_headers",
"libunwindstack",
@@ -3165,15 +3218,23 @@
BootclasspathJar().
With("apex_available", module_name).
WithMatcher("permitted_packages", android.NotInList(module_packages)).
+ WithMatcher("min_sdk_version", android.LessThanSdkVersion("Tiramisu")).
Because("jars that are part of the " + module_name +
- " module may only allow these packages: " + strings.Join(module_packages, ",") +
- ". Please jarjar or move code around.")
+ " module may only use these package prefixes: " + strings.Join(module_packages, ",") +
+ " with min_sdk < T. Please consider the following alternatives:\n" +
+ " 1. If the offending code is from a statically linked library, consider " +
+ "removing that dependency and using an alternative already in the " +
+ "bootclasspath, or perhaps a shared library." +
+ " 2. Move the offending code into an allowed package.\n" +
+ " 3. Jarjar the offending code. Please be mindful of the potential system " +
+ "health implications of bundling that code, particularly if the offending jar " +
+ "is part of the bootclasspath.")
rules = append(rules, permittedPackagesRule)
}
return rules
}
-// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
+// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART on Q/R/S.
// Adding code to the bootclasspath in new packages will cause issues on module update.
func qModulesPackages() map[string][]string {
return map[string][]string{
@@ -3187,7 +3248,7 @@
}
}
-// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
+// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART on R/S.
// Adding code to the bootclasspath in new packages will cause issues on module update.
func rModulesPackages() map[string][]string {
return map[string][]string{
@@ -3225,99 +3286,115 @@
// For Bazel / bp2build
type bazelApexBundleAttributes struct {
- Manifest bazel.LabelAttribute
- Android_manifest bazel.LabelAttribute
- File_contexts bazel.LabelAttribute
- Key bazel.LabelAttribute
- Certificate bazel.LabelAttribute
- Min_sdk_version string
- Updatable bazel.BoolAttribute
- Installable bazel.BoolAttribute
- Native_shared_libs bazel.LabelListAttribute
- Binaries bazel.StringListAttribute
- Prebuilts bazel.LabelListAttribute
+ Manifest bazel.LabelAttribute
+ Android_manifest bazel.LabelAttribute
+ File_contexts bazel.LabelAttribute
+ Key bazel.LabelAttribute
+ Certificate bazel.LabelAttribute
+ Min_sdk_version *string
+ Updatable bazel.BoolAttribute
+ Installable bazel.BoolAttribute
+ Binaries bazel.LabelListAttribute
+ Prebuilts bazel.LabelListAttribute
+ Native_shared_libs_32 bazel.LabelListAttribute
+ Native_shared_libs_64 bazel.LabelListAttribute
+ Compressible bazel.BoolAttribute
}
-func ApexBundleBp2Build(ctx android.TopDownMutatorContext) {
- module, ok := ctx.Module().(*apexBundle)
- if !ok {
- // Not an APEX bundle
- return
- }
- if !module.ConvertWithBp2build(ctx) {
- return
- }
+type convertedNativeSharedLibs struct {
+ Native_shared_libs_32 bazel.LabelListAttribute
+ Native_shared_libs_64 bazel.LabelListAttribute
+}
+
+// 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" {
return
}
- apexBundleBp2BuildInternal(ctx, module)
-}
-
-func apexBundleBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexBundle) {
var manifestLabelAttribute bazel.LabelAttribute
- if module.properties.Manifest != nil {
- manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Manifest))
+ if a.properties.Manifest != nil {
+ manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.Manifest))
}
var androidManifestLabelAttribute bazel.LabelAttribute
- if module.properties.AndroidManifest != nil {
- androidManifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.AndroidManifest))
+ if a.properties.AndroidManifest != nil {
+ androidManifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.AndroidManifest))
}
var fileContextsLabelAttribute bazel.LabelAttribute
- if module.properties.File_contexts != nil {
- fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.properties.File_contexts))
+ if a.properties.File_contexts != nil {
+ fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.properties.File_contexts))
}
- var minSdkVersion string
- if module.properties.Min_sdk_version != nil {
- minSdkVersion = *module.properties.Min_sdk_version
+ var minSdkVersion *string
+ if a.properties.Min_sdk_version != nil {
+ minSdkVersion = a.properties.Min_sdk_version
}
var keyLabelAttribute bazel.LabelAttribute
- if module.overridableProperties.Key != nil {
- keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.overridableProperties.Key))
+ if a.overridableProperties.Key != nil {
+ keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Key))
}
var certificateLabelAttribute bazel.LabelAttribute
- if module.overridableProperties.Certificate != nil {
- certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.overridableProperties.Certificate))
+ if a.overridableProperties.Certificate != nil {
+ certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Certificate))
}
- nativeSharedLibs := module.properties.ApexNativeDependencies.Native_shared_libs
- nativeSharedLibsLabelList := android.BazelLabelForModuleDeps(ctx, nativeSharedLibs)
- nativeSharedLibsLabelListAttribute := bazel.MakeLabelListAttribute(nativeSharedLibsLabelList)
+ nativeSharedLibs := &convertedNativeSharedLibs{
+ Native_shared_libs_32: bazel.LabelListAttribute{},
+ Native_shared_libs_64: bazel.LabelListAttribute{},
+ }
+ compileMultilib := "both"
+ if a.CompileMultilib() != nil {
+ compileMultilib = *a.CompileMultilib()
+ }
- prebuilts := module.overridableProperties.Prebuilts
+ // properties.Native_shared_libs is treated as "both"
+ convertBothLibs(ctx, compileMultilib, a.properties.Native_shared_libs, nativeSharedLibs)
+ convertBothLibs(ctx, compileMultilib, a.properties.Multilib.Both.Native_shared_libs, nativeSharedLibs)
+ convert32Libs(ctx, compileMultilib, a.properties.Multilib.Lib32.Native_shared_libs, nativeSharedLibs)
+ convert64Libs(ctx, compileMultilib, a.properties.Multilib.Lib64.Native_shared_libs, nativeSharedLibs)
+ convertFirstLibs(ctx, compileMultilib, a.properties.Multilib.First.Native_shared_libs, nativeSharedLibs)
+
+ prebuilts := a.overridableProperties.Prebuilts
prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, prebuilts)
prebuiltsLabelListAttribute := bazel.MakeLabelListAttribute(prebuiltsLabelList)
- binaries := module.properties.ApexNativeDependencies.Binaries
- binariesStringListAttribute := bazel.MakeStringListAttribute(binaries)
+ binaries := android.BazelLabelForModuleDeps(ctx, a.properties.ApexNativeDependencies.Binaries)
+ binariesLabelListAttribute := bazel.MakeLabelListAttribute(binaries)
var updatableAttribute bazel.BoolAttribute
- if module.properties.Updatable != nil {
- updatableAttribute.Value = module.properties.Updatable
+ if a.properties.Updatable != nil {
+ updatableAttribute.Value = a.properties.Updatable
}
var installableAttribute bazel.BoolAttribute
- if module.properties.Installable != nil {
- installableAttribute.Value = module.properties.Installable
+ if a.properties.Installable != nil {
+ installableAttribute.Value = a.properties.Installable
+ }
+
+ var compressibleAttribute bazel.BoolAttribute
+ if a.overridableProperties.Compressible != nil {
+ compressibleAttribute.Value = a.overridableProperties.Compressible
}
attrs := &bazelApexBundleAttributes{
- Manifest: manifestLabelAttribute,
- Android_manifest: androidManifestLabelAttribute,
- File_contexts: fileContextsLabelAttribute,
- Min_sdk_version: minSdkVersion,
- Key: keyLabelAttribute,
- Certificate: certificateLabelAttribute,
- Updatable: updatableAttribute,
- Installable: installableAttribute,
- Native_shared_libs: nativeSharedLibsLabelListAttribute,
- Binaries: binariesStringListAttribute,
- Prebuilts: prebuiltsLabelListAttribute,
+ Manifest: manifestLabelAttribute,
+ Android_manifest: androidManifestLabelAttribute,
+ File_contexts: fileContextsLabelAttribute,
+ Min_sdk_version: minSdkVersion,
+ Key: keyLabelAttribute,
+ Certificate: certificateLabelAttribute,
+ Updatable: updatableAttribute,
+ Installable: installableAttribute,
+ Native_shared_libs_32: nativeSharedLibs.Native_shared_libs_32,
+ Native_shared_libs_64: nativeSharedLibs.Native_shared_libs_64,
+ Binaries: binariesLabelListAttribute,
+ Prebuilts: prebuiltsLabelListAttribute,
+ Compressible: compressibleAttribute,
}
props := bazel.BazelTargetModuleProperties{
@@ -3325,5 +3402,109 @@
Bzl_load_location: "//build/bazel/rules:apex.bzl",
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
+}
+
+// The following conversions are based on this table where the rows are the compile_multilib
+// values and the columns are the properties.Multilib.*.Native_shared_libs. Each cell
+// represents how the libs should be compiled for a 64-bit/32-bit device: 32 means it
+// should be compiled as 32-bit, 64 means it should be compiled as 64-bit, none means it
+// should not be compiled.
+// multib/compile_multilib, 32, 64, both, first
+// 32, 32/32, none/none, 32/32, none/32
+// 64, none/none, 64/none, 64/none, 64/none
+// both, 32/32, 64/none, 32&64/32, 64/32
+// first, 32/32, 64/none, 64/32, 64/32
+
+func convert32Libs(ctx android.TopDownMutatorContext, compileMultilb string,
+ libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
+ libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
+ switch compileMultilb {
+ case "both", "32":
+ makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ case "first":
+ make32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ case "64":
+ // Incompatible, ignore
+ default:
+ invalidCompileMultilib(ctx, compileMultilb)
+ }
+}
+
+func convert64Libs(ctx android.TopDownMutatorContext, compileMultilb string,
+ libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
+ libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
+ switch compileMultilb {
+ case "both", "64", "first":
+ make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ case "32":
+ // Incompatible, ignore
+ default:
+ invalidCompileMultilib(ctx, compileMultilb)
+ }
+}
+
+func convertBothLibs(ctx android.TopDownMutatorContext, compileMultilb string,
+ libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
+ libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
+ switch compileMultilb {
+ case "both":
+ makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ case "first":
+ makeFirstSharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ case "32":
+ makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ case "64":
+ make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ default:
+ invalidCompileMultilib(ctx, compileMultilb)
+ }
+}
+
+func convertFirstLibs(ctx android.TopDownMutatorContext, compileMultilb string,
+ libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
+ libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
+ switch compileMultilb {
+ case "both", "first":
+ makeFirstSharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ case "32":
+ make32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ case "64":
+ make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ default:
+ invalidCompileMultilib(ctx, compileMultilb)
+ }
+}
+
+func makeFirstSharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
+ make32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+ make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+}
+
+func makeNoConfig32SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
+ list := bazel.LabelListAttribute{}
+ list.SetSelectValue(bazel.NoConfigAxis, "", libsLabelList)
+ nativeSharedLibs.Native_shared_libs_32.Append(list)
+}
+
+func make32SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
+ makeSharedLibsAttributes("x86", libsLabelList, &nativeSharedLibs.Native_shared_libs_32)
+ makeSharedLibsAttributes("arm", libsLabelList, &nativeSharedLibs.Native_shared_libs_32)
+}
+
+func make64SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
+ makeSharedLibsAttributes("x86_64", libsLabelList, &nativeSharedLibs.Native_shared_libs_64)
+ makeSharedLibsAttributes("arm64", libsLabelList, &nativeSharedLibs.Native_shared_libs_64)
+}
+
+func makeSharedLibsAttributes(config string, libsLabelList bazel.LabelList,
+ labelListAttr *bazel.LabelListAttribute) {
+ list := bazel.LabelListAttribute{}
+ list.SetSelectValue(bazel.ArchConfigurationAxis, config, libsLabelList)
+ labelListAttr.Append(list)
+}
+
+func invalidCompileMultilib(ctx android.TopDownMutatorContext, value string) {
+ ctx.PropertyErrorf("compile_multilib", "Invalid value: %s", value)
}
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 9178431..6faed70 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -17,9 +17,9 @@
package apex
import (
- "android/soong/android"
-
"github.com/google/blueprint"
+
+ "android/soong/android"
)
func init() {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 5eb7acf..6d77b06 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -215,7 +215,9 @@
variables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
variables.Platform_sdk_codename = proptools.StringPtr("Q")
variables.Platform_sdk_final = proptools.BoolPtr(false)
- variables.Platform_version_active_codenames = []string{"Q"}
+ // "Tiramisu" needs to be in the next line for compatibility with soong code,
+ // not because of these tests specifically (it's not used by the tests)
+ variables.Platform_version_active_codenames = []string{"Q", "Tiramisu"}
variables.Platform_vndk_version = proptools.StringPtr("29")
}),
)
@@ -621,7 +623,7 @@
java_libs: ["myjar"],
apps: ["AppFoo"],
rros: ["rro"],
- bpfs: ["bpf"],
+ bpfs: ["bpf", "netd_test"],
updatable: false,
}
@@ -674,6 +676,12 @@
srcs: ["bpf.c", "bpf2.c"],
}
+ bpf {
+ name: "netd_test",
+ srcs: ["netd_test.c"],
+ sub_dir: "netd",
+ }
+
`)
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"etc/myetc",
@@ -683,6 +691,7 @@
"overlay/blue/rro.apk",
"etc/bpf/bpf.o",
"etc/bpf/bpf2.o",
+ "etc/bpf/netd/netd_test.o",
})
}
@@ -1509,7 +1518,6 @@
apex {
name: "myapex",
key: "myapex.key",
- use_vendor: true,
native_shared_libs: ["mylib"],
updatable: false,
`+tc.minSdkVersion+`
@@ -1543,7 +1551,6 @@
}
}
`,
- setUseVendorAllowListForTest([]string{"myapex"}),
withUnbundledBuild,
)
@@ -1557,13 +1564,13 @@
ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so")
- mylibLdFlags := ctx.ModuleForTests("mylib", "android_vendor.29_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
- ensureContains(t, mylibLdFlags, "libbar/android_vendor.29_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
+ mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
+ ensureContains(t, mylibLdFlags, "libbar/android_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
for _, ver := range tc.shouldNotLink {
- ensureNotContains(t, mylibLdFlags, "libbar/android_vendor.29_arm64_armv8-a_shared_"+ver+"/libbar.so")
+ ensureNotContains(t, mylibLdFlags, "libbar/android_arm64_armv8-a_shared_"+ver+"/libbar.so")
}
- mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.29_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
+ mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
ver := tc.shouldLink
if tc.shouldLink == "current" {
ver = strconv.Itoa(android.FutureApiLevelInt)
@@ -2581,22 +2588,21 @@
`)
generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("generateFsConfig")
- dirs := strings.Split(generateFsRule.Args["exec_paths"], " ")
+ cmd := generateFsRule.RuleParams.Command
// Ensure that the subdirectories are all listed
- ensureListContains(t, dirs, "etc")
- ensureListContains(t, dirs, "etc/foo")
- ensureListContains(t, dirs, "etc/foo/bar")
- ensureListContains(t, dirs, "lib64")
- ensureListContains(t, dirs, "lib64/foo")
- ensureListContains(t, dirs, "lib64/foo/bar")
- ensureListContains(t, dirs, "lib")
- ensureListContains(t, dirs, "lib/foo")
- ensureListContains(t, dirs, "lib/foo/bar")
-
- ensureListContains(t, dirs, "bin")
- ensureListContains(t, dirs, "bin/foo")
- ensureListContains(t, dirs, "bin/foo/bar")
+ ensureContains(t, cmd, "/etc ")
+ ensureContains(t, cmd, "/etc/foo ")
+ ensureContains(t, cmd, "/etc/foo/bar ")
+ ensureContains(t, cmd, "/lib64 ")
+ ensureContains(t, cmd, "/lib64/foo ")
+ ensureContains(t, cmd, "/lib64/foo/bar ")
+ ensureContains(t, cmd, "/lib ")
+ ensureContains(t, cmd, "/lib/foo ")
+ ensureContains(t, cmd, "/lib/foo/bar ")
+ ensureContains(t, cmd, "/bin ")
+ ensureContains(t, cmd, "/bin/foo ")
+ ensureContains(t, cmd, "/bin/foo/bar ")
}
func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) {
@@ -2657,121 +2663,11 @@
})
}
-func TestUseVendor(t *testing.T) {
- ctx := testApex(t, `
- apex {
- name: "myapex",
- key: "myapex.key",
- native_shared_libs: ["mylib"],
- use_vendor: true,
- updatable: false,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "mylib",
- srcs: ["mylib.cpp"],
- shared_libs: ["mylib2"],
- system_shared_libs: [],
- vendor_available: true,
- stl: "none",
- apex_available: [ "myapex" ],
- }
-
- cc_library {
- name: "mylib2",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- vendor_available: true,
- stl: "none",
- apex_available: [ "myapex" ],
- }
- `,
- setUseVendorAllowListForTest([]string{"myapex"}),
- )
-
- inputsList := []string{}
- for _, i := range ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().BuildParamsForTests() {
- for _, implicit := range i.Implicits {
- inputsList = append(inputsList, implicit.String())
- }
- }
- inputsString := strings.Join(inputsList, " ")
-
- // ensure that the apex includes vendor variants of the direct and indirect deps
- ensureContains(t, inputsString, "android_vendor.29_arm64_armv8-a_shared_apex10000/mylib.so")
- ensureContains(t, inputsString, "android_vendor.29_arm64_armv8-a_shared_apex10000/mylib2.so")
-
- // ensure that the apex does not include core variants
- ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_apex10000/mylib.so")
- ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_apex10000/mylib2.so")
-}
-
-func TestUseVendorNotAllowedForSystemApexes(t *testing.T) {
- testApexError(t, `module "myapex" .*: use_vendor: not allowed`, `
- apex {
- name: "myapex",
- key: "myapex.key",
- use_vendor: true,
- }
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
- `,
- setUseVendorAllowListForTest([]string{""}),
- )
- // no error with allow list
- testApex(t, `
- apex {
- name: "myapex",
- key: "myapex.key",
- use_vendor: true,
- updatable: false,
- }
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
- `,
- setUseVendorAllowListForTest([]string{"myapex"}),
- )
-}
-
-func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) {
- testApexError(t, `dependency "mylib" of "myapex" missing variant:\n.*image:vendor`, `
- apex {
- name: "myapex",
- key: "myapex.key",
- native_shared_libs: ["mylib"],
- use_vendor: true,
- 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",
- }
- `)
-}
-
func TestVendorApex(t *testing.T) {
- ctx := testApex(t, `
+ result := android.GroupFixturePreparers(
+ prepareForApexTest,
+ android.FixtureModifyConfig(android.SetKatiEnabledForTests),
+ ).RunTestWithBp(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -2795,29 +2691,55 @@
}
`)
- ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+ ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
"bin/mybin",
"lib64/libfoo.so",
// TODO(b/159195575): Add an option to use VNDK libs from VNDK APEX
"lib64/libc++.so",
})
- apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, ctx, apexBundle)
+ apexBundle := result.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ data := android.AndroidMkDataForTest(t, result.TestContext, apexBundle)
name := apexBundle.BaseModuleName()
prefix := "TARGET_"
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
- androidMk := android.StringRelativeToTop(ctx.Config(), builder.String())
+ androidMk := android.StringRelativeToTop(result.Config, builder.String())
installPath := "out/target/product/test_device/vendor/apex"
ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath)
- apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+ apexManifestRule := result.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
ensureListNotContains(t, requireNativeLibs, ":vndk")
}
+func TestVendorApex_use_vndk_as_stable_TryingToIncludeVNDKLib(t *testing.T) {
+ testApexError(t, `Trying to include a VNDK library`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libc++"], // libc++ is a VNDK lib
+ vendor: true,
+ use_vndk_as_stable: true,
+ updatable: false,
+ }
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }`)
+}
+
func TestVendorApex_use_vndk_as_stable(t *testing.T) {
+ // myapex myapex2
+ // | |
+ // mybin ------. mybin2
+ // \ \ / |
+ // (stable) .---\--------` |
+ // \ / \ |
+ // \ / \ /
+ // libvndk libvendor
+ // (vndk)
ctx := testApex(t, `
apex {
name: "myapex",
@@ -2848,28 +2770,95 @@
cc_library {
name: "libvendor",
vendor: true,
+ stl: "none",
+ }
+ apex {
+ name: "myapex2",
+ key: "myapex.key",
+ binaries: ["mybin2"],
+ vendor: true,
+ use_vndk_as_stable: false,
+ updatable: false,
+ }
+ cc_binary {
+ name: "mybin2",
+ vendor: true,
+ shared_libs: ["libvndk", "libvendor"],
}
`)
vendorVariant := "android_vendor.29_arm64_armv8-a"
- ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_apex10000").Rule("ld")
- libs := names(ldRule.Args["libFlags"])
- // VNDK libs(libvndk/libc++) as they are
- ensureListContains(t, libs, "out/soong/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
- ensureListContains(t, libs, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
- // non-stable Vendor libs as APEX variants
- ensureListContains(t, libs, "out/soong/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
+ for _, tc := range []struct {
+ name string
+ apexName string
+ moduleName string
+ moduleVariant string
+ libs []string
+ contents []string
+ requireVndkNamespace bool
+ }{
+ {
+ name: "use_vndk_as_stable",
+ apexName: "myapex",
+ moduleName: "mybin",
+ moduleVariant: vendorVariant + "_apex10000",
+ libs: []string{
+ // should link with vendor variants of VNDK libs(libvndk/libc++)
+ "out/soong/.intermediates/libvndk/" + vendorVariant + "_shared/libvndk.so",
+ "out/soong/.intermediates/" + cc.DefaultCcCommonTestModulesDir + "libc++/" + vendorVariant + "_shared/libc++.so",
+ // unstable Vendor libs as APEX variant
+ "out/soong/.intermediates/libvendor/" + vendorVariant + "_shared_apex10000/libvendor.so",
+ },
+ contents: []string{
+ "bin/mybin",
+ "lib64/libvendor.so",
+ // VNDK libs (libvndk/libc++) are not included
+ },
+ requireVndkNamespace: true,
+ },
+ {
+ name: "!use_vndk_as_stable",
+ apexName: "myapex2",
+ moduleName: "mybin2",
+ moduleVariant: vendorVariant + "_myapex2",
+ libs: []string{
+ // should link with "unique" APEX(myapex2) variant of VNDK libs(libvndk/libc++)
+ "out/soong/.intermediates/libvndk/" + vendorVariant + "_shared_myapex2/libvndk.so",
+ "out/soong/.intermediates/" + cc.DefaultCcCommonTestModulesDir + "libc++/" + vendorVariant + "_shared_myapex2/libc++.so",
+ // unstable vendor libs have "merged" APEX variants
+ "out/soong/.intermediates/libvendor/" + vendorVariant + "_shared_apex10000/libvendor.so",
+ },
+ contents: []string{
+ "bin/mybin2",
+ "lib64/libvendor.so",
+ // VNDK libs are included as well
+ "lib64/libvndk.so",
+ "lib64/libc++.so",
+ },
+ requireVndkNamespace: false,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ // Check linked libs
+ ldRule := ctx.ModuleForTests(tc.moduleName, tc.moduleVariant).Rule("ld")
+ libs := names(ldRule.Args["libFlags"])
+ for _, lib := range tc.libs {
+ ensureListContains(t, libs, lib)
+ }
+ // Check apex contents
+ ensureExactContents(t, ctx, tc.apexName, "android_common_"+tc.apexName+"_image", tc.contents)
- // VNDK libs are not included when use_vndk_as_stable: true
- ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
- "bin/mybin",
- "lib64/libvendor.so",
- })
-
- apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
- requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
- ensureListContains(t, requireNativeLibs, ":vndk")
+ // Check "requireNativeLibs"
+ apexManifestRule := ctx.ModuleForTests(tc.apexName, "android_common_"+tc.apexName+"_image").Rule("apexManifestRule")
+ requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
+ if tc.requireVndkNamespace {
+ ensureListContains(t, requireNativeLibs, ":vndk")
+ } else {
+ ensureListNotContains(t, requireNativeLibs, ":vndk")
+ }
+ })
+ }
}
func TestProductVariant(t *testing.T) {
@@ -2900,7 +2889,7 @@
)
cflags := strings.Fields(
- ctx.ModuleForTests("foo", "android_product.29_arm64_armv8-a_apex10000").Rule("cc").Args["cFlags"])
+ ctx.ModuleForTests("foo", "android_product.29_arm64_armv8-a_myapex").Rule("cc").Args["cFlags"])
ensureListContains(t, cflags, "-D__ANDROID_VNDK__")
ensureListContains(t, cflags, "-D__ANDROID_APEX__")
ensureListContains(t, cflags, "-D__ANDROID_PRODUCT__")
@@ -2944,41 +2933,6 @@
}
}
-func TestAndroidMk_UseVendorRequired(t *testing.T) {
- ctx := testApex(t, `
- apex {
- name: "myapex",
- key: "myapex.key",
- use_vendor: true,
- native_shared_libs: ["mylib"],
- updatable: false,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "mylib",
- vendor_available: true,
- apex_available: ["myapex"],
- }
- `,
- setUseVendorAllowListForTest([]string{"myapex"}),
- )
-
- apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").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_REQUIRED_MODULES += libc libm libdl\n")
-}
-
func TestAndroidMk_VendorApexRequired(t *testing.T) {
ctx := testApex(t, `
apex {
@@ -3333,7 +3287,6 @@
"myapex",
"otherapex",
],
- use_apex_name_macro: true,
recovery_available: true,
min_sdk_version: "29",
}
@@ -3348,13 +3301,11 @@
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")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ 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")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
// 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
@@ -3363,42 +3314,15 @@
mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- // APEX variant has __ANDROID_APEX__ defined
- mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
-
- // APEX variant has __ANDROID_APEX__ defined
- mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"]
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
-
// recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
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__")
- // When a dependency of a cc_library sets use_apex_name_macro: true each apex gets a unique
- // variant.
-
// 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__")
- // APEX variant has __ANDROID_APEX__ defined
- mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
-
- // APEX variant has __ANDROID_APEX__ defined
- mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"]
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
-
// recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
mylibCFlags = ctx.ModuleForTests("mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
@@ -4428,7 +4352,7 @@
apex {
name: "myapex",
key: "myapex.key",
- binaries: ["myscript"],
+ sh_binaries: ["myscript"],
updatable: false,
}
@@ -6384,7 +6308,7 @@
})
// Permission XML should point to the activated path of impl jar of java_sdk_library
sdkLibrary := ctx.ModuleForTests("foo.xml", "android_common_myapex").Rule("java_sdk_xml")
- ensureContains(t, sdkLibrary.RuleParams.Command, `<library name=\"foo\" file=\"/apex/myapex/javalib/foo.jar\"`)
+ ensureMatches(t, sdkLibrary.RuleParams.Command, `<library\\n\s+name=\\\"foo\\\"\\n\s+file=\\\"/apex/myapex/javalib/foo.jar\\\"`)
}
func TestJavaSDKLibrary_WithinApex(t *testing.T) {
@@ -7215,6 +7139,7 @@
`, insert))
}
}),
+ dexpreopt.FixtureSetBootImageProfiles("art/build/boot/boot-image-profile.txt"),
).
ExtendWithErrorHandler(errorHandler).
RunTestWithBp(t, bp)
@@ -7222,6 +7147,75 @@
return result.TestContext
}
+func TestDuplicateDeapexeresFromPrebuiltApexes(t *testing.T) {
+ preparers := android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithApexBuildComponents,
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ "Multiple installable prebuilt APEXes provide ambiguous deapexers: com.android.myapex and com.mycompany.android.myapex"))
+
+ bpBase := `
+ apex_set {
+ name: "com.android.myapex",
+ installable: true,
+ exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ set: "myapex.apks",
+ }
+
+ apex_set {
+ name: "com.mycompany.android.myapex",
+ apex_name: "com.android.myapex",
+ installable: true,
+ exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ set: "company-myapex.apks",
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "my-bootclasspath-fragment",
+ apex_available: ["com.android.myapex"],
+ %s
+ }
+ `
+
+ t.Run("java_import", func(t *testing.T) {
+ _ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ apex_available: ["com.android.myapex"],
+ }
+ `)
+ })
+
+ t.Run("java_sdk_library_import", func(t *testing.T) {
+ _ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
+ java_sdk_library_import {
+ name: "libfoo",
+ public: {
+ jars: ["libbar.jar"],
+ },
+ apex_available: ["com.android.myapex"],
+ }
+ `)
+ })
+
+ t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) {
+ _ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `
+ image_name: "art",
+ contents: ["libfoo"],
+ `)+`
+ java_sdk_library_import {
+ name: "libfoo",
+ public: {
+ jars: ["libbar.jar"],
+ },
+ apex_available: ["com.android.myapex"],
+ }
+ `)
+ })
+}
+
func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
apex {
@@ -7253,6 +7247,23 @@
`)
}
+func TestUpdatable_cannot_be_vendor_apex(t *testing.T) {
+ testApexError(t, `"myapex" .*: updatable: vendor APEXes are not updatable`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: true,
+ soc_specific: 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 {
@@ -7519,6 +7530,7 @@
apex_available: ["myapex"],
sdk_version: "none",
system_modules: "none",
+ min_sdk_version: "30",
}
java_library {
name: "nonbcp_lib2",
@@ -7527,9 +7539,11 @@
permitted_packages: ["a.b"],
sdk_version: "none",
system_modules: "none",
+ min_sdk_version: "30",
}
apex {
name: "myapex",
+ min_sdk_version: "30",
key: "myapex.key",
java_libs: ["bcp_lib1", "nonbcp_lib2"],
updatable: false,
@@ -7542,8 +7556,8 @@
},
},
{
- name: "Bootclasspath apex jar not satisfying allowed module packages.",
- expectedError: `module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only allow these packages: foo.bar. Please jarjar or move code around.`,
+ name: "Bootclasspath apex jar not satisfying allowed module packages on Q.",
+ expectedError: `(?s)module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only use these package prefixes: foo.bar with min_sdk < T. Please consider the following alternatives:\n 1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library. 2. Move the offending code into an allowed package.\n 3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
bp: `
java_library {
name: "bcp_lib1",
@@ -7552,6 +7566,7 @@
permitted_packages: ["foo.bar"],
sdk_version: "none",
system_modules: "none",
+ min_sdk_version: "29",
}
java_library {
name: "bcp_lib2",
@@ -7560,9 +7575,85 @@
permitted_packages: ["foo.bar", "bar.baz"],
sdk_version: "none",
system_modules: "none",
+ min_sdk_version: "29",
}
apex {
name: "myapex",
+ min_sdk_version: "29",
+ key: "myapex.key",
+ java_libs: ["bcp_lib1", "bcp_lib2"],
+ updatable: false,
+ }
+ `,
+ bootJars: []string{"bcp_lib1", "bcp_lib2"},
+ modulesPackages: map[string][]string{
+ "myapex": []string{
+ "foo.bar",
+ },
+ },
+ },
+ {
+ name: "Bootclasspath apex jar not satisfying allowed module packages on R.",
+ expectedError: `(?s)module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only use these package prefixes: foo.bar with min_sdk < T. Please consider the following alternatives:\n 1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library. 2. Move the offending code into an allowed package.\n 3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
+ bp: `
+ java_library {
+ name: "bcp_lib1",
+ srcs: ["lib1/src/*.java"],
+ apex_available: ["myapex"],
+ permitted_packages: ["foo.bar"],
+ sdk_version: "none",
+ system_modules: "none",
+ min_sdk_version: "30",
+ }
+ java_library {
+ name: "bcp_lib2",
+ srcs: ["lib2/src/*.java"],
+ apex_available: ["myapex"],
+ permitted_packages: ["foo.bar", "bar.baz"],
+ sdk_version: "none",
+ system_modules: "none",
+ min_sdk_version: "30",
+ }
+ apex {
+ name: "myapex",
+ min_sdk_version: "30",
+ key: "myapex.key",
+ java_libs: ["bcp_lib1", "bcp_lib2"],
+ updatable: false,
+ }
+ `,
+ bootJars: []string{"bcp_lib1", "bcp_lib2"},
+ modulesPackages: map[string][]string{
+ "myapex": []string{
+ "foo.bar",
+ },
+ },
+ },
+ {
+ name: "Bootclasspath apex jar >= T not satisfying Q/R/S allowed module packages.",
+ expectedError: "",
+ bp: `
+ java_library {
+ name: "bcp_lib1",
+ srcs: ["lib1/src/*.java"],
+ apex_available: ["myapex"],
+ permitted_packages: ["foo.bar"],
+ sdk_version: "none",
+ system_modules: "none",
+ min_sdk_version: "current",
+ }
+ java_library {
+ name: "bcp_lib2",
+ srcs: ["lib2/src/*.java"],
+ apex_available: ["myapex"],
+ permitted_packages: ["foo.bar", "bar.baz"],
+ sdk_version: "none",
+ system_modules: "none",
+ min_sdk_version: "current",
+ }
+ apex {
+ name: "myapex",
+ min_sdk_version: "current",
key: "myapex.key",
java_libs: ["bcp_lib1", "bcp_lib2"],
updatable: false,
@@ -7863,6 +7954,28 @@
name: "myapex",
key: "myapex.key",
updatable: false,
+ custom_sign_tool: "sign_myapex",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+
+ apexKeysText := ctx.SingletonForTests("apex_keys_text")
+ content := apexKeysText.MaybeDescription("apexkeys.txt").BuildParams.Args["content"]
+ ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system_ext" sign_tool="sign_myapex"`)
+}
+
+func TestApexKeysTxtOverrides(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: false,
+ custom_sign_tool: "sign_myapex",
}
apex_key {
@@ -8439,6 +8552,309 @@
`)
}
+func TestAndroidMk_DexpreoptBuiltInstalledForApex(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: false,
+ java_libs: ["foo"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["foo.java"],
+ apex_available: ["myapex"],
+ installable: true,
+ }
+ `,
+ dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+ )
+
+ apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
+ 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")
+}
+
+func TestAndroidMk_DexpreoptBuiltInstalledForApex_Prebuilt(t *testing.T) {
+ ctx := testApex(t, `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["foo"],
+ }
+
+ java_import {
+ name: "foo",
+ jars: ["foo.jar"],
+ apex_available: ["myapex"],
+ }
+ `,
+ dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+ )
+
+ prebuilt := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
+ entriesList := android.AndroidMkEntriesForTest(t, ctx, prebuilt)
+ mainModuleEntries := entriesList[0]
+ android.AssertArrayString(t,
+ "LOCAL_REQUIRED_MODULES",
+ mainModuleEntries.EntryMap["LOCAL_REQUIRED_MODULES"],
+ []string{
+ "foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex",
+ "foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex",
+ })
+}
+
+func TestAndroidMk_RequiredModules(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: false,
+ java_libs: ["foo"],
+ required: ["otherapex"],
+ }
+
+ apex {
+ name: "otherapex",
+ key: "myapex.key",
+ updatable: false,
+ java_libs: ["foo"],
+ required: ["otherapex"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["foo.java"],
+ apex_available: ["myapex", "otherapex"],
+ installable: true,
+ }
+ `)
+
+ apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
+ var builder strings.Builder
+ data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
+ androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherapex")
+}
+
+func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) {
+ preparer := android.GroupFixturePreparers(
+ PrepareForTestWithApexBuildComponents,
+ prepareForTestWithMyapex,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.PrepareForTestWithJavaDefaultModules,
+ android.PrepareForTestWithAndroidBuildComponents,
+ dexpreopt.FixtureSetApexBootJars("myapex:mybootclasspathlib"),
+ dexpreopt.FixtureSetApexSystemServerJars("myapex:mysystemserverclasspathlib"),
+ )
+
+ // Test java_sdk_library in bootclasspath_fragment may define higher min_sdk_version than the apex
+ t.Run("bootclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) {
+ preparer.RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: ["mybootclasspathfragment"],
+ min_sdk_version: "30",
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspathfragment",
+ contents: ["mybootclasspathlib"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library {
+ name: "mybootclasspathlib",
+ srcs: ["mybootclasspathlib.java"],
+ apex_available: ["myapex"],
+ compile_dex: true,
+ unsafe_ignore_missing_latest_api: true,
+ min_sdk_version: "31",
+ static_libs: ["util"],
+ }
+
+ java_library {
+ name: "util",
+ srcs: ["a.java"],
+ apex_available: ["myapex"],
+ min_sdk_version: "31",
+ static_libs: ["another_util"],
+ }
+
+ java_library {
+ name: "another_util",
+ srcs: ["a.java"],
+ min_sdk_version: "31",
+ apex_available: ["myapex"],
+ }
+ `)
+ })
+
+ // Test java_sdk_library in systemserverclasspath_fragment may define higher min_sdk_version than the apex
+ t.Run("systemserverclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) {
+ preparer.RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
+ min_sdk_version: "30",
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ contents: ["mysystemserverclasspathlib"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library {
+ name: "mysystemserverclasspathlib",
+ srcs: ["mysystemserverclasspathlib.java"],
+ apex_available: ["myapex"],
+ compile_dex: true,
+ min_sdk_version: "32",
+ unsafe_ignore_missing_latest_api: true,
+ static_libs: ["util"],
+ }
+
+ java_library {
+ name: "util",
+ srcs: ["a.java"],
+ apex_available: ["myapex"],
+ min_sdk_version: "31",
+ static_libs: ["another_util"],
+ }
+
+ java_library {
+ name: "another_util",
+ srcs: ["a.java"],
+ min_sdk_version: "31",
+ apex_available: ["myapex"],
+ }
+ `)
+ })
+
+ t.Run("bootclasspath_fragment jar must set min_sdk_version", func(t *testing.T) {
+ preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mybootclasspathlib".*must set min_sdk_version`)).
+ RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: ["mybootclasspathfragment"],
+ min_sdk_version: "30",
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspathfragment",
+ contents: ["mybootclasspathlib"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library {
+ name: "mybootclasspathlib",
+ srcs: ["mybootclasspathlib.java"],
+ apex_available: ["myapex"],
+ compile_dex: true,
+ unsafe_ignore_missing_latest_api: true,
+ }
+ `)
+ })
+
+ t.Run("systemserverclasspath_fragment jar must set min_sdk_version", func(t *testing.T) {
+ preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mysystemserverclasspathlib".*must set min_sdk_version`)).
+ RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
+ min_sdk_version: "30",
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ contents: ["mysystemserverclasspathlib"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library {
+ name: "mysystemserverclasspathlib",
+ srcs: ["mysystemserverclasspathlib.java"],
+ apex_available: ["myapex"],
+ compile_dex: true,
+ unsafe_ignore_missing_latest_api: true,
+ }
+ `)
+ })
+}
+
+// Verifies that the APEX depends on all the Make modules in the list.
+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)
+ }
+}
+
+// Verifies that the APEX does not depend on any of the Make modules in the list.
+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)
+ }
+}
+
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index cb7d3d1..ce6b7f7 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -16,11 +16,13 @@
import (
"fmt"
+ "path"
"sort"
"strings"
"testing"
"android/soong/android"
+ "android/soong/dexpreopt"
"android/soong/java"
"github.com/google/blueprint/proptools"
@@ -35,11 +37,14 @@
)
// Some additional files needed for the art apex.
-var prepareForTestWithArtApex = android.FixtureMergeMockFs(android.MockFS{
- "com.android.art.avbpubkey": nil,
- "com.android.art.pem": nil,
- "system/sepolicy/apex/com.android.art-file_contexts": nil,
-})
+var prepareForTestWithArtApex = android.GroupFixturePreparers(
+ android.FixtureMergeMockFs(android.MockFS{
+ "com.android.art.avbpubkey": nil,
+ "com.android.art.pem": nil,
+ "system/sepolicy/apex/com.android.art-file_contexts": nil,
+ }),
+ dexpreopt.FixtureSetBootImageProfiles("art/build/boot/boot-image-profile.txt"),
+)
func TestBootclasspathFragments(t *testing.T) {
result := android.GroupFixturePreparers(
@@ -405,9 +410,11 @@
// bootclasspath_fragment's contents property.
java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
addSource("foo", "bar"),
+ java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
).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/arm/boot.art",
"javalib/arm/boot.oat",
@@ -431,12 +438,80 @@
`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) {
+ 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"),
+ 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("boot image disable generate profile", 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"),
+ dexpreopt.FixtureDisableGenerateProfile(true),
+ ).RunTest(t)
+
+ files := getFiles(t, result.TestContext, "com.android.art", "android_common_com.android.art_image")
+ for _, file := range files {
+ matched, _ := path.Match("etc/boot-image.prof", file.path)
+ android.AssertBoolEquals(t, "\"etc/boot-image.prof\" should not be in the APEX", matched, false)
+ }
+ })
+
t.Run("boot image files with preferred prebuilt", func(t *testing.T) {
result := android.GroupFixturePreparers(
commonPreparer,
@@ -448,9 +523,12 @@
// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
addPrebuilt(true, "foo", "bar"),
+
+ java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
).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/arm/boot.art",
"javalib/arm/boot.oat",
@@ -475,12 +553,66 @@
`prebuilt_com.android.art`,
})
+ // The boot images are installed in the APEX by Soong, so there shouldn't be any dexpreopt-related Make modules.
+ ensureDoesNotContainRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "mybootclasspathfragment-dexpreopt-arm64-boot.art",
+ "mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+ "mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+ "mybootclasspathfragment-dexpreopt-arm-boot.art",
+ "mybootclasspathfragment-dexpreopt-arm-boot.oat",
+ "mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+ })
+
// Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
// locations for the art image.
module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art")
checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
})
+ t.Run("boot image files from preferred prebuilt no boot image in apex", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ commonPreparer,
+
+ // Configure some libraries in the art bootclasspath_fragment that match the source
+ // bootclasspath_fragment's contents property.
+ java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+ addSource("foo", "bar"),
+
+ // Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
+ addPrebuilt(true, "foo", "bar"),
+
+ java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
+ ).RunTest(t)
+
+ ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "etc/boot-image.prof",
+ "etc/classpaths/bootclasspath.pb",
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+
+ ensureContainsRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "mybootclasspathfragment-dexpreopt-arm64-boot.art",
+ "mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+ "mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+ "mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+ "mybootclasspathfragment-dexpreopt-arm-boot.art",
+ "mybootclasspathfragment-dexpreopt-arm-boot.oat",
+ "mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+ "mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+ })
+ })
+
t.Run("source with inconsistency between config and contents", func(t *testing.T) {
android.GroupFixturePreparers(
commonPreparer,
@@ -542,7 +674,7 @@
}
func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) {
- result := android.GroupFixturePreparers(
+ preparers := android.GroupFixturePreparers(
prepareForTestWithBootclasspathFragment,
prepareForTestWithArtApex,
@@ -553,7 +685,10 @@
// Configure some libraries in the art bootclasspath_fragment.
java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
- ).RunTestWithBp(t, `
+ java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
+ )
+
+ bp := `
prebuilt_apex {
name: "com.android.art",
arch: {
@@ -599,22 +734,45 @@
all_flags: "mybootclasspathfragment/all-flags.csv",
},
}
- `)
- java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
- `com.android.art.apex.selector`,
- `prebuilt_mybootclasspathfragment`,
+ // A prebuilt apex with the same apex_name that shouldn't interfere when it isn't enabled.
+ prebuilt_apex {
+ name: "com.mycompany.android.art",
+ apex_name: "com.android.art",
+ %s
+ src: "com.mycompany.android.art.apex",
+ exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+ }
+ `
+
+ t.Run("disabled alternative APEX", func(t *testing.T) {
+ result := preparers.RunTestWithBp(t, fmt.Sprintf(bp, "enabled: false,"))
+
+ java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
+ `com.android.art.apex.selector`,
+ `prebuilt_mybootclasspathfragment`,
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{
+ `com.android.art.deapexer`,
+ `dex2oatd`,
+ `prebuilt_bar`,
+ `prebuilt_foo`,
+ })
+
+ 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")
})
- java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{
- `com.android.art.deapexer`,
- `dex2oatd`,
- `prebuilt_bar`,
- `prebuilt_foo`,
+ t.Run("enabled alternative APEX", func(t *testing.T) {
+ preparers.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ "Multiple installable prebuilt APEXes provide ambiguous deapexers: com.android.art and com.mycompany.android.art")).
+ RunTestWithBp(t, fmt.Sprintf(bp, ""))
})
-
- module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
- checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
}
// checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
diff --git a/apex/builder.go b/apex/builder.go
index c05c20c..fc4bf8a 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -65,24 +65,13 @@
pctx.HostBinToolVariable("extract_apks", "extract_apks")
pctx.HostBinToolVariable("make_f2fs", "make_f2fs")
pctx.HostBinToolVariable("sload_f2fs", "sload_f2fs")
+ pctx.HostBinToolVariable("make_erofs", "make_erofs")
pctx.HostBinToolVariable("apex_compression_tool", "apex_compression_tool")
+ pctx.HostBinToolVariable("dexdeps", "dexdeps")
pctx.SourcePathVariable("genNdkUsedbyApexPath", "build/soong/scripts/gen_ndk_usedby_apex.sh")
}
var (
- // Create a canned fs config file where all files and directories are
- // by default set to (uid/gid/mode) = (1000/1000/0644)
- // TODO(b/113082813) make this configurable using config.fs syntax
- generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
- Command: `( set -e; echo '/ 1000 1000 0755' ` +
- `&& for i in ${ro_paths}; do echo "/$$i 1000 1000 0644"; done ` +
- `&& for i in ${exec_paths}; do echo "/$$i 0 2000 0755"; done ` +
- `&& ( tr ' ' '\n' <${out}.apklist | for i in ${apk_paths}; do read apk; echo "/$$i 0 2000 0755"; zipinfo -1 $$apk | sed "s:\(.*\):/$$i/\1 1000 1000 0644:"; done ) ) > ${out}`,
- Description: "fs_config ${out}",
- Rspfile: "$out.apklist",
- RspfileContent: "$in",
- }, "ro_paths", "exec_paths", "apk_paths")
-
apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{
Command: `rm -f $out && ${jsonmodify} $in ` +
`-a provideNativeLibs ${provideNativeLibs} ` +
@@ -105,7 +94,7 @@
Description: "convert ${in}=>${out}",
})
- // TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
+ // TODO(b/113233103): make sure that file_contexts is as expected, i.e., validate
// against the binary policy using sefcontext_compiler -p <policy>.
// TODO(b/114327326): automate the generation of file_contexts
@@ -120,7 +109,7 @@
`--payload_type image ` +
`--key ${key} ${opt_flags} ${image_dir} ${out} `,
CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
- "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", "${sload_f2fs}",
+ "${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}",
@@ -408,22 +397,47 @@
return output.OutputPath
}
+func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path {
+ return java.ManifestFixer(ctx, androidManifestFile, java.ManifestFixerParams{
+ TestOnly: true,
+ })
+}
+
// buildUnflattendApex creates build rules to build an APEX using apexer.
func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
apexType := a.properties.ApexType
suffix := apexType.suffix()
+ apexName := proptools.StringDefault(a.properties.Apex_name, a.BaseModuleName())
////////////////////////////////////////////////////////////////////////////////////////////
// Step 1: copy built files to appropriate directories under the image directory
imageDir := android.PathForModuleOut(ctx, "image"+suffix)
+ installSymbolFiles := !ctx.Config().KatiEnabled() || a.ExportedToMake()
+
+ // 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 {
+ 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)
for _, fi := range a.filesInfo {
destPath := imageDir.Join(ctx, fi.path()).String()
-
// Prepare the destination path
destPathDir := filepath.Dir(destPath)
if fi.class == appSet {
@@ -431,6 +445,8 @@
}
copyCommands = append(copyCommands, "mkdir -p "+destPathDir)
+ installMapPath := fi.builtFile
+
// 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() {
@@ -438,20 +454,38 @@
pathOnDevice := filepath.Join("/system", fi.path())
copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
} else {
+ var installedPath android.InstallPath
if fi.class == appSet {
copyCommands = append(copyCommands,
- fmt.Sprintf("unzip -qDD -d %s %s", destPathDir, fi.builtFile.String()))
+ fmt.Sprintf("unzip -qDD -d %s %s", destPathDir,
+ fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs().String()))
+ if installSymbolFiles {
+ installedPath = ctx.InstallFileWithExtraFilesZip(pathWhenActivated.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)
+ }
}
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.
- for _, symlinkPath := range fi.symlinkPaths() {
- symlinkDest := imageDir.Join(ctx, symlinkPath).String()
- copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
+ // Create additional symlinks pointing the file inside the APEX (if any). Note that
+ // this is independent from the symlink optimization.
+ for _, symlinkPath := range fi.symlinkPaths() {
+ 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)
+ }
+ }
+
+ installMapPath = installedPath
}
// Copy the test files (if any)
@@ -468,8 +502,21 @@
copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest)
implicitInputs = append(implicitInputs, d.SrcPath)
}
+
+ 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)...)
+ a.SetLicenseInstallMap(installs)
+ }
////////////////////////////////////////////////////////////////////////////////////////////
// Step 1.a: Write the list of files in this APEX to a txt file and compare it against
@@ -523,61 +570,17 @@
}
unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")
- outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
+ outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
- // Figure out if need to compress apex.
- compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, false) && !a.testApex && !ctx.Config().UnbundledBuildApps()
+ // 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 {
+
////////////////////////////////////////////////////////////////////////////////////
// Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
// in this APEX. The file will be used by apexer in later steps.
- // TODO(jiyong): make this as a function
- // TODO(jiyong): use the RuleBuilder
- var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
- var executablePaths []string // this also includes dirs
- var extractedAppSetPaths android.Paths
- var extractedAppSetDirs []string
- for _, f := range a.filesInfo {
- pathInApex := f.path()
- if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
- executablePaths = append(executablePaths, pathInApex)
- for _, d := range f.dataPaths {
- readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.RelativeInstallPath, d.SrcPath.Rel()))
- }
- for _, s := range f.symlinks {
- executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
- }
- } else if f.class == appSet {
- extractedAppSetPaths = append(extractedAppSetPaths, f.builtFile)
- extractedAppSetDirs = append(extractedAppSetDirs, f.installDir)
- } else {
- readOnlyPaths = append(readOnlyPaths, pathInApex)
- }
- dir := f.installDir
- for !android.InList(dir, executablePaths) && dir != "" {
- executablePaths = append(executablePaths, dir)
- dir, _ = filepath.Split(dir) // move up to the parent
- if len(dir) > 0 {
- // remove trailing slash
- dir = dir[:len(dir)-1]
- }
- }
- }
- sort.Strings(readOnlyPaths)
- sort.Strings(executablePaths)
- cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
- ctx.Build(pctx, android.BuildParams{
- Rule: generateFsConfig,
- Output: cannedFsConfig,
- Description: "generate fs config",
- Inputs: extractedAppSetPaths,
- Args: map[string]string{
- "ro_paths": strings.Join(readOnlyPaths, " "),
- "exec_paths": strings.Join(executablePaths, " "),
- "apk_paths": strings.Join(extractedAppSetDirs, " "),
- },
- })
+ cannedFsConfig := a.buildCannedFsConfig(ctx)
implicitInputs = append(implicitInputs, cannedFsConfig)
////////////////////////////////////////////////////////////////////////////////////
@@ -598,8 +601,15 @@
if a.properties.AndroidManifest != nil {
androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest))
+
+ if a.testApex {
+ androidManifestFile = markManifestTestOnly(ctx, androidManifestFile)
+ }
+
implicitInputs = append(implicitInputs, androidManifestFile)
optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
+ } else if a.testApex {
+ optFlags = append(optFlags, "--test_only")
}
// Determine target/min sdk version from the context
@@ -706,24 +716,38 @@
"readelf": "${config.ClangBin}/llvm-readelf",
},
})
- a.apisUsedByModuleFile = apisUsedbyOutputFile
+ a.nativeApisUsedByModuleFile = apisUsedbyOutputFile
- var libNames []string
+ var nativeLibNames []string
for _, f := range a.filesInfo {
if f.class == nativeSharedLib {
- libNames = append(libNames, f.stem())
+ nativeLibNames = append(nativeLibNames, f.stem())
}
}
apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt")
- ndkLibraryList := android.PathForSource(ctx, "system/core/rootdir/etc/public.libraries.android.txt")
rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")).
Output(apisBackedbyOutputFile).
- Input(ndkLibraryList).
- Flags(libNames)
+ Flags(nativeLibNames)
rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex")
- a.apisBackedByModuleFile = apisBackedbyOutputFile
+ a.nativeApisBackedByModuleFile = apisBackedbyOutputFile
+
+ var javaLibOrApkPath []android.Path
+ for _, f := range a.filesInfo {
+ if f.class == javaSharedLib || f.class == app {
+ javaLibOrApkPath = append(javaLibOrApkPath, f.builtFile)
+ }
+ }
+ javaApiUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.xml")
+ javaUsedByRule := android.NewRuleBuilder(pctx, ctx)
+ javaUsedByRule.Command().
+ Tool(android.PathForSource(ctx, "build/soong/scripts/gen_java_usedby_apex.sh")).
+ BuiltTool("dexdeps").
+ Output(javaApiUsedbyOutputFile).
+ Inputs(javaLibOrApkPath)
+ javaUsedByRule.Build("java_usedby_list", "Generate Java APIs used by Apex")
+ a.javaApisUsedByModuleFile = javaApiUsedbyOutputFile
bundleConfig := a.buildBundleConfig(ctx)
@@ -824,47 +848,55 @@
a.outputFile = signedCompressedOutputFile
}
+ installSuffix := suffix
+ if a.isCompressed {
+ installSuffix = imageCapexSuffix
+ }
+
// Install to $OUT/soong/{target,host}/.../apex.
- ctx.InstallFile(a.installDir, a.Name()+suffix, a.outputFile)
+ a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
+ a.compatSymlinks.Paths()...)
// installed-files.txt is dist'ed
a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
}
-// Context "decorator", overriding the InstallBypassMake method to always reply `true`.
-type flattenedApexContext struct {
- android.ModuleContext
-}
-
-func (c *flattenedApexContext) InstallBypassMake() bool {
- return true
-}
-
// buildFlattenedApex creates rules for a flattened APEX. Flattened APEX actually doesn't have a
// single output file. It is a phony target for all the files under /system/apex/<name> directory.
// This function creates the installation rules for the files.
func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
bundleName := a.Name()
+ installedSymlinks := append(android.InstallPaths(nil), a.compatSymlinks...)
if a.installable() {
for _, fi := range a.filesInfo {
dir := filepath.Join("apex", bundleName, fi.installDir)
- target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.stem(), fi.builtFile)
- for _, sym := range fi.symlinks {
- ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
+ 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())
+ 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))
+ }
}
}
+
+ // Create install rules for the files added in GenerateAndroidBuildActions after
+ // buildFlattenedApex is called. Add the links to system libs (if any) as dependencies
+ // of the apex_manifest.pb file since it is always present.
+ dir := filepath.Join("apex", bundleName)
+ installDir := android.PathForModuleInstall(ctx, dir)
+ ctx.InstallFile(installDir, "apex_manifest.pb", a.manifestPbOut, installedSymlinks.Paths()...)
+ ctx.InstallFile(installDir, "apex_pubkey", a.publicKeyFile)
}
a.fileContexts = a.buildFileContexts(ctx)
- // Temporarily wrap the original `ctx` into a `flattenedApexContext` to have it reply true
- // to `InstallBypassMake()` (thus making the call `android.PathForModuleInstall` below use
- // `android.pathForInstallInMakeDir` instead of `android.PathForOutput`) to return the
- // correct path to the flattened APEX (as its contents is installed by Make, not Soong).
- // TODO(jiyong): Why do we need to set outputFile for flattened APEX? We don't seem to use
- // it and it actually points to a path that can never be built. Remove this.
- factx := flattenedApexContext{ctx}
- a.outputFile = android.PathForModuleInstall(&factx, "apex", bundleName)
+ a.outputFile = android.PathForModuleInstall(ctx, "apex", bundleName)
}
// getCertificateAndPrivateKey retrieves the cert and the private key that will be used to sign
@@ -996,3 +1028,65 @@
a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build())
}
+
+func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath {
+ var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
+ var executablePaths []string // this also includes dirs
+ var appSetDirs []string
+ appSetFiles := make(map[string]android.Path)
+ for _, f := range a.filesInfo {
+ pathInApex := f.path()
+ if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
+ executablePaths = append(executablePaths, pathInApex)
+ for _, d := range f.dataPaths {
+ readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.RelativeInstallPath, d.SrcPath.Rel()))
+ }
+ for _, s := range f.symlinks {
+ executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
+ }
+ } else if f.class == appSet {
+ appSetDirs = append(appSetDirs, f.installDir)
+ appSetFiles[f.installDir] = f.builtFile
+ } else {
+ readOnlyPaths = append(readOnlyPaths, pathInApex)
+ }
+ dir := f.installDir
+ for !android.InList(dir, executablePaths) && dir != "" {
+ executablePaths = append(executablePaths, dir)
+ dir, _ = filepath.Split(dir) // move up to the parent
+ if len(dir) > 0 {
+ // remove trailing slash
+ dir = dir[:len(dir)-1]
+ }
+ }
+ }
+ sort.Strings(readOnlyPaths)
+ sort.Strings(executablePaths)
+ sort.Strings(appSetDirs)
+
+ cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
+ builder := android.NewRuleBuilder(pctx, ctx)
+ cmd := builder.Command()
+ cmd.Text("(")
+ cmd.Text("echo '/ 1000 1000 0755';")
+ for _, p := range readOnlyPaths {
+ cmd.Textf("echo '/%s 1000 1000 0644';", p)
+ }
+ for _, p := range executablePaths {
+ cmd.Textf("echo '/%s 0 2000 0755';", p)
+ }
+ for _, dir := range appSetDirs {
+ cmd.Textf("echo '/%s 0 2000 0755';", dir)
+ file := appSetFiles[dir]
+ cmd.Text("zipinfo -1").Input(file).Textf(`| sed "s:\(.*\):/%s/\1 1000 1000 0644:";`, dir)
+ }
+ // Custom fs_config is "appended" to the last so that entries from the file are preferred
+ // over default ones set above.
+ if a.properties.Canned_fs_config != nil {
+ cmd.Text("cat").Input(android.PathForModuleSrc(ctx, *a.properties.Canned_fs_config))
+ }
+ cmd.Text(")").FlagWithOutput("> ", cannedFsConfig)
+ builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName()))
+
+ return cannedFsConfig.OutputPath
+}
diff --git a/apex/key.go b/apex/key.go
index e2695d7..829410e 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -34,8 +34,6 @@
func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
-
- android.RegisterBp2BuildMutator("apex_key", ApexKeyBp2Build)
}
type apexKey struct {
@@ -123,13 +121,18 @@
containerCertificate string
containerPrivateKey string
partition string
+ signTool string
}
toString := func(e apexKeyEntry) string {
- format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q\n"
+ signTool := ""
+ if e.signTool != "" {
+ signTool = fmt.Sprintf(" sign_tool=%q", e.signTool)
+ }
+ format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q%s\n"
if e.presigned {
- return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition)
+ return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition, signTool)
} else {
- return fmt.Sprintf(format, e.name, e.publicKey, e.privateKey, e.containerCertificate, e.containerPrivateKey, e.partition)
+ return fmt.Sprintf(format, e.name, e.publicKey, e.privateKey, e.containerCertificate, e.containerPrivateKey, e.partition, signTool)
}
}
@@ -145,6 +148,7 @@
containerCertificate: pem.String(),
containerPrivateKey: key.String(),
partition: m.PartitionTag(ctx.DeviceConfig()),
+ signTool: proptools.String(m.properties.Custom_sign_tool),
}
}
})
@@ -203,20 +207,9 @@
Private_key bazel.LabelAttribute
}
-func ApexKeyBp2Build(ctx android.TopDownMutatorContext) {
- module, ok := ctx.Module().(*apexKey)
- if !ok {
- // Not an APEX key
- return
- }
- if !module.ConvertWithBp2build(ctx) {
- return
- }
- if ctx.ModuleType() != "apex_key" {
- return
- }
-
- apexKeyBp2BuildInternal(ctx, module)
+// ConvertWithBp2build performs conversion apexKey for bp2build
+func (m *apexKey) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ apexKeyBp2BuildInternal(ctx, m)
}
func apexKeyBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexKey) {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 55a9e42..158c804 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -17,6 +17,7 @@
import (
"fmt"
"io"
+ "path/filepath"
"strconv"
"strings"
@@ -54,18 +55,18 @@
installDir android.InstallPath
installFilename string
+ installedFile android.InstallPath
outputApex android.WritablePath
// A list of apexFile objects created in prebuiltCommon.initApexFilesForAndroidMk which are used
// to create make modules in prebuiltCommon.AndroidMkEntries.
apexFilesForAndroidMk []apexFile
- // list of commands to create symlinks for backward compatibility.
- // these commands will be attached as LOCAL_POST_INSTALL_CMD
- compatSymlinks []string
+ // Installed locations of symlinks for backward compatibility.
+ compatSymlinks android.InstallPaths
hostRequired []string
- postInstallCommands []string
+ requiredModuleNames []string
}
type sanitizedPrebuilt interface {
@@ -138,10 +139,6 @@
// to build the prebuilts themselves.
forceDisable = forceDisable || ctx.Config().UnbundledBuild()
- // Force disable the prebuilts when coverage is enabled.
- forceDisable = forceDisable || ctx.DeviceConfig().NativeCoverageEnabled()
- forceDisable = forceDisable || ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
-
// b/137216042 don't use prebuilts when address sanitizer is on, unless the prebuilt has a sanitized source
sanitized := ctx.Module().(sanitizedPrebuilt)
forceDisable = forceDisable || (android.InList("address", ctx.Config().SanitizeDevice()) && !sanitized.hasSanitizedSource("address"))
@@ -199,9 +196,19 @@
}
p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
}
- } else if tag == exportedBootclasspathFragmentTag ||
- tag == exportedSystemserverclasspathFragmentTag {
- // Visit the children of the bootclasspath_fragment and systemserver_fragment.
+ } else if tag == exportedBootclasspathFragmentTag {
+ bcpfModule, ok := child.(*java.PrebuiltBootclasspathFragmentModule)
+ if !ok {
+ ctx.PropertyErrorf("exported_bootclasspath_fragments", "%q is not a prebuilt_bootclasspath_fragment module", name)
+ return false
+ }
+ for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
+ p.requiredModuleNames = append(p.requiredModuleNames, makeModuleName)
+ }
+ // Visit the children of the bootclasspath_fragment.
+ return true
+ } else if tag == exportedSystemserverclasspathFragmentTag {
+ // Visit the children of the systemserver_fragment.
return true
}
@@ -215,6 +222,7 @@
entries.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", fi.targetRequiredModuleNames...)
entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", fi.hostRequiredModuleNames...)
}
+ entries.AddStrings("LOCAL_REQUIRED_MODULES", p.requiredModuleNames...)
}
func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
@@ -226,15 +234,12 @@
Host_required: p.hostRequired,
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
+ entries.SetPath("LOCAL_SOONG_INSTALLED_MODULE", p.installedFile)
+ entries.SetString("LOCAL_SOONG_INSTALL_PAIRS", p.outputApex.String()+":"+p.installedFile.String())
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.prebuiltCommonProperties.Overrides...)
- postInstallCommands := append([]string{}, p.postInstallCommands...)
- postInstallCommands = append(postInstallCommands, p.compatSymlinks...)
- if len(postInstallCommands) > 0 {
- entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(postInstallCommands, " && "))
- }
p.addRequiredModules(entries)
},
},
@@ -263,23 +268,15 @@
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
+ entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", filepath.Join(p.installDir.String(), fi.stem()))
+ entries.SetString("LOCAL_SOONG_INSTALL_PAIRS",
+ fi.builtFile.String()+":"+filepath.Join(p.installDir.String(), fi.stem()))
// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
// we will have foo.jar.jar
entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
- var classesJar android.Path
- var headerJar android.Path
- if javaModule, ok := fi.module.(java.ApexDependency); ok {
- classesJar = javaModule.ImplementationAndResourcesJars()[0]
- headerJar = javaModule.HeaderJars()[0]
- } else {
- classesJar = fi.builtFile
- headerJar = fi.builtFile
- }
- entries.SetString("LOCAL_SOONG_CLASSES_JAR", classesJar.String())
- entries.SetString("LOCAL_SOONG_HEADER_JAR", headerJar.String())
entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
entries.SetString("LOCAL_DEX_PREOPT", "false")
},
@@ -772,15 +769,15 @@
// Save the files that need to be made available to Make.
p.initApexFilesForAndroidMk(ctx)
- if p.installable() {
- ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
- }
-
// in case that prebuilt_apex replaces source apex (using prefer: prop)
- p.compatSymlinks = makeCompatSymlinks(p.BaseModuleName(), ctx)
+ p.compatSymlinks = makeCompatSymlinks(p.BaseModuleName(), ctx, true)
// or that prebuilt_apex overrides other apexes (using overrides: prop)
for _, overridden := range p.prebuiltCommonProperties.Overrides {
- p.compatSymlinks = append(p.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
+ p.compatSymlinks = append(p.compatSymlinks, makeCompatSymlinks(overridden, ctx, true)...)
+ }
+
+ if p.installable() {
+ p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks.Paths()...)
}
}
@@ -976,25 +973,14 @@
a.installDir = android.PathForModuleInstall(ctx, "apex")
if a.installable() {
- ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
+ a.installedFile = ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
}
// in case that apex_set replaces source apex (using prefer: prop)
- a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
+ a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, true)
// or that apex_set overrides other apexes (using overrides: prop)
for _, overridden := range a.prebuiltCommonProperties.Overrides {
- a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
- }
-
- if ctx.Config().InstallExtraFlattenedApexes() {
- // flattened apex should be in /system_ext/apex
- flattenedApexDir := android.PathForModuleInstall(&systemExtContext{ctx}, "apex", a.BaseModuleName())
- a.postInstallCommands = append(a.postInstallCommands,
- fmt.Sprintf("$(HOST_OUT_EXECUTABLES)/deapexer --debugfs_path $(HOST_OUT_EXECUTABLES)/debugfs extract %s %s",
- a.outputApex.String(),
- flattenedApexDir.ToMakePath().String(),
- ))
- a.hostRequired = []string{"deapexer", "debugfs"}
+ a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx, true)...)
}
}
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 412fa0e..d037664 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -231,3 +231,95 @@
`prebuilt_foo`,
})
}
+
+func TestSystemserverclasspathFragmentStandaloneContents(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithSystemserverclasspathFragment,
+ prepareForTestWithMyapex,
+ dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo"),
+ ).RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ systemserverclasspath_fragments: [
+ "mysystemserverclasspathfragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ standalone_contents: [
+ "foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ `)
+
+ ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ "etc/classpaths/systemserverclasspath.pb",
+ "javalib/foo.jar",
+ })
+}
+
+func TestPrebuiltStandaloneSystemserverclasspathFragmentContents(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithSystemserverclasspathFragment,
+ prepareForTestWithMyapex,
+ dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo"),
+ ).RunTestWithBp(t, `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
+ }
+
+ java_import {
+ name: "foo",
+ jars: ["foo.jar"],
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ prebuilt_systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ prefer: true,
+ standalone_contents: [
+ "foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ `)
+
+ java.CheckModuleDependencies(t, result.TestContext, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+ `myapex.deapexer`,
+ `prebuilt_foo`,
+ })
+}
diff --git a/apex/testing.go b/apex/testing.go
index 69bd73e..337c862 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -24,6 +24,7 @@
android.MockFS{
// Needed by apex.
"system/core/rootdir/etc/public.libraries.android.txt": nil,
+ "build/soong/scripts/gen_java_usedby_apex.sh": nil,
"build/soong/scripts/gen_ndk_backedby_apex.sh": nil,
// Needed by prebuilt_apex.
"build/soong/scripts/unpack-prebuilt-apex.sh": nil,
diff --git a/apex/vndk.go b/apex/vndk.go
index 75c0fb0..ef3e5e1 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -15,7 +15,6 @@
package apex
import (
- "path/filepath"
"strings"
"android/soong/android"
@@ -96,11 +95,14 @@
}
// name is module.BaseModuleName() which is used as LOCAL_MODULE_NAME and also LOCAL_OVERRIDES_*
-func makeCompatSymlinks(name string, ctx android.ModuleContext) (symlinks []string) {
+func makeCompatSymlinks(name string, ctx android.ModuleContext, primaryApex bool) (symlinks android.InstallPaths) {
// small helper to add symlink commands
- addSymlink := func(target, dir, linkName string) {
- link := filepath.Join(dir, linkName)
- symlinks = append(symlinks, "mkdir -p "+dir+" && rm -rf "+link+" && ln -sf "+target+" "+link)
+ addSymlink := func(target string, dir android.InstallPath, linkName string) {
+ if primaryApex {
+ symlinks = append(symlinks, ctx.InstallAbsoluteSymlink(dir, linkName, target))
+ } else {
+ symlinks = append(symlinks, dir.Join(ctx, linkName))
+ }
}
// TODO(b/142911355): [VNDK APEX] Fix hard-coded references to /system/lib/vndk
@@ -118,14 +120,15 @@
// the name of vndk apex is formatted "com.android.vndk.v" + version
apexName := vndkApexNamePrefix + vndkVersion
if ctx.Config().Android64() {
- addSymlink("/apex/"+apexName+"/lib64", "$(TARGET_OUT)/lib64", "vndk-sp-"+vndkVersion)
- addSymlink("/apex/"+apexName+"/lib64", "$(TARGET_OUT)/lib64", "vndk-"+vndkVersion)
+ dir := android.PathForModuleInPartitionInstall(ctx, "system", "lib64")
+ addSymlink("/apex/"+apexName+"/lib64", dir, "vndk-sp-"+vndkVersion)
+ addSymlink("/apex/"+apexName+"/lib64", dir, "vndk-"+vndkVersion)
}
if !ctx.Config().Android64() || ctx.DeviceConfig().DeviceSecondaryArch() != "" {
- addSymlink("/apex/"+apexName+"/lib", "$(TARGET_OUT)/lib", "vndk-sp-"+vndkVersion)
- addSymlink("/apex/"+apexName+"/lib", "$(TARGET_OUT)/lib", "vndk-"+vndkVersion)
+ dir := android.PathForModuleInPartitionInstall(ctx, "system", "lib")
+ addSymlink("/apex/"+apexName+"/lib", dir, "vndk-sp-"+vndkVersion)
+ addSymlink("/apex/"+apexName+"/lib", dir, "vndk-"+vndkVersion)
}
- return
}
// http://b/121248172 - create a link from /system/usr/icu to
@@ -133,19 +136,9 @@
// A symlink can't overwrite a directory and the /system/usr/icu directory once
// existed so the required structure must be created whatever we find.
if name == "com.android.i18n" {
- addSymlink("/apex/com.android.i18n/etc/icu", "$(TARGET_OUT)/usr", "icu")
- return
+ dir := android.PathForModuleInPartitionInstall(ctx, "system", "usr")
+ addSymlink("/apex/com.android.i18n/etc/icu", dir, "icu")
}
- // TODO(b/124106384): Clean up compat symlinks for ART binaries.
- if name == "com.android.art" || strings.HasPrefix(name, "com.android.art.") {
- addSymlink("/apex/com.android.art/bin/dalvikvm", "$(TARGET_OUT)/bin", "dalvikvm")
- dex2oat := "dex2oat32"
- if ctx.Config().Android64() {
- dex2oat = "dex2oat64"
- }
- addSymlink("/apex/com.android.art/bin/"+dex2oat, "$(TARGET_OUT)/bin", "dex2oat")
- return
- }
- return
+ return symlinks
}
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 8741afb..fd8cf67 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -18,6 +18,7 @@
"encoding/json"
"fmt"
"path/filepath"
+ "regexp"
"strings"
"github.com/google/blueprint/proptools"
@@ -59,6 +60,8 @@
InputDepSetIds []int
Mnemonic string
OutputIds []int
+ TemplateContent string
+ Substitutions []KeyValuePair
}
// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
@@ -100,6 +103,20 @@
artifactIdToPath map[int]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{
+ // 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$")
+
+// 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 {
@@ -163,7 +180,31 @@
}
}
}
- return inputPaths, nil
+
+ // TODO(b/197135294): Clean up this custom runfiles handling logic when
+ // SourceSymlinkManifest and SymlinkTree actions are supported.
+ filteredInputPaths := filterOutPy3wrapperAndManifestFileFromInputPaths(inputPaths)
+
+ return filteredInputPaths, nil
+}
+
+// See go/python-binary-host-mixed-build for more details.
+// 1) For py3wrapper.sh, there is no action for creating py3wrapper.sh in the aquery output of
+// Bazel py_binary targets, so there is no Ninja build statements generated for creating it.
+// 2) For MANIFEST file, SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
+// but it doesn't contain sufficient information so no Ninja build statements are generated
+// for creating it.
+// So in mixed build mode, when these two are used as input of some Ninja build statement,
+// since there is no build statement to create them, they should be removed from input paths.
+func filterOutPy3wrapperAndManifestFileFromInputPaths(inputPaths []string) []string {
+ filteredInputPaths := []string{}
+ for _, path := range inputPaths {
+ if strings.HasSuffix(path, py3wrapperFileName) || manifestFilePattern.MatchString(path) {
+ continue
+ }
+ filteredInputPaths = append(filteredInputPaths, path)
+ }
+ return filteredInputPaths
}
func (a *aqueryArtifactHandler) artifactIdsFromDepsetId(depsetId int) ([]int, error) {
@@ -230,7 +271,7 @@
}
buildStatement := BuildStatement{
- Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "),
+ Command: strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " "),
Depfile: depfile,
OutputPaths: outputPaths,
InputPaths: inputPaths,
@@ -245,9 +286,49 @@
out := outputPaths[0]
outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
out = proptools.ShellEscapeIncludingSpaces(out)
- in := proptools.ShellEscapeIncludingSpaces(inputPaths[0])
- buildStatement.Command = fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -rsf %[3]s %[2]s", outDir, out, in)
+ 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)
}
@@ -257,10 +338,85 @@
return buildStatements, nil
}
+// expandTemplateContent substitutes the tokens in a template.
+func expandTemplateContent(actionEntry action) string {
+ replacerString := []string{}
+ for _, pair := range actionEntry.Substitutions {
+ value := pair.Value
+ if val, ok := TemplateActionOverriddenTokens[pair.Key]; ok {
+ value = val
+ }
+ replacerString = append(replacerString, pair.Key, value)
+ }
+ replacer := strings.NewReplacer(replacerString...)
+ return replacer.Replace(actionEntry.TemplateContent)
+}
+
+func escapeCommandlineArgument(str string) string {
+ // \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
+ replacer := strings.NewReplacer(
+ `\`, `\\`,
+ `$`, `\$`,
+ "`", "\\`",
+ `"`, `\"`,
+ "\n", "\\n",
+ `'`, `'"'"'`,
+ )
+ return replacer.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" {
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 43e4155..68e50c2 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -709,7 +709,7 @@
}
expectedBuildStatements := []BuildStatement{
BuildStatement{
- Command: "/bin/bash -c touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out",
+ 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",
@@ -859,7 +859,7 @@
BuildStatement{
Command: "mkdir -p one/symlink_subdir && " +
"rm -f one/symlink_subdir/symlink && " +
- "ln -rsf one/file_subdir/file one/symlink_subdir/symlink",
+ "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
InputPaths: []string{"one/file_subdir/file"},
OutputPaths: []string{"one/symlink_subdir/symlink"},
SymlinkPaths: []string{"one/symlink_subdir/symlink"},
@@ -923,14 +923,14 @@
BuildStatement{
Command: "mkdir -p 'one/symlink subdir' && " +
"rm -f 'one/symlink subdir/symlink' && " +
- "ln -rsf 'one/file subdir/file' 'one/symlink subdir/symlink'",
+ "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
InputPaths: []string{"one/file subdir/file"},
OutputPaths: []string{"one/symlink subdir/symlink"},
SymlinkPaths: []string{"one/symlink subdir/symlink"},
Mnemonic: "SolibSymlink",
},
}
- assertBuildStatements(t, actual, expectedBuildStatements)
+ assertBuildStatements(t, expectedBuildStatements, actual)
}
func TestSymlinkMultipleInputs(t *testing.T) {
@@ -1015,6 +1015,355 @@
assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
}
+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"
+ }]
+}`
+
+ actual, err := AqueryBuildStatements([]byte(inputString))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ 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",
+ },
+ }
+ assertBuildStatements(t, expectedBuildStatements, actual)
+}
+
+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"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1 output to template expand action, got: output []`)
+}
+
+func TestPythonZipperActionSuccess(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))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ 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",
+ },
+ }
+ 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 []`)
+}
+
func assertError(t *testing.T, err error, expected string) {
t.Helper()
if err == nil {
@@ -1029,7 +1378,7 @@
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",
+ t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
len(expected), len(actual), expected, actual)
return
}
@@ -1040,7 +1389,7 @@
continue ACTUAL_LOOP
}
}
- t.Errorf("unexpected build statement %v.\n expected: %v",
+ t.Errorf("unexpected build statement %#v.\n expected: %#v",
actualStatement, expected)
return
}
diff --git a/bazel/configurability.go b/bazel/configurability.go
index e9641e7..7355ac7 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -39,6 +39,7 @@
osArchAndroidArm64 = "android_arm64"
osArchAndroidX86 = "android_x86"
osArchAndroidX86_64 = "android_x86_64"
+ osArchDarwinArm64 = "darwin_arm64"
osArchDarwinX86_64 = "darwin_x86_64"
osArchLinuxX86 = "linux_glibc_x86"
osArchLinuxX86_64 = "linux_glibc_x86_64"
@@ -96,6 +97,7 @@
osArchAndroidArm64: "//build/bazel/platforms/os_arch:android_arm64",
osArchAndroidX86: "//build/bazel/platforms/os_arch:android_x86",
osArchAndroidX86_64: "//build/bazel/platforms/os_arch:android_x86_64",
+ osArchDarwinArm64: "//build/bazel/platforms/os_arch:darwin_arm64",
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",
@@ -107,6 +109,21 @@
osArchWindowsX86_64: "//build/bazel/platforms/os_arch:windows_x86_64",
ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
}
+
+ // Map where keys are OsType names, and values are slices containing the archs
+ // that that OS supports.
+ // These definitions copied from arch.go.
+ // 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, archX86, archX86_64},
+ osLinux: {archX86, archX86_64},
+ osLinuxMusl: {archX86, archX86_64},
+ osDarwin: {archArm64, archX86_64},
+ osLinuxBionic: {archArm64, archX86_64},
+ // TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
+ osWindows: {archX86, archX86_64},
+ }
)
// basic configuration types
@@ -120,6 +137,10 @@
productVariables
)
+func osArchString(os string, arch string) string {
+ return fmt.Sprintf("%s_%s", os, arch)
+}
+
func (ct configurationType) String() string {
return map[configurationType]string{
noConfig: "no_config",
@@ -156,9 +177,9 @@
}
// SelectKey returns the Bazel select key for a given configurationType and config string.
-func (ct configurationType) SelectKey(config string) string {
- ct.validateConfig(config)
- switch ct {
+func (ca ConfigurationAxis) SelectKey(config string) string {
+ ca.validateConfig(config)
+ switch ca.configurationType {
case noConfig:
panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
case arch:
@@ -168,12 +189,13 @@
case osArch:
return platformOsArchMap[config]
case productVariables:
- if config == ConditionsDefaultConfigKey {
+ if strings.HasSuffix(config, ConditionsDefaultConfigKey) {
+ // e.g. "acme__feature1__conditions_default" or "android__board__conditions_default"
return ConditionsDefaultSelectKey
}
- return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(config))
+ return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
default:
- panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
+ panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType))
}
}
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 0bd71c6..5d00b0b 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -14,9 +14,11 @@
type CcInfo struct {
OutputFiles []string
CcObjectFiles []string
+ CcSharedLibraryFiles []string
CcStaticLibraryFiles []string
Includes []string
SystemIncludes []string
+ Headers []string
// Archives owned by the current target (not by its dependencies). These will
// be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles,
// but general cc_library will also have dynamic libraries in output files).
@@ -105,13 +107,14 @@
includes = cc_info.compilation_context.includes.to_list()
system_includes = cc_info.compilation_context.system_includes.to_list()
+headers = [f.path for f in cc_info.compilation_context.headers.to_list()]
ccObjectFiles = []
staticLibraries = []
rootStaticArchives = []
linker_inputs = cc_info.linking_context.linker_inputs.to_list()
-static_info_tag = "//build/bazel/rules:cc_library_static.bzl%CcStaticLibraryInfo"
+static_info_tag = "//build/bazel/rules/cc:cc_library_static.bzl%CcStaticLibraryInfo"
if static_info_tag in providers(target):
static_info = providers(target)[static_info_tag]
ccObjectFiles = [f.path for f in static_info.objects]
@@ -126,27 +129,43 @@
if linker_input.owner == target.label:
rootStaticArchives.append(library.static_library.path)
-rootDynamicLibraries = []
+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:
- rootDynamicLibraries += [lib.dynamic_library.path]
+ path = lib.dynamic_library.path
+ rootSharedLibraries += [path]
+ sharedLibraries.append(path)
+else:
+ for linker_input in linker_inputs:
+ for library in linker_input.libraries:
+ if library.dynamic_library:
+ path = library.dynamic_library.path
+ sharedLibraries.append(path)
+ if linker_input.owner == target.label:
+ rootSharedLibraries.append(path)
toc_file = ""
-toc_file_tag = "//build/bazel/rules:generate_toc.bzl%CcTocInfo"
+toc_file_tag = "//build/bazel/rules/cc:generate_toc.bzl%CcTocInfo"
if toc_file_tag in providers(target):
toc_file = providers(target)[toc_file_tag].toc.path
+else:
+ # NOTE: It's OK if there's no ToC, as Soong just uses it for optimization
+ pass
returns = [
outputFiles,
- staticLibraries,
ccObjectFiles,
+ sharedLibraries,
+ staticLibraries,
includes,
system_includes,
+ headers,
rootStaticArchives,
- rootDynamicLibraries,
+ rootSharedLibraries,
[toc_file]
]
@@ -157,30 +176,39 @@
// 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) {
- var outputFiles []string
- var ccObjects []string
-
+ const expectedLen = 10
splitString := strings.Split(rawString, "|")
- if expectedLen := 8; len(splitString) != expectedLen {
+ if len(splitString) != expectedLen {
return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
}
outputFilesString := splitString[0]
- ccStaticLibrariesString := splitString[1]
- ccObjectsString := splitString[2]
- outputFiles = splitOrEmpty(outputFilesString, ", ")
+ 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
+
+ outputFiles := splitOrEmpty(outputFilesString, ", ")
+ ccObjects := splitOrEmpty(ccObjectsString, ", ")
+ ccSharedLibraries := splitOrEmpty(ccSharedLibrariesString, ", ")
ccStaticLibraries := splitOrEmpty(ccStaticLibrariesString, ", ")
- ccObjects = splitOrEmpty(ccObjectsString, ", ")
- includes := splitOrEmpty(splitString[3], ", ")
- systemIncludes := splitOrEmpty(splitString[4], ", ")
- rootStaticArchives := splitOrEmpty(splitString[5], ", ")
- rootDynamicLibraries := splitOrEmpty(splitString[6], ", ")
- tocFile := splitString[7] // NOTE: Will be the empty string if there wasn't
+ 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,
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 34d0832..606e285 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -63,6 +63,8 @@
}
func TestGetCcInfoParseResults(t *testing.T) {
+ const expectedSplits = 10
+ noResult := strings.Repeat("|", expectedSplits-1)
testCases := []struct {
description string
input string
@@ -71,13 +73,15 @@
}{
{
description: "no result",
- input: "|||||||",
+ input: noResult,
expectedOutput: CcInfo{
OutputFiles: []string{},
CcObjectFiles: []string{},
+ CcSharedLibraryFiles: []string{},
CcStaticLibraryFiles: []string{},
Includes: []string{},
SystemIncludes: []string{},
+ Headers: []string{},
RootStaticArchives: []string{},
RootDynamicLibraries: []string{},
TocFile: "",
@@ -85,27 +89,56 @@
},
{
description: "only output",
- input: "test|||||||",
+ 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: "all items set",
- input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1|lib.so.toc",
+ 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",
expectedOutput: 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",
@@ -115,22 +148,22 @@
description: "too few result splits",
input: "|",
expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, []string{"", ""}),
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, []string{"", ""}),
},
{
description: "too many result splits",
- input: strings.Repeat("|", 8),
+ input: strings.Repeat("|", expectedSplits+1), // 2 too many
expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, make([]string, 9)),
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)),
},
}
for _, tc := range testCases {
actualOutput, err := GetCcInfo.ParseResult(tc.input)
if (err == nil && tc.expectedErrorMessage != "") ||
(err != nil && err.Error() != tc.expectedErrorMessage) {
- t.Errorf("%q: expected Error %s, got %s", tc.description, tc.expectedErrorMessage, err)
+ 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: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+ t.Errorf("%q:\n expected %#v\n!= actual %#v", tc.description, tc.expectedOutput, actualOutput)
}
}
}
diff --git a/bazel/properties.go b/bazel/properties.go
index ee32e73..1300a53 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -107,6 +107,14 @@
return dirs
}
+// Add inserts the label Label at the end of the LabelList.
+func (ll *LabelList) Add(label *Label) {
+ if label == nil {
+ return
+ }
+ ll.Includes = append(ll.Includes, *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 {
@@ -164,48 +172,36 @@
// Subtract needle from haystack
func SubtractStrings(haystack []string, needle []string) []string {
// This is really a set
- remainder := make(map[string]bool)
-
- for _, s := range haystack {
- remainder[s] = true
- }
+ needleMap := make(map[string]bool)
for _, s := range needle {
- delete(remainder, s)
+ needleMap[s] = true
}
var strings []string
- for s, _ := range remainder {
- strings = append(strings, s)
+ for _, s := range haystack {
+ if exclude := needleMap[s]; !exclude {
+ strings = append(strings, s)
+ }
}
- sort.SliceStable(strings, func(i, j int) bool {
- return strings[i] < strings[j]
- })
-
return strings
}
// Subtract needle from haystack
func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
// This is really a set
- remainder := make(map[Label]bool)
-
- for _, label := range haystack {
- remainder[label] = true
- }
- for _, label := range needle {
- delete(remainder, label)
+ needleMap := make(map[Label]bool)
+ for _, s := range needle {
+ needleMap[s] = true
}
var labels []Label
- for label, _ := range remainder {
- labels = append(labels, label)
+ for _, label := range haystack {
+ if exclude := needleMap[label]; !exclude {
+ labels = append(labels, label)
+ }
}
- sort.SliceStable(labels, func(i, j int) bool {
- return labels[i].Label < labels[j].Label
- })
-
return labels
}
@@ -248,9 +244,69 @@
ConfigurableValues configurableLabels
}
+func (la *LabelAttribute) axisTypes() map[configurationType]bool {
+ types := map[configurationType]bool{}
+ for k := range la.ConfigurableValues {
+ if len(la.ConfigurableValues[k]) > 0 {
+ types[k.configurationType] = true
+ }
+ }
+ return types
+}
+
+// Collapse reduces the configurable axes of the label attribute to a single axis.
+// This is necessary for final writing to bp2build, as a configurable label
+// attribute can only be comprised by a single select.
+func (la *LabelAttribute) Collapse() error {
+ axisTypes := la.axisTypes()
+ _, containsOs := axisTypes[os]
+ _, containsArch := axisTypes[arch]
+ _, containsOsArch := axisTypes[osArch]
+ _, 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 (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 := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
+ // Do nothing, as the arch_os is explicitly defined already.
+ } else {
+ archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
+ osVal := la.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.
+ la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
+ } else if osVal != nil && archVal == nil {
+ la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
+ } else if osVal == nil && archVal != nil {
+ la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
+ }
+ }
+ }
+ }
+ // All os_arch values are now set. Clear os and arch axes.
+ delete(la.ConfigurableValues, ArchConfigurationAxis)
+ delete(la.ConfigurableValues, OsConfigurationAxis)
+ }
+ return nil
+}
+
// HasConfigurableValues returns whether there are configurable values set for this label.
func (la LabelAttribute) HasConfigurableValues() bool {
- return len(la.ConfigurableValues) > 0
+ for _, selectValues := range la.ConfigurableValues {
+ if len(selectValues) > 0 {
+ return true
+ }
+ }
+ return false
}
// SetValue sets the base, non-configured value for the Label
@@ -275,13 +331,13 @@
}
// SelectValue gets a value for a bazel select for the given axis and config.
-func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
+func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
axis.validateConfig(config)
switch axis.configurationType {
case noConfig:
- return *la.Value
+ return la.Value
case arch, os, osArch, productVariables:
- return *la.ConfigurableValues[axis][config]
+ return la.ConfigurableValues[axis][config]
default:
panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
}
@@ -328,7 +384,12 @@
// HasConfigurableValues returns whether there are configurable values for this attribute.
func (ba BoolAttribute) HasConfigurableValues() bool {
- return len(ba.ConfigurableValues) > 0
+ for _, cfgToBools := range ba.ConfigurableValues {
+ if len(cfgToBools) > 0 {
+ return true
+ }
+ }
+ return false
}
// SetSelectValue sets value for the given axis/config.
@@ -347,6 +408,106 @@
}
}
+// ToLabelListAttribute creates and returns a LabelListAttribute from this
+// bool attribute, where each bool in this attribute corresponds to a
+// label list value in the resultant attribute.
+func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
+ getLabelList := func(boolPtr *bool) LabelList {
+ if boolPtr == nil {
+ return LabelList{nil, nil}
+ } else if *boolPtr {
+ return trueVal
+ } else {
+ return falseVal
+ }
+ }
+
+ mainVal := getLabelList(ba.Value)
+ if !ba.HasConfigurableValues() {
+ return MakeLabelListAttribute(mainVal), nil
+ }
+
+ result := LabelListAttribute{}
+ 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 := getLabelList(&boolPtr)
+ if !val.Equals(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.
+func (ba *BoolAttribute) Collapse() error {
+ axisTypes := ba.axisTypes()
+ _, containsOs := axisTypes[os]
+ _, containsArch := axisTypes[arch]
+ _, containsOsArch := axisTypes[osArch]
+ _, containsProductVariables := axisTypes[productVariables]
+ if containsProductVariables {
+ if containsOs || containsArch || containsOsArch {
+ return fmt.Errorf("boolean 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 := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
+ // Do nothing, as the arch_os is explicitly defined already.
+ } else {
+ archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
+ osVal := ba.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.
+ ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
+ } else if osVal != nil && archVal == nil {
+ ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
+ } else if osVal == nil && archVal != nil {
+ ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
+ }
+ }
+ }
+ }
+ // All os_arch values are now set. Clear os and arch axes.
+ delete(ba.ConfigurableValues, ArchConfigurationAxis)
+ delete(ba.ConfigurableValues, OsConfigurationAxis)
+ // Verify post-condition; this should never fail, provided no additional
+ // axes are introduced.
+ if len(ba.ConfigurableValues) > 1 {
+ panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
+ }
+ }
+ return nil
+}
+
+func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
+ types := map[configurationType]bool{}
+ for k := range ba.ConfigurableValues {
+ if len(ba.ConfigurableValues[k]) > 0 {
+ types[k.configurationType] = true
+ }
+ }
+ return types
+}
+
// SelectValue gets the value for the given axis/config.
func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
axis.validateConfig(config)
@@ -378,9 +539,23 @@
// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
type labelListSelectValues map[string]LabelList
-func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
+func (ll labelListSelectValues) addSelects(label labelSelectValues) {
+ for k, v := range label {
+ if label == nil {
+ continue
+ }
+ l := ll[k]
+ (&l).Add(v)
+ ll[k] = l
+ }
+}
+
+func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
for k, v := range other {
l := ll[k]
+ if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
+ l.Includes = []Label{}
+ }
(&l).Append(v)
ll[k] = l
}
@@ -412,6 +587,12 @@
// This mode facilitates use of attribute defaults: an empty list should
// override the default.
ForceSpecifyEmptyList bool
+
+ // If true, signal the intent to the code generator to emit all select keys,
+ // even if the Includes list for that key is empty. This mode facilitates
+ // specific select statements where an empty list for a non-default select
+ // key has a meaning.
+ EmitEmptyList bool
}
type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
@@ -430,17 +611,22 @@
cll[axis][config] = list
}
-func (cll configurableLabelLists) Append(other configurableLabelLists) {
+func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
for axis, otherSelects := range other {
selects := cll[axis]
if selects == nil {
selects = make(labelListSelectValues, len(otherSelects))
}
- selects.appendSelects(otherSelects)
+ selects.appendSelects(otherSelects, forceSpecifyEmptyList)
cll[axis] = selects
}
}
+func (lla *LabelListAttribute) Clone() *LabelListAttribute {
+ result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
+ return result.Append(*lla)
+}
+
// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
func MakeLabelListAttribute(value LabelList) LabelListAttribute {
return LabelListAttribute{
@@ -494,21 +680,47 @@
}
// Append all values, including os and arch specific ones, from another
-// LabelListAttribute to this LabelListAttribute.
-func (lla *LabelListAttribute) Append(other LabelListAttribute) {
- if lla.ForceSpecifyEmptyList && !other.Value.IsNil() {
+// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
+func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
+ forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
+ if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
lla.Value.Includes = []Label{}
}
lla.Value.Append(other.Value)
if lla.ConfigurableValues == nil {
lla.ConfigurableValues = make(configurableLabelLists)
}
- lla.ConfigurableValues.Append(other.ConfigurableValues)
+ lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
+ return lla
+}
+
+// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
+// LabelList within the LabelListAttribute
+func (lla *LabelListAttribute) Add(label *LabelAttribute) {
+ if label == nil {
+ return
+ }
+
+ lla.Value.Add(label.Value)
+ if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
+ lla.ConfigurableValues = make(configurableLabelLists)
+ }
+ for axis, _ := range label.ConfigurableValues {
+ if _, exists := lla.ConfigurableValues[axis]; !exists {
+ lla.ConfigurableValues[axis] = make(labelListSelectValues)
+ }
+ lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
+ }
}
// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
func (lla LabelListAttribute) HasConfigurableValues() bool {
- return len(lla.ConfigurableValues) > 0
+ for _, selectValues := range lla.ConfigurableValues {
+ if len(selectValues) > 0 {
+ return true
+ }
+ }
+ return false
}
// IsEmpty returns true if the attribute has no values under any configuration.
@@ -524,6 +736,24 @@
return true
}
+// IsNil returns true if the attribute has not been set for any configuration.
+func (lla LabelListAttribute) IsNil() bool {
+ if lla.Value.Includes != nil {
+ return false
+ }
+ return !lla.HasConfigurableValues()
+}
+
+// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
+// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
+// be removed, e.g. if they could cause duplicate element failures.
+func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
+ val := lla.SelectValue(axis, config)
+ newList := SubtractBazelLabelList(val, labelList)
+ newList.Excludes = append(newList.Excludes, labelList.Includes...)
+ lla.SetSelectValue(axis, config, newList)
+}
+
// 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() {
@@ -546,9 +776,13 @@
lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
}
- // Now that the Value list is finalized for this axis, compare it with the original
- // list, and put the difference into the default condition for the axis.
- lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = SubtractBazelLabelList(baseLabels, lla.Value)
+ // Now that the Value list is finalized for this axis, compare it with
+ // the original list, and union the difference with the default
+ // condition for the axis.
+ difference := SubtractBazelLabelList(baseLabels, lla.Value)
+ existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
+ existingDefaults.Append(difference)
+ lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
// if everything ends up without includes, just delete the axis
if !lla.ConfigurableValues[axis].HasConfigurableValues() {
@@ -568,7 +802,7 @@
// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
// label and whether it was changed.
-type LabelMapper func(OtherModuleContext, string) (string, bool)
+type LabelMapper func(OtherModuleContext, Label) (string, bool)
// LabelPartition contains descriptions of a partition for labels
type LabelPartition struct {
@@ -590,7 +824,7 @@
// not.
func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
if lf.LabelMapper != nil {
- if newLabel, changed := lf.LabelMapper(ctx, label.Label); changed {
+ if newLabel, changed := lf.LabelMapper(ctx, label); changed {
return &Label{newLabel, label.OriginalModuleName}
}
}
@@ -754,17 +988,28 @@
// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
func (sla StringListAttribute) HasConfigurableValues() bool {
- return len(sla.ConfigurableValues) > 0
+ for _, selectValues := range sla.ConfigurableValues {
+ if len(selectValues) > 0 {
+ return true
+ }
+ }
+ return false
}
// Append appends all values, including os and arch specific ones, from another
// StringListAttribute to this StringListAttribute
-func (sla *StringListAttribute) Append(other StringListAttribute) {
+func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
sla.Value = append(sla.Value, other.Value...)
if sla.ConfigurableValues == nil {
sla.ConfigurableValues = make(configurableStringLists)
}
sla.ConfigurableValues.Append(other.ConfigurableValues)
+ return sla
+}
+
+func (sla *StringListAttribute) Clone() *StringListAttribute {
+ result := &StringListAttribute{}
+ return result.Append(*sla)
}
// SetSelectValue set a value for a bazel select for the given axis, config and value.
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index f53fdc1..c7f9776 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -236,8 +236,9 @@
),
ConfigurableValues: configurableLabelLists{
ArchConfigurationAxis: labelListSelectValues{
- "arm": makeLabelList([]string{}, []string{"arm_exclude"}),
- "x86": makeLabelList([]string{"x86_include"}, []string{}),
+ "arm": makeLabelList([]string{}, []string{"arm_exclude"}),
+ "x86": makeLabelList([]string{"x86_include"}, []string{}),
+ ConditionsDefaultConfigKey: makeLabelList([]string{"default_include"}, []string{}),
},
OsConfigurationAxis: labelListSelectValues{
"android": makeLabelList([]string{}, []string{"android_exclude"}),
@@ -246,7 +247,13 @@
OsArchConfigurationAxis: labelListSelectValues{
"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
},
- ProductVariableConfigurationAxis("a"): labelListSelectValues{
+ ProductVariableConfigurationAxis("product_with_defaults"): 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{}),
+ },
+ ProductVariableConfigurationAxis("product_only_with_excludes"): labelListSelectValues{
"a": makeLabelList([]string{}, []string{"not_in_value"}),
},
},
@@ -254,25 +261,31 @@
attr.ResolveExcludes()
- expectedBaseIncludes := []Label{Label{Label: "all_include"}}
+ expectedBaseIncludes := []Label{{Label: "all_include"}}
if !reflect.DeepEqual(expectedBaseIncludes, attr.Value.Includes) {
t.Errorf("Expected Value includes %q, got %q", attr.Value.Includes, expectedBaseIncludes)
}
var nilLabels []Label
expectedConfiguredIncludes := map[ConfigurationAxis]map[string][]Label{
- ArchConfigurationAxis: map[string][]Label{
- "arm": nilLabels,
- "x86": makeLabels("arm_exclude", "x86_include"),
- "conditions_default": makeLabels("arm_exclude"),
+ ArchConfigurationAxis: {
+ "arm": nilLabels,
+ "x86": makeLabels("arm_exclude", "x86_include"),
+ ConditionsDefaultConfigKey: makeLabels("arm_exclude", "default_include"),
},
- OsConfigurationAxis: map[string][]Label{
- "android": nilLabels,
- "linux": makeLabels("android_exclude", "linux_include"),
- "conditions_default": makeLabels("android_exclude"),
+ OsConfigurationAxis: {
+ "android": nilLabels,
+ "linux": makeLabels("android_exclude", "linux_include"),
+ ConditionsDefaultConfigKey: makeLabels("android_exclude"),
},
- OsArchConfigurationAxis: map[string][]Label{
- "linux_x86": makeLabels("linux_x86_include"),
- "conditions_default": nilLabels,
+ OsArchConfigurationAxis: {
+ "linux_x86": makeLabels("linux_x86_include"),
+ ConditionsDefaultConfigKey: nilLabels,
+ },
+ ProductVariableConfigurationAxis("product_with_defaults"): {
+ "a": nilLabels,
+ "b": makeLabels("b_val"),
+ "c": makeLabels("c_val"),
+ ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
},
}
for _, axis := range attr.SortedConfigurationAxes() {
@@ -288,7 +301,7 @@
for config, value := range gotForAxis {
if expected, ok := expectedForAxis[config]; ok {
if !reflect.DeepEqual(expected, value.Includes) {
- t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value.Includes)
+ t.Errorf("For %s,\nexpected: %#v\ngot %#v", axis, expected, value.Includes)
}
} else {
t.Errorf("Got unexpected config %q for %s", config, axis)
@@ -300,16 +313,16 @@
// labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of
// typ
func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {
- return func(omc OtherModuleContext, label string) (string, bool) {
- m, ok := omc.ModuleFromName(label)
+ return func(omc OtherModuleContext, label Label) (string, bool) {
+ m, ok := omc.ModuleFromName(label.Label)
if !ok {
- return label, false
+ return label.Label, false
}
mTyp := omc.OtherModuleType(m)
if typ == mTyp {
- return label + suffix, true
+ return label.Label + suffix, true
}
- return label, false
+ return label.Label, false
}
}
diff --git a/bootstrap.bash b/bootstrap.bash
index 4db8539..726692a 100755
--- a/bootstrap.bash
+++ b/bootstrap.bash
@@ -15,9 +15,9 @@
# limitations under the License.
echo '==== ERROR: bootstrap.bash & ./soong are obsolete ====' >&2
-echo 'Use `m --skip-make` with a standalone OUT_DIR instead.' >&2
+echo 'Use `m --soong-only` with a standalone OUT_DIR instead.' >&2
echo 'Without envsetup.sh, use:' >&2
-echo ' build/soong/soong_ui.bash --make-mode --skip-make' >&2
+echo ' build/soong/soong_ui.bash --make-mode --soong-only' >&2
echo '======================================================' >&2
exit 1
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 1250e92..b904c35 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -18,6 +18,8 @@
],
deps: [
"soong-android",
+ "soong-android-soongconfig",
+ "soong-shared",
"soong-apex",
"soong-bazel",
"soong-cc",
@@ -26,18 +28,21 @@
"soong-genrule",
"soong-python",
"soong-sh",
+ "soong-starlark-format",
+ "soong-ui-metrics",
],
testSrcs: [
"android_app_certificate_conversion_test.go",
+ "android_app_conversion_test.go",
"apex_conversion_test.go",
"apex_key_conversion_test.go",
"build_conversion_test.go",
"bzl_conversion_test.go",
- "cc_genrule_conversion_test.go",
+ "cc_binary_conversion_test.go",
"cc_library_conversion_test.go",
"cc_library_headers_conversion_test.go",
- "cc_library_static_conversion_test.go",
"cc_library_shared_conversion_test.go",
+ "cc_library_static_conversion_test.go",
"cc_object_conversion_test.go",
"conversion_test.go",
"filegroup_conversion_test.go",
@@ -47,6 +52,7 @@
"python_binary_conversion_test.go",
"python_library_conversion_test.go",
"sh_conversion_test.go",
+ "soong_config_module_type_conversion_test.go",
"testing.go",
],
pluginFor: [
diff --git a/bp2build/android_app_certificate_conversion_test.go b/bp2build/android_app_certificate_conversion_test.go
index 022c687..035a352 100644
--- a/bp2build/android_app_certificate_conversion_test.go
+++ b/bp2build/android_app_certificate_conversion_test.go
@@ -31,19 +31,19 @@
func TestAndroidAppCertificateSimple(t *testing.T) {
runAndroidAppCertificateTestCase(t, bp2buildTestCase{
- description: "Android app certificate - simple example",
- moduleTypeUnderTest: "android_app_certificate",
- moduleTypeUnderTestFactory: java.AndroidAppCertificateFactory,
- moduleTypeUnderTestBp2BuildMutator: java.AndroidAppCertificateBp2Build,
- filesystem: map[string]string{},
+ 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{`android_app_certificate(
- name = "com.android.apogee.cert",
- certificate = "chamber_of_secrets_dir",
-)`}})
+ expectedBazelTargets: []string{
+ makeBazelTarget("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
new file mode 100644
index 0000000..28de06c
--- /dev/null
+++ b/bp2build/android_app_conversion_test.go
@@ -0,0 +1,92 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/java"
+
+ "testing"
+)
+
+func runAndroidAppTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ runBp2BuildTestCase(t, registerAndroidAppModuleTypes, tc)
+}
+
+func registerAndroidAppModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestMinimalAndroidApp(t *testing.T) {
+ 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: `
+android_app {
+ name: "TestApp",
+ srcs: ["app.java"],
+ sdk_version: "current",
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("android_binary", "TestApp", attrNameToString{
+ "srcs": `["app.java"]`,
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ }),
+ }})
+}
+
+func TestAndroidAppAllSupportedFields(t *testing.T) {
+ 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") + `
+android_app {
+ name: "TestApp",
+ srcs: ["app.java"],
+ sdk_version: "current",
+ package_name: "com.google",
+ resource_dirs: ["resa", "resb"],
+ manifest: "manifest/AndroidManifest.xml",
+ static_libs: ["static_lib_dep"]
+}
+`,
+ 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"]`,
+ }),
+ }})
+}
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index 456f18a..9057189 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -19,6 +19,7 @@
"android/soong/apex"
"android/soong/cc"
"android/soong/java"
+ "android/soong/sh"
"testing"
)
@@ -32,6 +33,8 @@
// CC module types needed as they can be APEX dependencies
cc.RegisterCCBuildComponents(ctx)
+ ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
+ ctx.RegisterModuleType("cc_binary", cc.BinaryFactory)
ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
@@ -40,72 +43,75 @@
func TestApexBundleSimple(t *testing.T) {
runApexTestCase(t, bp2buildTestCase{
- description: "apex - simple example",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
- filesystem: map[string]string{},
+ description: "apex - example with all props",
+ moduleTypeUnderTest: "apex",
+ moduleTypeUnderTestFactory: apex.BundleFactory,
+ filesystem: map[string]string{},
blueprint: `
apex_key {
- name: "com.android.apogee.key",
- public_key: "com.android.apogee.avbpubkey",
- private_key: "com.android.apogee.pem",
+ 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",
+ name: "com.android.apogee.certificate",
+ certificate: "com.android.apogee",
bazel_module: { bp2build_available: false },
}
cc_library {
- name: "native_shared_lib_2",
+ name: "native_shared_lib_1",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "native_shared_lib_2",
bazel_module: { bp2build_available: false },
}
// TODO(b/194878861): Add bp2build support for prebuilt_etc
cc_library {
- name: "pretend_prebuilt_1",
- bazel_module: { bp2build_available: false },
+ name: "pretend_prebuilt_1",
+ bazel_module: { bp2build_available: false },
}
// TODO(b/194878861): Add bp2build support for prebuilt_etc
cc_library {
- name: "pretend_prebuilt_2",
- bazel_module: { bp2build_available: false },
+ name: "pretend_prebuilt_2",
+ bazel_module: { bp2build_available: false },
}
filegroup {
name: "com.android.apogee-file_contexts",
- srcs: [
- "com.android.apogee-file_contexts",
- ],
- bazel_module: { bp2build_available: false },
+ 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",
+ 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: [
- "binary_1",
- "binary_2",
+ "cc_binary_1",
+ "sh_binary_2",
],
prebuilts: [
"pretend_prebuilt_1",
@@ -113,57 +119,188 @@
],
}
`,
- expectedBazelTargets: []string{`apex(
- name = "com.android.apogee",
- android_manifest = "ApogeeAndroidManifest.xml",
- binaries = [
- "binary_1",
- "binary_2",
- ],
- certificate = ":com.android.apogee.certificate",
- file_contexts = ":com.android.apogee-file_contexts",
- installable = False,
- key = ":com.android.apogee.key",
- manifest = "apogee_manifest.json",
- min_sdk_version = "29",
- native_shared_libs = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+ "android_manifest": `"ApogeeAndroidManifest.xml"`,
+ "binaries": `[
+ ":cc_binary_1",
+ ":sh_binary_2",
+ ]`,
+ "certificate": `":com.android.apogee.certificate"`,
+ "file_contexts": `":com.android.apogee-file_contexts"`,
+ "installable": "False",
+ "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",
- ],
- prebuilts = [
+ ]`,
+ "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": `[
":pretend_prebuilt_1",
":pretend_prebuilt_2",
- ],
- updatable = False,
-)`}})
+ ]`,
+ "updatable": "False",
+ "compressible": "False",
+ }),
+ }})
+}
+
+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{
+ "native_shared_libs_32": `[
+ ":native_shared_lib_1",
+ ":native_shared_lib_3",
+ ] + select({
+ "//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
+ "//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
+ "//conditions:default": [],
+ })`,
+ "native_shared_libs_64": `select({
+ "//build/bazel/platforms/arch:arm64": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_4",
+ ":native_shared_lib_2",
+ ],
+ "//build/bazel/platforms/arch:x86_64": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_4",
+ ":native_shared_lib_2",
+ ],
+ "//conditions:default": [],
+ })`,
+ }),
+ }})
+}
+
+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({
+ "//build/bazel/platforms/arch:arm": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_3",
+ ":native_shared_lib_2",
+ ],
+ "//build/bazel/platforms/arch:x86": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_3",
+ ":native_shared_lib_2",
+ ],
+ "//conditions:default": [],
+ })`,
+ "native_shared_libs_64": `select({
+ "//build/bazel/platforms/arch:arm64": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_4",
+ ":native_shared_lib_2",
+ ],
+ "//build/bazel/platforms/arch:x86_64": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_4",
+ ":native_shared_lib_2",
+ ],
+ "//conditions:default": [],
+ })`,
+ }),
+ }})
+}
+
+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{
+ "native_shared_libs_32": `[
+ ":native_shared_lib_1",
+ ":native_shared_lib_3",
+ ] + select({
+ "//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
+ "//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
+ "//conditions:default": [],
+ })`,
+ }),
+ }})
+}
+
+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{
+ "native_shared_libs_64": `select({
+ "//build/bazel/platforms/arch:arm64": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_4",
+ ":native_shared_lib_2",
+ ],
+ "//build/bazel/platforms/arch:x86_64": [
+ ":native_shared_lib_1",
+ ":native_shared_lib_4",
+ ":native_shared_lib_2",
+ ],
+ "//conditions:default": [],
+ })`,
+ }),
+ }})
}
func TestApexBundleDefaultPropertyValues(t *testing.T) {
runApexTestCase(t, bp2buildTestCase{
- description: "apex - default property values",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
- filesystem: map[string]string{},
+ description: "apex - default property values",
+ moduleTypeUnderTest: "apex",
+ moduleTypeUnderTestFactory: apex.BundleFactory,
+ filesystem: map[string]string{},
blueprint: `
apex {
name: "com.android.apogee",
manifest: "apogee_manifest.json",
}
`,
- expectedBazelTargets: []string{`apex(
- name = "com.android.apogee",
- manifest = "apogee_manifest.json",
-)`}})
+ expectedBazelTargets: []string{makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+ "manifest": `"apogee_manifest.json"`,
+ }),
+ }})
}
func TestApexBundleHasBazelModuleProps(t *testing.T) {
runApexTestCase(t, bp2buildTestCase{
- description: "apex - has bazel module props",
- moduleTypeUnderTest: "apex",
- moduleTypeUnderTestFactory: apex.BundleFactory,
- moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
- filesystem: map[string]string{},
+ description: "apex - has bazel module props",
+ moduleTypeUnderTest: "apex",
+ moduleTypeUnderTestFactory: apex.BundleFactory,
+ filesystem: map[string]string{},
blueprint: `
apex {
name: "apogee",
@@ -171,8 +308,58 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{`apex(
- name = "apogee",
- manifest = "manifest.json",
-)`}})
+ expectedBazelTargets: []string{makeBazelTarget("apex", "apogee", attrNameToString{
+ "manifest": `"manifest.json"`,
+ }),
+ }})
+}
+
+func createMultilibBlueprint(compile_multilib string) string {
+ return `
+cc_library {
+ name: "native_shared_lib_1",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "native_shared_lib_2",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "native_shared_lib_3",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "native_shared_lib_4",
+ bazel_module: { bp2build_available: false },
+}
+
+apex {
+ name: "com.android.apogee",
+ compile_multilib: "` + compile_multilib + `",
+ multilib: {
+ both: {
+ native_shared_libs: [
+ "native_shared_lib_1",
+ ],
+ },
+ first: {
+ native_shared_libs: [
+ "native_shared_lib_2",
+ ],
+ },
+ lib32: {
+ native_shared_libs: [
+ "native_shared_lib_3",
+ ],
+ },
+ lib64: {
+ native_shared_libs: [
+ "native_shared_lib_4",
+ ],
+ },
+ },
+}`
}
diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
index 8e1aa09..1d949901 100644
--- a/bp2build/apex_key_conversion_test.go
+++ b/bp2build/apex_key_conversion_test.go
@@ -31,11 +31,10 @@
func TestApexKeySimple(t *testing.T) {
runApexKeyTestCase(t, bp2buildTestCase{
- description: "apex key - simple example",
- moduleTypeUnderTest: "apex_key",
- moduleTypeUnderTestFactory: apex.ApexKeyFactory,
- moduleTypeUnderTestBp2BuildMutator: apex.ApexKeyBp2Build,
- filesystem: map[string]string{},
+ description: "apex key - simple example",
+ moduleTypeUnderTest: "apex_key",
+ moduleTypeUnderTestFactory: apex.ApexKeyFactory,
+ filesystem: map[string]string{},
blueprint: `
apex_key {
name: "com.android.apogee.key",
@@ -43,9 +42,9 @@
private_key: "com.android.apogee.pem",
}
`,
- expectedBazelTargets: []string{`apex_key(
- name = "com.android.apogee.key",
- private_key = "com.android.apogee.pem",
- public_key = "com.android.apogee.avbpubkey",
-)`}})
+ expectedBazelTargets: []string{makeBazelTarget("apex_key", "com.android.apogee.key", attrNameToString{
+ "private_key": `"com.android.apogee.pem"`,
+ "public_key": `"com.android.apogee.avbpubkey"`,
+ }),
+ }})
}
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 45a3cb6..b0c3899 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -15,11 +15,12 @@
package bp2build
import (
- "android/soong/android"
- "android/soong/bazel"
"fmt"
"os"
"strings"
+
+ "android/soong/android"
+ "android/soong/bazel"
)
// Codegen is the backend of bp2build. The code generator is responsible for
@@ -43,7 +44,7 @@
writeFiles(ctx, bp2buildDir, bp2buildFiles)
soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
- writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(res.metrics))
+ writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(ctx.Config(), res.metrics))
return res.metrics
}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index c05a62b..1d3b105 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -27,6 +27,7 @@
"android/soong/android"
"android/soong/bazel"
+ "android/soong/starlark_fmt"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -239,9 +240,7 @@
func propsToAttributes(props map[string]string) string {
var attributes string
for _, propName := range android.SortedStringKeys(props) {
- if shouldGenerateAttribute(propName) {
- attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
- }
+ attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
}
return attributes
}
@@ -261,7 +260,9 @@
// Simple metrics tracking for bp2build
metrics := CodegenMetrics{
- ruleClassCount: make(map[string]int),
+ ruleClassCount: make(map[string]uint64),
+ convertedModuleTypeCount: make(map[string]uint64),
+ totalModuleTypeCount: make(map[string]uint64),
}
dirs := make(map[string]bool)
@@ -271,6 +272,7 @@
bpCtx := ctx.Context()
bpCtx.VisitAllModules(func(m blueprint.Module) {
dir := bpCtx.ModuleDir(m)
+ moduleType := bpCtx.ModuleType(m)
dirs[dir] = true
var targets []BazelTarget
@@ -294,7 +296,7 @@
// targets in the same BUILD file (or package).
// Log the module.
- metrics.AddConvertedModule(m.Name(), Handcrafted)
+ metrics.AddConvertedModule(m, moduleType, Handcrafted)
pathToBuildFile := getBazelPackagePath(b)
if _, exists := buildFileToAppend[pathToBuildFile]; exists {
@@ -314,7 +316,7 @@
// Handle modules converted to generated targets.
// Log the module.
- metrics.AddConvertedModule(m.Name(), Generated)
+ metrics.AddConvertedModule(aModule, moduleType, Generated)
// Handle modules with unconverted deps. By default, emit a warning.
if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 {
@@ -326,6 +328,15 @@
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 {
+ metrics.moduleWithMissingDepsMsgs = append(metrics.moduleWithMissingDepsMsgs, msg)
+ } else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+ errs = append(errs, fmt.Errorf(msg))
+ return
+ }
+ }
targets = generateBazelTargets(bpCtx, aModule)
for _, t := range targets {
// A module can potentially generate more than 1 Bazel
@@ -333,7 +344,7 @@
metrics.IncrementRuleClassCount(t.ruleClass)
}
} else {
- metrics.IncrementUnconvertedCount()
+ metrics.AddUnconvertedModule(moduleType)
return
}
case QueryView:
@@ -422,7 +433,8 @@
attrs := m.BazelAttributes()
props := extractModuleProperties(attrs, true)
- delete(props.Attrs, "bp2build_available")
+ // name is handled in a special manner
+ delete(props.Attrs, "name")
// Return the Bazel target with rule class and attributes, ready to be
// code-generated.
@@ -457,6 +469,10 @@
depLabels[qualifiedTargetLabel(ctx, depModule)] = true
})
}
+
+ for p, _ := range ignoredPropNames {
+ delete(props.Attrs, p)
+ }
attributes := propsToAttributes(props.Attrs)
depLabelList := "[\n"
@@ -529,8 +545,8 @@
// prettyPrint a property value into the equivalent Starlark representation
// recursively.
-func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
- if isZero(propertyValue) {
+func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) (string, error) {
+ if !emitZeroValues && isZero(propertyValue) {
// A property value being set or unset actually matters -- Soong does set default
// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
@@ -544,48 +560,27 @@
return "", nil
}
- var ret string
switch propertyValue.Kind() {
case reflect.String:
- ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+ return fmt.Sprintf("\"%v\"", escapeString(propertyValue.String())), nil
case reflect.Bool:
- ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+ return starlark_fmt.PrintBool(propertyValue.Bool()), nil
case reflect.Int, reflect.Uint, reflect.Int64:
- ret = fmt.Sprintf("%v", propertyValue.Interface())
+ return fmt.Sprintf("%v", propertyValue.Interface()), nil
case reflect.Ptr:
- return prettyPrint(propertyValue.Elem(), indent)
+ return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
case reflect.Slice:
- if propertyValue.Len() == 0 {
- return "[]", nil
- }
-
- if propertyValue.Len() == 1 {
- // Single-line list for list with only 1 element
- ret += "["
- indexedValue, err := prettyPrint(propertyValue.Index(0), indent)
+ elements := make([]string, 0, propertyValue.Len())
+ for i := 0; i < propertyValue.Len(); i++ {
+ val, err := prettyPrint(propertyValue.Index(i), indent, emitZeroValues)
if err != nil {
return "", err
}
- ret += indexedValue
- ret += "]"
- } else {
- // otherwise, use a multiline list.
- ret += "[\n"
- for i := 0; i < propertyValue.Len(); i++ {
- indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
- if err != nil {
- return "", err
- }
-
- if indexedValue != "" {
- ret += makeIndent(indent + 1)
- ret += indexedValue
- ret += ",\n"
- }
+ if val != "" {
+ elements = append(elements, val)
}
- ret += makeIndent(indent)
- ret += "]"
}
+ return starlark_fmt.PrintList(elements, indent, "%s"), nil
case reflect.Struct:
// Special cases where the bp2build sends additional information to the codegenerator
@@ -596,18 +591,12 @@
return fmt.Sprintf("%q", label.Label), nil
}
- ret = "{\n"
// Sort and print the struct props by the key.
structProps := extractStructProperties(propertyValue, indent)
if len(structProps) == 0 {
return "", nil
}
- for _, k := range android.SortedStringKeys(structProps) {
- ret += makeIndent(indent + 1)
- ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
- }
- ret += makeIndent(indent)
- ret += "}"
+ return starlark_fmt.PrintDict(structProps, indent), nil
case reflect.Interface:
// TODO(b/164227191): implement pretty print for interfaces.
// Interfaces are used for for arch, multilib and target properties.
@@ -616,7 +605,6 @@
return "", fmt.Errorf(
"unexpected kind for property struct field: %s", propertyValue.Kind())
}
- return ret, nil
}
// Converts a reflected property struct value into a map of property names and property values,
@@ -657,7 +645,7 @@
}
propertyName := proptools.PropertyNameForField(field.Name)
- prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
+ prettyPrintedValue, err := prettyPrint(fieldValue, indent+1, false)
if err != nil {
panic(
fmt.Errorf(
@@ -695,9 +683,9 @@
} else {
return true
}
- // Always print bools, if you want a bool attribute to be able to take the default value, use a
- // bool pointer instead
- case reflect.Bool:
+ // Always print bool/strings, if you want a bool/string attribute to be able to take the default value, use a
+ // pointer instead
+ case reflect.Bool, reflect.String:
return false
default:
if !value.IsValid() {
@@ -721,13 +709,6 @@
return strings.ReplaceAll(s, "\"", "\\\"")
}
-func makeIndent(indent int) string {
- if indent < 0 {
- panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
- }
- return strings.Repeat(" ", indent)
-}
-
func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
name := ""
if c.ModuleSubDir(logicModule) != "" {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index f14574c..b21a477 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -41,6 +41,7 @@
soong_module_deps = [
],
bool_prop = False,
+ string_prop = "",
)`,
},
{
@@ -58,6 +59,7 @@
soong_module_deps = [
],
bool_prop = True,
+ string_prop = "",
)`,
},
{
@@ -76,6 +78,7 @@
],
bool_prop = False,
owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
+ string_prop = "",
)`,
},
{
@@ -94,6 +97,7 @@
],
bool_prop = False,
required = ["bar"],
+ string_prop = "",
)`,
},
{
@@ -111,6 +115,7 @@
soong_module_deps = [
],
bool_prop = False,
+ string_prop = "",
target_required = [
"qux",
"bazqux",
@@ -147,6 +152,7 @@
"tag": ".bar",
"targets": ["goal_bar"],
}],
+ string_prop = "",
)`,
},
{
@@ -179,6 +185,7 @@
}],
owner = "custom_owner",
required = ["bar"],
+ string_prop = "",
target_required = [
"qux",
"bazqux",
@@ -223,40 +230,56 @@
func TestGenerateBazelTargetModules(t *testing.T) {
testCases := []bp2buildTestCase{
{
+ description: "string ptr props",
blueprint: `custom {
name: "foo",
- string_list_prop: ["a", "b"],
- string_prop: "a",
+ string_ptr_prop: "",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`custom(
- name = "foo",
- string_list_prop = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("custom", "foo", attrNameToString{
+ "string_ptr_prop": `""`,
+ }),
+ },
+ },
+ {
+ description: "string 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{
+ "string_list_prop": `[
"a",
"b",
- ],
- string_prop = "a",
-)`,
+ ]`,
+ "string_ptr_prop": `"a"`,
+ }),
},
},
{
+ description: "control characters",
blueprint: `custom {
- name: "control_characters",
+ name: "foo",
string_list_prop: ["\t", "\n"],
- string_prop: "a\t\n\r",
+ string_ptr_prop: "a\t\n\r",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`custom(
- name = "control_characters",
- string_list_prop = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("custom", "foo", attrNameToString{
+ "string_list_prop": `[
"\t",
"\n",
- ],
- string_prop = "a\t\n\r",
-)`,
+ ]`,
+ "string_ptr_prop": `"a\t\n\r"`,
+ }),
},
},
{
+ description: "handles dep",
blueprint: `custom {
name: "has_dep",
arch_paths: [":dep"],
@@ -268,36 +291,121 @@
arch_paths: ["abc"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`custom(
- name = "dep",
- arch_paths = ["abc"],
-)`,
- `custom(
- name = "has_dep",
- arch_paths = [":dep"],
-)`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("custom", "dep", attrNameToString{
+ "arch_paths": `["abc"]`,
+ }),
+ makeBazelTarget("custom", "has_dep", attrNameToString{
+ "arch_paths": `[":dep"]`,
+ }),
},
},
{
+ description: "non-existent dep",
+ blueprint: `custom {
+ name: "has_dep",
+ arch_paths: [":dep"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("custom", "has_dep", attrNameToString{
+ "arch_paths": `[":dep__BP2BUILD__MISSING__DEP"]`,
+ }),
+ },
+ },
+ {
+ description: "arch-variant srcs",
blueprint: `custom {
name: "arch_paths",
arch: {
- x86: {
- arch_paths: ["abc"],
- },
+ x86: { arch_paths: ["x86.txt"] },
+ x86_64: { arch_paths: ["x86_64.txt"] },
+ arm: { arch_paths: ["arm.txt"] },
+ arm64: { arch_paths: ["arm64.txt"] },
+ },
+ target: {
+ linux: { arch_paths: ["linux.txt"] },
+ bionic: { arch_paths: ["bionic.txt"] },
+ host: { arch_paths: ["host.txt"] },
+ not_windows: { arch_paths: ["not_windows.txt"] },
+ android: { arch_paths: ["android.txt"] },
+ linux_musl: { arch_paths: ["linux_musl.txt"] },
+ musl: { arch_paths: ["musl.txt"] },
+ linux_glibc: { arch_paths: ["linux_glibc.txt"] },
+ glibc: { arch_paths: ["glibc.txt"] },
+ linux_bionic: { arch_paths: ["linux_bionic.txt"] },
+ darwin: { arch_paths: ["darwin.txt"] },
+ windows: { arch_paths: ["windows.txt"] },
+ },
+ multilib: {
+ lib32: { arch_paths: ["lib32.txt"] },
+ lib64: { arch_paths: ["lib64.txt"] },
},
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`custom(
- name = "arch_paths",
- arch_paths = select({
- "//build/bazel/platforms/arch:x86": ["abc"],
+ expectedBazelTargets: []string{
+ makeBazelTarget("custom", "arch_paths", attrNameToString{
+ "arch_paths": `select({
+ "//build/bazel/platforms/arch:arm": [
+ "arm.txt",
+ "lib32.txt",
+ ],
+ "//build/bazel/platforms/arch:arm64": [
+ "arm64.txt",
+ "lib64.txt",
+ ],
+ "//build/bazel/platforms/arch:x86": [
+ "x86.txt",
+ "lib32.txt",
+ ],
+ "//build/bazel/platforms/arch:x86_64": [
+ "x86_64.txt",
+ "lib64.txt",
+ ],
"//conditions:default": [],
- }),
-)`,
+ }) + select({
+ "//build/bazel/platforms/os:android": [
+ "linux.txt",
+ "bionic.txt",
+ "android.txt",
+ ],
+ "//build/bazel/platforms/os:darwin": [
+ "host.txt",
+ "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",
+ "bionic.txt",
+ "linux_bionic.txt",
+ "not_windows.txt",
+ ],
+ "//build/bazel/platforms/os:linux_musl": [
+ "host.txt",
+ "linux.txt",
+ "musl.txt",
+ "linux_musl.txt",
+ "not_windows.txt",
+ ],
+ "//build/bazel/platforms/os:windows": [
+ "host.txt",
+ "windows.txt",
+ ],
+ "//conditions:default": [],
+ })`,
+ }),
},
},
{
+ description: "arch-variant deps",
blueprint: `custom {
name: "has_dep",
arch: {
@@ -313,79 +421,82 @@
arch_paths: ["abc"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`custom(
- name = "dep",
- arch_paths = ["abc"],
-)`,
- `custom(
- name = "has_dep",
- arch_paths = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("custom", "dep", attrNameToString{
+ "arch_paths": `["abc"]`,
+ }),
+ makeBazelTarget("custom", "has_dep", attrNameToString{
+ "arch_paths": `select({
"//build/bazel/platforms/arch:x86": [":dep"],
"//conditions:default": [],
- }),
-)`,
+ })`,
+ }),
},
},
{
+ description: "embedded props",
blueprint: `custom {
name: "embedded_props",
embedded_prop: "abc",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`custom(
- name = "embedded_props",
- embedded_attr = "abc",
-)`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("custom", "embedded_props", attrNameToString{
+ "embedded_attr": `"abc"`,
+ }),
},
},
{
+ description: "ptr to embedded props",
blueprint: `custom {
name: "ptr_to_embedded_props",
other_embedded_prop: "abc",
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`custom(
- name = "ptr_to_embedded_props",
- other_embedded_attr = "abc",
-)`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("custom", "ptr_to_embedded_props", attrNameToString{
+ "other_embedded_attr": `"abc"`,
+ }),
},
},
}
dir := "."
for _, testCase := range testCases {
- config := android.TestConfig(buildDir, nil, testCase.blueprint, nil)
- ctx := android.NewTestContext(config)
+ t.Run(testCase.description, func(t *testing.T) {
+ config := android.TestConfig(buildDir, nil, testCase.blueprint, nil)
+ ctx := android.NewTestContext(config)
- registerCustomModuleForBp2buildConversion(ctx)
+ registerCustomModuleForBp2buildConversion(ctx)
- _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
- if errored(t, testCase, errs) {
- continue
- }
- _, errs = ctx.ResolveDependencies(config)
- if errored(t, testCase, errs) {
- continue
- }
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ if errored(t, testCase, errs) {
+ return
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if errored(t, testCase, errs) {
+ return
+ }
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
- bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
- android.FailIfErrored(t, err)
+ 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, got %d", expectedCount, actualCount)
- } else {
- for i, expectedBazelTarget := range testCase.expectedBazelTargets {
- actualBazelTarget := bazelTargets[i]
- if actualBazelTarget.content != expectedBazelTarget {
- t.Errorf(
- "Expected generated Bazel target to be '%s', got '%s'",
- expectedBazelTarget,
- actualBazelTarget.content,
- )
+ 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 {
+ actualBazelTarget := bazelTargets[i]
+ if actualBazelTarget.content != expectedBazelTarget {
+ t.Errorf(
+ "Expected generated Bazel target to be '%s', got '%s'",
+ expectedBazelTarget,
+ actualBazelTarget.content,
+ )
+ }
}
}
- }
+ })
}
}
@@ -498,6 +609,7 @@
{
bp: `custom {
name: "bar",
+ one_to_many_prop: true,
bazel_module: { bp2build_available: true },
}`,
expectedBazelTarget: `my_library(
@@ -522,7 +634,6 @@
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutatorFromStarlark)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -560,90 +671,81 @@
func TestModuleTypeBp2Build(t *testing.T) {
testCases := []bp2buildTestCase{
{
- description: "filegroup with does not specify srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "filegroup with does not specify srcs",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
blueprint: `filegroup {
name: "fg_foo",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- `filegroup(
- name = "fg_foo",
-)`,
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
},
},
{
- description: "filegroup with no srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "filegroup with no srcs",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
blueprint: `filegroup {
name: "fg_foo",
srcs: [],
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- `filegroup(
- name = "fg_foo",
-)`,
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
},
},
{
- description: "filegroup with srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "filegroup with srcs",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
blueprint: `filegroup {
name: "fg_foo",
srcs: ["a", "b"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`filegroup(
- name = "fg_foo",
- srcs = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
"a",
"b",
- ],
-)`,
+ ]`,
+ }),
},
},
{
- description: "filegroup with excludes srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ 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{`filegroup(
- name = "fg_foo",
- srcs = ["b"],
-)`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ "srcs": `["b"]`,
+ }),
},
},
{
- description: "filegroup with glob",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "filegroup with glob",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
blueprint: `filegroup {
- name: "foo",
+ name: "fg_foo",
srcs: ["**/*.txt"],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`filegroup(
- name = "foo",
- srcs = [
+ 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": "",
@@ -653,25 +755,10 @@
},
},
{
- description: "filegroup with glob in subdir",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- blueprint: `filegroup {
- name: "foo",
- srcs: ["a.txt"],
- bazel_module: { bp2build_available: true },
-}`,
- dir: "other",
- expectedBazelTargets: []string{`filegroup(
- name = "fg_foo",
- srcs = [
- "a.txt",
- "b.txt",
- "subdir/a.txt",
- ],
-)`,
- },
+ description: "filegroup with glob in subdir",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ dir: "other",
filesystem: map[string]string{
"other/Android.bp": `filegroup {
name: "fg_foo",
@@ -683,28 +770,28 @@
"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,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "depends_on_other_dir_module",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
blueprint: `filegroup {
- name: "foobar",
+ name: "fg_foo",
srcs: [
":foo",
"c",
],
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`filegroup(
- name = "foobar",
- srcs = [
- "//other:foo",
- "c",
- ],
-)`,
- },
filesystem: map[string]string{
"other/Android.bp": `filegroup {
name: "foo",
@@ -712,13 +799,20 @@
bazel_module: { bp2build_available: true },
}`,
},
+ expectedBazelTargets: []string{
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "//other:foo",
+ "c",
+ ]`,
+ }),
+ },
},
{
- description: "depends_on_other_unconverted_module_error",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- unconvertedDepsMode: errorModulesUnconvertedDeps,
+ description: "depends_on_other_unconverted_module_error",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ unconvertedDepsMode: errorModulesUnconvertedDeps,
blueprint: `filegroup {
name: "foobar",
srcs: [
@@ -748,18 +842,16 @@
func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
testCases := []struct {
- moduleTypeUnderTest string
- moduleTypeUnderTestFactory android.ModuleFactory
- moduleTypeUnderTestBp2BuildMutator bp2buildMutator
- bp string
- expectedCount int
- description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ bp string
+ expectedCount int
+ description string
}{
{
- description: "explicitly unavailable",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "explicitly unavailable",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
bp: `filegroup {
name: "foo",
srcs: ["a", "b"],
@@ -768,10 +860,9 @@
expectedCount: 0,
},
{
- description: "implicitly unavailable",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "implicitly unavailable",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
bp: `filegroup {
name: "foo",
srcs: ["a", "b"],
@@ -779,10 +870,9 @@
expectedCount: 0,
},
{
- description: "explicitly available",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "explicitly available",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
bp: `filegroup {
name: "foo",
srcs: ["a", "b"],
@@ -791,12 +881,12 @@
expectedCount: 1,
},
{
- description: "generates more than 1 target if needed",
- moduleTypeUnderTest: "custom",
- moduleTypeUnderTestFactory: customModuleFactory,
- moduleTypeUnderTestBp2BuildMutator: customBp2BuildMutatorFromStarlark,
+ description: "generates more than 1 target if needed",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactory,
bp: `custom {
name: "foo",
+ one_to_many_prop: true,
bazel_module: { bp2build_available: true },
}`,
expectedCount: 3,
@@ -809,7 +899,6 @@
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
- ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -829,20 +918,18 @@
func TestAllowlistingBp2buildTargetsWithConfig(t *testing.T) {
testCases := []struct {
- moduleTypeUnderTest string
- moduleTypeUnderTestFactory android.ModuleFactory
- moduleTypeUnderTestBp2BuildMutator bp2buildMutator
- expectedCount map[string]int
- description string
- bp2buildConfig android.Bp2BuildConfig
- checkDir string
- fs map[string]string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ expectedCount map[string]int
+ description string
+ bp2buildConfig android.Bp2BuildConfig
+ checkDir string
+ fs map[string]string
}{
{
- description: "test bp2build config package and subpackages config",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "test bp2build config package and subpackages config",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
expectedCount: map[string]int{
"migrated": 1,
"migrated/but_not_really": 0,
@@ -864,10 +951,9 @@
},
},
{
- description: "test bp2build config opt-in and opt-out",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "test bp2build config opt-in and opt-out",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
expectedCount: map[string]int{
"package-opt-in": 2,
"package-opt-in/subpackage": 0,
@@ -918,7 +1004,6 @@
config := android.TestConfig(buildDir, nil, "", fs)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
- ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterBp2BuildConfig(testCase.bp2buildConfig)
ctx.RegisterForBazelConversion()
@@ -949,10 +1034,9 @@
func TestCombineBuildFilesBp2buildTargets(t *testing.T) {
testCases := []bp2buildTestCase{
{
- description: "filegroup bazel_module.label",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "filegroup bazel_module.label",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
blueprint: `filegroup {
name: "fg_foo",
bazel_module: { label: "//other:fg_foo" },
@@ -965,19 +1049,18 @@
},
},
{
- description: "multiple bazel_module.label same BUILD",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ description: "multiple bazel_module.label same BUILD",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
blueprint: `filegroup {
- name: "fg_foo",
- bazel_module: { label: "//other:fg_foo" },
- }
+ name: "fg_foo",
+ bazel_module: { label: "//other:fg_foo" },
+ }
- filegroup {
- name: "foo",
- bazel_module: { label: "//other:foo" },
- }`,
+ filegroup {
+ name: "foo",
+ bazel_module: { label: "//other:foo" },
+ }`,
expectedBazelTargets: []string{
`// BUILD file`,
},
@@ -986,60 +1069,56 @@
},
},
{
- description: "filegroup bazel_module.label and bp2build in subdir",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- dir: "other",
- blueprint: ``,
+ 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: {
- bp2build_available: true,
- },
- }
- filegroup {
- name: "fg_bar",
- bazel_module: {
- label: "//other:fg_bar"
- },
- }`,
+ name: "fg_foo",
+ bazel_module: {
+ bp2build_available: true,
+ },
+ }
+ filegroup {
+ name: "fg_bar",
+ bazel_module: {
+ label: "//other:fg_bar"
+ },
+ }`,
"other/BUILD.bazel": `// definition for fg_bar`,
},
expectedBazelTargets: []string{
- `filegroup(
- name = "fg_foo",
-)`, `// definition for fg_bar`,
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+ `// definition for fg_bar`,
},
},
{
- description: "filegroup bazel_module.label and filegroup bp2build",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- blueprint: `filegroup {
- name: "fg_foo",
- bazel_module: {
- label: "//other:fg_foo",
- },
- }
+ description: "filegroup bazel_module.label and filegroup bp2build",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
- filegroup {
- name: "fg_bar",
- bazel_module: {
- bp2build_available: true,
- },
- }`,
- expectedBazelTargets: []string{
- `filegroup(
- name = "fg_bar",
-)`,
- `// BUILD file`,
- },
filesystem: map[string]string{
"other/BUILD.bazel": `// BUILD file`,
},
+ blueprint: `filegroup {
+ name: "fg_foo",
+ bazel_module: {
+ label: "//other:fg_foo",
+ },
+ }
+
+ filegroup {
+ name: "fg_bar",
+ bazel_module: {
+ bp2build_available: true,
+ },
+ }`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("filegroup", "fg_bar", map[string]string{}),
+ `// BUILD file`,
+ },
},
}
@@ -1059,7 +1138,6 @@
config := android.TestConfig(buildDir, nil, testCase.blueprint, fs)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
- ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, toParse)
@@ -1105,26 +1183,15 @@
func TestGlobExcludeSrcs(t *testing.T) {
testCases := []bp2buildTestCase{
{
- description: "filegroup top level exclude_srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ 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 },
}`,
- expectedBazelTargets: []string{`filegroup(
- name = "fg_foo",
- srcs = [
- "a.txt",
- "b.txt",
- "//dir:e.txt",
- "//dir:f.txt",
- ],
-)`,
- },
filesystem: map[string]string{
"a.txt": "",
"b.txt": "",
@@ -1133,14 +1200,23 @@
"dir/e.txt": "",
"dir/f.txt": "",
},
+ expectedBazelTargets: []string{
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
+ "a.txt",
+ "b.txt",
+ "//dir:e.txt",
+ "//dir:f.txt",
+ ]`,
+ }),
+ },
},
{
- description: "filegroup in subdir exclude_srcs",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- blueprint: "",
- dir: "dir",
+ 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",
@@ -1155,142 +1231,81 @@
"dir/subdir/e.txt": "",
"dir/subdir/f.txt": "",
},
- expectedBazelTargets: []string{`filegroup(
- name = "fg_foo",
- srcs = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ "srcs": `[
"a.txt",
"//dir/subdir:e.txt",
"//dir/subdir:f.txt",
- ],
-)`,
+ ]`,
+ }),
},
},
}
- dir := "."
for _, testCase := range testCases {
- fs := make(map[string][]byte)
- toParse := []string{
- "Android.bp",
- }
- 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)
- ctx := android.NewTestContext(config)
- ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
- ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
- ctx.RegisterForBazelConversion()
-
- _, errs := ctx.ParseFileList(dir, toParse)
- if errored(t, testCase, errs) {
- continue
- }
- _, errs = ctx.ResolveDependencies(config)
- if errored(t, testCase, errs) {
- continue
- }
-
- checkDir := dir
- if testCase.dir != "" {
- checkDir = testCase.dir
- }
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
- bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
- android.FailIfErrored(t, err)
- if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
- t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets)
- } else {
- for i, target := range bazelTargets {
- if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
- t.Errorf(
- "%s: Expected generated Bazel target to be '%s', got '%s'",
- testCase.description,
- w,
- g,
- )
- }
- }
- }
+ t.Run(testCase.description, func(t *testing.T) {
+ runBp2BuildTestCaseSimple(t, testCase)
+ })
}
}
func TestCommonBp2BuildModuleAttrs(t *testing.T) {
testCases := []bp2buildTestCase{
{
- description: "Required into data test",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- blueprint: `filegroup {
- name: "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{`filegroup(
- name = "fg_foo",
- data = [":reqd"],
-)`,
- `filegroup(
- name = "reqd",
-)`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ "data": `[":reqd"]`,
+ }),
},
},
{
- description: "Required via arch into data test",
- moduleTypeUnderTest: "python_library",
- moduleTypeUnderTestFactory: python.PythonLibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: python.PythonLibraryBp2Build,
- blueprint: `python_library {
- name: "reqdx86",
- bazel_module: { bp2build_available: false, },
-}
-
-python_library {
- name: "reqdarm",
- bazel_module: { bp2build_available: false, },
-}
-
+ 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",
arch: {
- arm: {
- required: ["reqdarm"],
- },
- x86: {
- required: ["reqdx86"],
- },
+ arm: {
+ required: ["reqdarm"],
+ },
+ x86: {
+ required: ["reqdx86"],
+ },
},
bazel_module: { bp2build_available: true },
}`,
- expectedBazelTargets: []string{`py_library(
- name = "fg_foo",
- data = select({
+ 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",
-)`,
+ })`,
+ "srcs_version": `"PY3"`,
+ }),
},
},
{
- description: "Required appended to data test",
- moduleTypeUnderTest: "python_library",
- moduleTypeUnderTestFactory: python.PythonLibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: python.PythonLibraryBp2Build,
- blueprint: `python_library {
- name: "reqd",
- srcs: ["src.py"],
-}
-
+ 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") + `
python_library {
name: "fg_foo",
data: ["data.bin"],
@@ -1298,52 +1313,36 @@
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
- `py_library(
- name = "fg_foo",
- data = [
+ makeBazelTarget("py_library", "fg_foo", map[string]string{
+ "data": `[
"data.bin",
":reqd",
- ],
- srcs_version = "PY3",
-)`,
- `py_library(
- name = "reqd",
- srcs = ["src.py"],
- srcs_version = "PY3",
-)`,
- },
- filesystem: map[string]string{
- "data.bin": "",
- "src.py": "",
+ ]`,
+ "srcs_version": `"PY3"`,
+ }),
},
},
{
- description: "All props-to-attrs at once together test",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- blueprint: `filegroup {
- name: "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{
- `filegroup(
- name = "fg_foo",
- data = [":reqd"],
-)`,
- `filegroup(
- name = "reqd",
-)`,
+ makeBazelTarget("filegroup", "fg_foo", map[string]string{
+ "data": `[":reqd"]`,
+ }),
},
- filesystem: map[string]string{},
},
}
- for _, test := range testCases {
- runBp2BuildTestCaseSimple(t, test)
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ runBp2BuildTestCaseSimple(t, tc)
+ })
}
}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 1e78c0e..f3345a6 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -100,6 +100,7 @@
# nested_props_ptr start
# "nested_prop": attr.string(),
# nested_props_ptr end
+ "one_to_many_prop": attr.bool(),
"other_embedded_prop": attr.string(),
"string_list_prop": attr.string_list(),
"string_prop": attr.string(),
@@ -128,6 +129,7 @@
# nested_props_ptr start
# "nested_prop": attr.string(),
# nested_props_ptr end
+ "one_to_many_prop": attr.bool(),
"other_embedded_prop": attr.string(),
"string_list_prop": attr.string_list(),
"string_prop": attr.string(),
@@ -156,6 +158,7 @@
# nested_props_ptr start
# "nested_prop": attr.string(),
# nested_props_ptr end
+ "one_to_many_prop": attr.bool(),
"other_embedded_prop": attr.string(),
"string_list_prop": attr.string_list(),
"string_prop": attr.string(),
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
new file mode 100644
index 0000000..8d94079
--- /dev/null
+++ b/bp2build/cc_binary_conversion_test.go
@@ -0,0 +1,500 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/genrule"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+const (
+ ccBinaryTypePlaceHolder = "{rule_name}"
+)
+
+type testBazelTarget struct {
+ typ string
+ name string
+ attrs attrNameToString
+}
+
+func generateBazelTargetsForTest(targets []testBazelTarget) []string {
+ ret := make([]string, 0, len(targets))
+ for _, t := range targets {
+ ret = append(ret, makeBazelTarget(t.typ, t.name, t.attrs))
+ }
+ return ret
+}
+
+type ccBinaryBp2buildTestCase struct {
+ description string
+ blueprint string
+ targets []testBazelTarget
+}
+
+func registerCcBinaryModuleTypes(ctx android.RegistrationContext) {
+ cc.RegisterCCBuildComponents(ctx)
+ ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+ ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+ ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+}
+
+var binaryReplacer = strings.NewReplacer(ccBinaryTypePlaceHolder, "cc_binary")
+var hostBinaryReplacer = strings.NewReplacer(ccBinaryTypePlaceHolder, "cc_binary_host")
+
+func runCcBinaryTests(t *testing.T, tc ccBinaryBp2buildTestCase) {
+ t.Helper()
+ runCcBinaryTestCase(t, tc)
+ runCcHostBinaryTestCase(t, tc)
+}
+
+func runCcBinaryTestCase(t *testing.T, tc 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) {
+ t.Helper()
+ runBp2BuildTestCase(t, registerCcBinaryModuleTypes, testCase)
+ })
+}
+
+func runCcHostBinaryTestCase(t *testing.T, tc 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),
+ })
+ })
+}
+
+func TestBasicCcBinary(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: "basic -- properties -> attrs with little/no transformation",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ srcs: ["a.cc"],
+ local_include_dirs: ["dir"],
+ include_dirs: ["absolute_dir"],
+ cflags: ["-Dcopt"],
+ cppflags: ["-Dcppflag"],
+ conlyflags: ["-Dconlyflag"],
+ asflags: ["-Dasflag"],
+ ldflags: ["ld-flag"],
+ rtti: true,
+ strip: {
+ all: true,
+ keep_symbols: true,
+ keep_symbols_and_debug_frame: true,
+ keep_symbols_list: ["symbol"],
+ none: true,
+ },
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", attrNameToString{
+ "absolute_includes": `["absolute_dir"]`,
+ "asflags": `["-Dasflag"]`,
+ "conlyflags": `["-Dconlyflag"]`,
+ "copts": `["-Dcopt"]`,
+ "cppflags": `["-Dcppflag"]`,
+ "linkopts": `["ld-flag"]`,
+ "local_includes": `[
+ "dir",
+ ".",
+ ]`,
+ "rtti": `True`,
+ "srcs": `["a.cc"]`,
+ "strip": `{
+ "all": True,
+ "keep_symbols": True,
+ "keep_symbols_and_debug_frame": True,
+ "keep_symbols_list": ["symbol"],
+ "none": True,
+ }`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcBinaryWithSharedLdflagDisableFeature(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: `ldflag "-shared" disables static_flag feature`,
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ ldflags: ["-shared"],
+ include_build_directory: false,
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", attrNameToString{
+ "features": `["-static_flag"]`,
+ "linkopts": `["-shared"]`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcBinaryWithLinkStatic(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: "link static",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ static_executable: true,
+ include_build_directory: false,
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", attrNameToString{
+ "linkshared": `False`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcBinaryVersionScript(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: `version script`,
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ include_build_directory: false,
+ version_script: "vs",
+}
+`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", attrNameToString{
+ "additional_linker_inputs": `["vs"]`,
+ "linkopts": `["-Wl,--version-script,$(location vs)"]`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcBinarySplitSrcsByLang(t *testing.T) {
+ runCcHostBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "split srcs by lang",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ srcs: [
+ "asonly.S",
+ "conly.c",
+ "cpponly.cpp",
+ ":fg_foo",
+ ],
+ include_build_directory: false,
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "fg_foo"),
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", attrNameToString{
+ "srcs": `[
+ "cpponly.cpp",
+ ":fg_foo_cpp_srcs",
+ ]`,
+ "srcs_as": `[
+ "asonly.S",
+ ":fg_foo_as_srcs",
+ ]`,
+ "srcs_c": `[
+ "conly.c",
+ ":fg_foo_c_srcs",
+ ]`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcBinaryDoNotDistinguishBetweenDepsAndImplementationDeps(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "no implementation deps",
+ blueprint: `
+genrule {
+ name: "generated_hdr",
+ cmd: "nothing to see here",
+ bazel_module: { bp2build_available: false },
+}
+
+genrule {
+ name: "export_generated_hdr",
+ cmd: "nothing to see here",
+ bazel_module: { bp2build_available: false },
+}
+
+{rule_name} {
+ name: "foo",
+ srcs: ["foo.cpp"],
+ shared_libs: ["implementation_shared_dep", "shared_dep"],
+ export_shared_lib_headers: ["shared_dep"],
+ static_libs: ["implementation_static_dep", "static_dep"],
+ export_static_lib_headers: ["static_dep", "whole_static_dep"],
+ whole_static_libs: ["not_explicitly_exported_whole_static_dep", "whole_static_dep"],
+ include_build_directory: false,
+ generated_headers: ["generated_hdr", "export_generated_hdr"],
+ export_generated_headers: ["export_generated_hdr"],
+}
+` +
+ simpleModuleDoNotConvertBp2build("cc_library_static", "static_dep") +
+ simpleModuleDoNotConvertBp2build("cc_library_static", "implementation_static_dep") +
+ simpleModuleDoNotConvertBp2build("cc_library_static", "whole_static_dep") +
+ simpleModuleDoNotConvertBp2build("cc_library_static", "not_explicitly_exported_whole_static_dep") +
+ simpleModuleDoNotConvertBp2build("cc_library", "shared_dep") +
+ simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep"),
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", attrNameToString{
+ "deps": `[
+ ":implementation_static_dep",
+ ":static_dep",
+ ]`,
+ "dynamic_deps": `[
+ ":implementation_shared_dep",
+ ":shared_dep",
+ ]`,
+ "srcs": `[
+ "foo.cpp",
+ ":generated_hdr",
+ ":export_generated_hdr",
+ ]`,
+ "whole_archive_deps": `[
+ ":not_explicitly_exported_whole_static_dep",
+ ":whole_static_dep",
+ ]`,
+ "local_includes": `["."]`,
+ },
+ },
+ },
+ })
+}
+
+func TestCcBinaryNocrtTests(t *testing.T) {
+ baseTestCases := []struct {
+ description string
+ soongProperty string
+ bazelAttr attrNameToString
+ }{
+ {
+ description: "nocrt: true",
+ soongProperty: `nocrt: true,`,
+ bazelAttr: attrNameToString{"link_crt": `False`},
+ },
+ {
+ description: "nocrt: false",
+ soongProperty: `nocrt: false,`,
+ bazelAttr: attrNameToString{},
+ },
+ {
+ description: "nocrt: not set",
+ bazelAttr: attrNameToString{},
+ },
+ }
+
+ baseBlueprint := `{rule_name} {
+ name: "foo",%s
+ include_build_directory: false,
+}
+`
+
+ for _, btc := range baseTestCases {
+ prop := btc.soongProperty
+ if len(prop) > 0 {
+ prop = "\n" + prop
+ }
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: btc.description,
+ blueprint: fmt.Sprintf(baseBlueprint, prop),
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", btc.bazelAttr},
+ },
+ })
+ }
+}
+
+func TestCcBinaryNo_libcrtTests(t *testing.T) {
+ baseTestCases := []struct {
+ description string
+ soongProperty string
+ bazelAttr attrNameToString
+ }{
+ {
+ description: "no_libcrt: true",
+ soongProperty: `no_libcrt: true,`,
+ bazelAttr: attrNameToString{"use_libcrt": `False`},
+ },
+ {
+ description: "no_libcrt: false",
+ soongProperty: `no_libcrt: false,`,
+ bazelAttr: attrNameToString{"use_libcrt": `True`},
+ },
+ {
+ description: "no_libcrt: not set",
+ bazelAttr: attrNameToString{},
+ },
+ }
+
+ baseBlueprint := `{rule_name} {
+ name: "foo",%s
+ include_build_directory: false,
+}
+`
+
+ for _, btc := range baseTestCases {
+ prop := btc.soongProperty
+ if len(prop) > 0 {
+ prop = "\n" + prop
+ }
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: btc.description,
+ blueprint: fmt.Sprintf(baseBlueprint, prop),
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", btc.bazelAttr},
+ },
+ })
+ }
+}
+
+func TestCcBinaryPropertiesToFeatures(t *testing.T) {
+ baseTestCases := []struct {
+ description string
+ soongProperty string
+ bazelAttr attrNameToString
+ }{
+ {
+ description: "pack_relocation: true",
+ soongProperty: `pack_relocations: true,`,
+ bazelAttr: attrNameToString{},
+ },
+ {
+ description: "pack_relocations: false",
+ soongProperty: `pack_relocations: false,`,
+ bazelAttr: attrNameToString{"features": `["disable_pack_relocations"]`},
+ },
+ {
+ description: "pack_relocations: not set",
+ bazelAttr: attrNameToString{},
+ },
+ {
+ description: "pack_relocation: true",
+ soongProperty: `allow_undefined_symbols: true,`,
+ bazelAttr: attrNameToString{"features": `["-no_undefined_symbols"]`},
+ },
+ {
+ description: "allow_undefined_symbols: false",
+ soongProperty: `allow_undefined_symbols: false,`,
+ bazelAttr: attrNameToString{},
+ },
+ {
+ description: "allow_undefined_symbols: not set",
+ bazelAttr: attrNameToString{},
+ },
+ }
+
+ baseBlueprint := `{rule_name} {
+ name: "foo",%s
+ include_build_directory: false,
+}
+`
+ for _, btc := range baseTestCases {
+ prop := btc.soongProperty
+ if len(prop) > 0 {
+ prop = "\n" + prop
+ }
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ description: btc.description,
+ blueprint: fmt.Sprintf(baseBlueprint, prop),
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", btc.bazelAttr},
+ },
+ })
+ }
+}
+
+func TestCcBinarySharedProto(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ blueprint: soongCcProtoLibraries + `{rule_name} {
+ name: "foo",
+ srcs: ["foo.proto"],
+ proto: {
+ },
+ include_build_directory: false,
+}`,
+ targets: []testBazelTarget{
+ {"proto_library", "foo_proto", attrNameToString{
+ "srcs": `["foo.proto"]`,
+ }}, {"cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }}, {"cc_binary", "foo", attrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryStaticProto(t *testing.T) {
+ runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+ blueprint: soongCcProtoLibraries + `{rule_name} {
+ name: "foo",
+ srcs: ["foo.proto"],
+ static_executable: true,
+ proto: {
+ },
+ include_build_directory: false,
+}`,
+ targets: []testBazelTarget{
+ {"proto_library", "foo_proto", attrNameToString{
+ "srcs": `["foo.proto"]`,
+ }}, {"cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }}, {"cc_binary", "foo", attrNameToString{
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ "linkshared": `False`,
+ }},
+ },
+ })
+}
diff --git a/bp2build/cc_genrule_conversion_test.go b/bp2build/cc_genrule_conversion_test.go
deleted file mode 100644
index a7e9cb2..0000000
--- a/bp2build/cc_genrule_conversion_test.go
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package bp2build
-
-import (
- "testing"
-
- "android/soong/android"
- "android/soong/cc"
- "android/soong/genrule"
-)
-
-var otherCcGenruleBp = map[string]string{
- "other/Android.bp": `cc_genrule {
- name: "foo.tool",
- out: ["foo_tool.out"],
- srcs: ["foo_tool.in"],
- cmd: "cp $(in) $(out)",
-}
-cc_genrule {
- name: "other.tool",
- out: ["other_tool.out"],
- srcs: ["other_tool.in"],
- cmd: "cp $(in) $(out)",
-}`,
-}
-
-func runCcGenruleTestCase(t *testing.T, tc bp2buildTestCase) {
- t.Helper()
- runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
-}
-
-func TestCliVariableReplacement(t *testing.T) {
- runCcGenruleTestCase(t, bp2buildTestCase{
- description: "cc_genrule with command line variable replacements",
- moduleTypeUnderTest: "cc_genrule",
- moduleTypeUnderTestFactory: cc.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build,
- blueprint: `cc_genrule {
- name: "foo.tool",
- out: ["foo_tool.out"],
- srcs: ["foo_tool.in"],
- cmd: "cp $(in) $(out)",
- bazel_module: { bp2build_available: true },
-}
-
-cc_genrule {
- name: "foo",
- out: ["foo.out"],
- srcs: ["foo.in"],
- tools: [":foo.tool"],
- cmd: "$(location :foo.tool) --genDir=$(genDir) arg $(in) $(out)",
- bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{
- `genrule(
- name = "foo",
- cmd = "$(location :foo.tool) --genDir=$(RULEDIR) arg $(SRCS) $(OUTS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = [":foo.tool"],
-)`,
- `genrule(
- name = "foo.tool",
- cmd = "cp $(SRCS) $(OUTS)",
- outs = ["foo_tool.out"],
- srcs = ["foo_tool.in"],
-)`,
- },
- })
-}
-
-func TestUsingLocationsLabel(t *testing.T) {
- runCcGenruleTestCase(t, bp2buildTestCase{
- description: "cc_genrule using $(locations :label)",
- moduleTypeUnderTest: "cc_genrule",
- moduleTypeUnderTestFactory: cc.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build,
- blueprint: `cc_genrule {
- name: "foo.tools",
- out: ["foo_tool.out", "foo_tool2.out"],
- srcs: ["foo_tool.in"],
- cmd: "cp $(in) $(out)",
- bazel_module: { bp2build_available: true },
-}
-
-cc_genrule {
- name: "foo",
- out: ["foo.out"],
- srcs: ["foo.in"],
- tools: [":foo.tools"],
- cmd: "$(locations :foo.tools) -s $(out) $(in)",
- bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(locations :foo.tools) -s $(OUTS) $(SRCS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = [":foo.tools"],
-)`,
- `genrule(
- name = "foo.tools",
- cmd = "cp $(SRCS) $(OUTS)",
- outs = [
- "foo_tool.out",
- "foo_tool2.out",
- ],
- srcs = ["foo_tool.in"],
-)`,
- },
- })
-}
-
-func TestUsingLocationsAbsoluteLabel(t *testing.T) {
- runCcGenruleTestCase(t, bp2buildTestCase{
- description: "cc_genrule using $(locations //absolute:label)",
- moduleTypeUnderTest: "cc_genrule",
- moduleTypeUnderTestFactory: cc.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build,
- blueprint: `cc_genrule {
- name: "foo",
- out: ["foo.out"],
- srcs: ["foo.in"],
- tool_files: [":foo.tool"],
- cmd: "$(locations :foo.tool) -s $(out) $(in)",
- bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = ["//other:foo.tool"],
-)`,
- },
- filesystem: otherCcGenruleBp,
- })
-}
-
-func TestSrcsUsingAbsoluteLabel(t *testing.T) {
- runCcGenruleTestCase(t, bp2buildTestCase{
- description: "cc_genrule srcs using $(locations //absolute:label)",
- moduleTypeUnderTest: "cc_genrule",
- moduleTypeUnderTestFactory: cc.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build,
- blueprint: `cc_genrule {
- name: "foo",
- out: ["foo.out"],
- srcs: [":other.tool"],
- tool_files: [":foo.tool"],
- cmd: "$(locations :foo.tool) -s $(out) $(location :other.tool)",
- bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)",
- outs = ["foo.out"],
- srcs = ["//other:other.tool"],
- tools = ["//other:foo.tool"],
-)`,
- },
- filesystem: otherCcGenruleBp,
- })
-}
-
-func TestLocationsLabelUsesFirstToolFile(t *testing.T) {
- runCcGenruleTestCase(t, bp2buildTestCase{
- description: "cc_genrule using $(location) label should substitute first tool label automatically",
- moduleTypeUnderTest: "cc_genrule",
- moduleTypeUnderTestFactory: cc.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build,
- blueprint: `cc_genrule {
- name: "foo",
- out: ["foo.out"],
- srcs: ["foo.in"],
- tool_files: [":foo.tool", ":other.tool"],
- cmd: "$(location) -s $(out) $(in)",
- bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(location //other:foo.tool) -s $(OUTS) $(SRCS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = [
- "//other:foo.tool",
- "//other:other.tool",
- ],
-)`,
- },
- filesystem: otherCcGenruleBp,
- })
-}
-
-func TestLocationsLabelUsesFirstTool(t *testing.T) {
- runCcGenruleTestCase(t, bp2buildTestCase{
- description: "cc_genrule using $(locations) label should substitute first tool label automatically",
- moduleTypeUnderTest: "cc_genrule",
- moduleTypeUnderTestFactory: cc.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build,
- blueprint: `cc_genrule {
- name: "foo",
- out: ["foo.out"],
- srcs: ["foo.in"],
- tools: [":foo.tool", ":other.tool"],
- cmd: "$(locations) -s $(out) $(in)",
- bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = [
- "//other:foo.tool",
- "//other:other.tool",
- ],
-)`,
- },
- filesystem: otherCcGenruleBp,
- })
-}
-
-func TestWithoutToolsOrToolFiles(t *testing.T) {
- runCcGenruleTestCase(t, bp2buildTestCase{
- description: "cc_genrule without tools or tool_files can convert successfully",
- moduleTypeUnderTest: "cc_genrule",
- moduleTypeUnderTestFactory: cc.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build,
- blueprint: `cc_genrule {
- name: "foo",
- out: ["foo.out"],
- srcs: ["foo.in"],
- cmd: "cp $(in) $(out)",
- bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "cp $(SRCS) $(OUTS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
-)`,
- },
- })
-}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 0d65822..8fde655 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -15,6 +15,7 @@
package bp2build
import (
+ "fmt"
"testing"
"android/soong/android"
@@ -26,18 +27,20 @@
soongCcLibraryPreamble = `
cc_defaults {
name: "linux_bionic_supported",
+}`
+
+ soongCcProtoLibraries = `
+cc_library {
+ name: "libprotobuf-cpp-lite",
+ bazel_module: { bp2build_available: false },
}
-toolchain_library {
- name: "libclang_rt.builtins-x86_64-android",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- vendor_ramdisk_available: true,
- product_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- src: "",
+cc_library {
+ name: "libprotobuf-cpp-full",
+ bazel_module: { bp2build_available: false },
}`
+
+ soongCcProtoPreamble = soongCcLibraryPreamble + soongCcProtoLibraries
)
func runCcLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
@@ -50,16 +53,14 @@
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
- ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
}
func TestCcLibrarySimple(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - simple example",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library - simple example",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
filesystem: map[string]string{
"android.cpp": "",
"bionic.cpp": "",
@@ -80,8 +81,8 @@
"x86_64.cpp": "",
"foo-dir/a.h": "",
},
- blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "some-headers" }
+ blueprint: soongCcLibraryPreamble +
+ simpleModuleDoNotConvertBp2build("cc_library_headers", "some-headers") + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -116,39 +117,38 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "foo-lib",
- copts = ["-Wall"],
- export_includes = ["foo-dir"],
- implementation_deps = [":some-headers"],
- linkopts = ["-Wl,--exclude-libs=bar.a"] + select({
+ expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ "copts": `["-Wall"]`,
+ "export_includes": `["foo-dir"]`,
+ "implementation_deps": `[":some-headers"]`,
+ "linkopts": `["-Wl,--exclude-libs=bar.a"] + select({
"//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"],
"//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=qux.a"],
"//conditions:default": [],
- }),
- srcs = ["impl.cpp"] + select({
+ })`,
+ "srcs": `["impl.cpp"] + select({
"//build/bazel/platforms/arch:x86": ["x86.cpp"],
"//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"],
"//conditions:default": [],
}) + select({
"//build/bazel/platforms/os:android": [
- "android.cpp",
"bionic.cpp",
+ "android.cpp",
],
"//build/bazel/platforms/os:darwin": ["darwin.cpp"],
"//build/bazel/platforms/os:linux": ["linux.cpp"],
"//build/bazel/platforms/os:linux_bionic": ["bionic.cpp"],
"//conditions:default": [],
- }),
-)`}})
+ })`,
+ }),
+ })
}
func TestCcLibraryTrimmedLdAndroid(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - trimmed example of //bionic/linker:ld-android",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ 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": "",
@@ -156,8 +156,8 @@
"linker_block_allocator.h": "",
"linker_cfi.h": "",
},
- blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "libc_headers" }
+ blueprint: soongCcLibraryPreamble +
+ simpleModuleDoNotConvertBp2build("cc_library_headers", "libc_headers") + `
cc_library {
name: "fake-ld-android",
srcs: ["ld_android.cpp"],
@@ -187,16 +187,16 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "fake-ld-android",
- copts = [
+ expectedBazelTargets: makeCcLibraryTargets("fake-ld-android", attrNameToString{
+ "srcs": `["ld_android.cpp"]`,
+ "copts": `[
"-Wall",
"-Wextra",
"-Wunused",
"-Werror",
- ],
- implementation_deps = [":libc_headers"],
- linkopts = [
+ ]`,
+ "implementation_deps": `[":libc_headers"]`,
+ "linkopts": `[
"-Wl,--exclude-libs=libgcc.a",
"-Wl,--exclude-libs=libgcc_stripped.a",
"-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
@@ -207,19 +207,17 @@
"//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=libgcc_eh.a"],
"//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=libgcc_eh.a"],
"//conditions:default": [],
- }),
- srcs = ["ld_android.cpp"],
-)`},
+ })`,
+ }),
})
}
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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
- dir: "external",
+ 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": "",
@@ -254,24 +252,22 @@
`,
},
blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{`cc_library(
- name = "fake-libarm-optimized-routines-math",
- copts = select({
+ expectedBazelTargets: makeCcLibraryTargets("fake-libarm-optimized-routines-math", attrNameToString{
+ "copts": `select({
"//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"],
"//conditions:default": [],
- }),
- local_includes = ["."],
- srcs_c = ["math/cosf.c"],
-)`},
+ })`,
+ "local_includes": `["."]`,
+ "srcs_c": `["math/cosf.c"]`,
+ }),
})
}
func TestCcLibrarySharedStaticProps(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library shared/static props",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library shared/static props",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
filesystem: map[string]string{
"both.cpp": "",
"sharedonly.cpp": "",
@@ -347,37 +343,59 @@
bazel_module: { bp2build_available: false },
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- copts = ["bothflag"],
- implementation_deps = [":static_dep_for_both"],
- implementation_dynamic_deps = [":shared_dep_for_both"],
- shared = {
- "copts": ["sharedflag"],
- "implementation_deps": [":static_dep_for_shared"],
- "implementation_dynamic_deps": [":shared_dep_for_shared"],
- "srcs": ["sharedonly.cpp"],
- "whole_archive_deps": [":whole_static_lib_for_shared"],
- },
- srcs = ["both.cpp"],
- static = {
- "copts": ["staticflag"],
- "implementation_deps": [":static_dep_for_static"],
- "implementation_dynamic_deps": [":shared_dep_for_static"],
- "srcs": ["staticonly.cpp"],
- "whole_archive_deps": [":whole_static_lib_for_static"],
- },
- whole_archive_deps = [":whole_static_lib_for_both"],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ "copts": `[
+ "bothflag",
+ "staticflag",
+ ]`,
+ "implementation_deps": `[
+ ":static_dep_for_both",
+ ":static_dep_for_static",
+ ]`,
+ "implementation_dynamic_deps": `[
+ ":shared_dep_for_both",
+ ":shared_dep_for_static",
+ ]`,
+ "srcs": `[
+ "both.cpp",
+ "staticonly.cpp",
+ ]`,
+ "whole_archive_deps": `[
+ ":whole_static_lib_for_both",
+ ":whole_static_lib_for_static",
+ ]`}),
+ makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ "copts": `[
+ "bothflag",
+ "sharedflag",
+ ]`,
+ "implementation_deps": `[
+ ":static_dep_for_both",
+ ":static_dep_for_shared",
+ ]`,
+ "implementation_dynamic_deps": `[
+ ":shared_dep_for_both",
+ ":shared_dep_for_shared",
+ ]`,
+ "srcs": `[
+ "both.cpp",
+ "sharedonly.cpp",
+ ]`,
+ "whole_archive_deps": `[
+ ":whole_static_lib_for_both",
+ ":whole_static_lib_for_shared",
+ ]`,
+ }),
+ },
})
}
func TestCcLibraryDeps(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library shared/static props",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library shared/static props",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
filesystem: map[string]string{
"both.cpp": "",
"sharedonly.cpp": "",
@@ -431,52 +449,80 @@
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{`cc_library(
- name = "a",
- copts = ["bothflag"],
- deps = [":static_dep_for_both"],
- dynamic_deps = [":shared_dep_for_both"],
- implementation_deps = [":implementation_static_dep_for_both"],
- implementation_dynamic_deps = [":implementation_shared_dep_for_both"],
- shared = {
- "copts": ["sharedflag"],
- "deps": [":static_dep_for_shared"],
- "dynamic_deps": [":shared_dep_for_shared"],
- "implementation_deps": [":implementation_static_dep_for_shared"],
- "implementation_dynamic_deps": [":implementation_shared_dep_for_shared"],
- "srcs": ["sharedonly.cpp"],
- "whole_archive_deps": [
- ":not_explicitly_exported_whole_static_dep_for_shared",
- ":whole_static_dep_for_shared",
- ],
- },
- srcs = ["both.cpp"],
- static = {
- "copts": ["staticflag"],
- "deps": [":static_dep_for_static"],
- "dynamic_deps": [":shared_dep_for_static"],
- "implementation_deps": [":implementation_static_dep_for_static"],
- "implementation_dynamic_deps": [":implementation_shared_dep_for_static"],
- "srcs": ["staticonly.cpp"],
- "whole_archive_deps": [
- ":not_explicitly_exported_whole_static_dep_for_static",
- ":whole_static_dep_for_static",
- ],
- },
- whole_archive_deps = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ "copts": `[
+ "bothflag",
+ "staticflag",
+ ]`,
+ "deps": `[
+ ":static_dep_for_both",
+ ":static_dep_for_static",
+ ]`,
+ "dynamic_deps": `[
+ ":shared_dep_for_both",
+ ":shared_dep_for_static",
+ ]`,
+ "implementation_deps": `[
+ ":implementation_static_dep_for_both",
+ ":implementation_static_dep_for_static",
+ ]`,
+ "implementation_dynamic_deps": `[
+ ":implementation_shared_dep_for_both",
+ ":implementation_shared_dep_for_static",
+ ]`,
+ "srcs": `[
+ "both.cpp",
+ "staticonly.cpp",
+ ]`,
+ "whole_archive_deps": `[
":not_explicitly_exported_whole_static_dep_for_both",
":whole_static_dep_for_both",
- ],
-)`},
- })
+ ":not_explicitly_exported_whole_static_dep_for_static",
+ ":whole_static_dep_for_static",
+ ]`,
+ }),
+ makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ "copts": `[
+ "bothflag",
+ "sharedflag",
+ ]`,
+ "deps": `[
+ ":static_dep_for_both",
+ ":static_dep_for_shared",
+ ]`,
+ "dynamic_deps": `[
+ ":shared_dep_for_both",
+ ":shared_dep_for_shared",
+ ]`,
+ "implementation_deps": `[
+ ":implementation_static_dep_for_both",
+ ":implementation_static_dep_for_shared",
+ ]`,
+ "implementation_dynamic_deps": `[
+ ":implementation_shared_dep_for_both",
+ ":implementation_shared_dep_for_shared",
+ ]`,
+ "srcs": `[
+ "both.cpp",
+ "sharedonly.cpp",
+ ]`,
+ "whole_archive_deps": `[
+ ":not_explicitly_exported_whole_static_dep_for_both",
+ ":whole_static_dep_for_both",
+ ":not_explicitly_exported_whole_static_dep_for_shared",
+ ":whole_static_dep_for_shared",
+ ]`,
+ })},
+ },
+ )
}
func TestCcLibraryWholeStaticLibsAlwaysLink(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
- dir: "foo/bar",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ dir: "foo/bar",
filesystem: map[string]string{
"foo/bar/Android.bp": `
cc_library {
@@ -500,26 +546,30 @@
`,
},
blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- shared = {
- "whole_archive_deps": [":whole_static_lib_for_shared_alwayslink"],
- },
- static = {
- "whole_archive_deps": [":whole_static_lib_for_static_alwayslink"],
- },
- whole_archive_deps = [":whole_static_lib_for_both_alwayslink"],
-)`},
- })
+ 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{
+ "whole_archive_deps": `[
+ ":whole_static_lib_for_both_alwayslink",
+ ":whole_static_lib_for_shared_alwayslink",
+ ]`,
+ }),
+ },
+ },
+ )
}
func TestCcLibrarySharedStaticPropsInArch(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library shared/static props in arch",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
- dir: "foo/bar",
+ 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": "",
@@ -590,71 +640,86 @@
`,
},
blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- copts = ["bothflag"],
- implementation_deps = [":static_dep_for_both"],
- local_includes = ["."],
- shared = {
- "copts": ["sharedflag"] + select({
- "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"],
- "//conditions:default": [],
- }),
- "implementation_deps": [":static_dep_for_shared"] + select({
- "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
- "//conditions:default": [],
- }),
- "implementation_dynamic_deps": select({
- "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
- "//conditions:default": [],
- }),
- "srcs": ["sharedonly.cpp"] + select({
- "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": ["android_shared.cpp"],
- "//conditions:default": [],
- }),
- "whole_archive_deps": select({
- "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
- "//conditions:default": [],
- }),
- },
- srcs = ["both.cpp"],
- static = {
- "copts": ["staticflag"] + select({
- "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
- "//conditions:default": [],
- }),
- "implementation_deps": [":static_dep_for_static"] + select({
- "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
- "//conditions:default": [],
- }),
- "srcs": ["staticonly.cpp"] + select({
- "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
- "//conditions:default": [],
- }),
- },
-)`},
- })
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ "copts": `[
+ "bothflag",
+ "staticflag",
+ ] + select({
+ "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
+ "//conditions:default": [],
+ })`,
+ "implementation_deps": `[
+ ":static_dep_for_both",
+ ":static_dep_for_static",
+ ] + select({
+ "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ "srcs": `[
+ "both.cpp",
+ "staticonly.cpp",
+ ] + select({
+ "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
+ "//conditions:default": [],
+ })`,
+ }),
+ makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ "copts": `[
+ "bothflag",
+ "sharedflag",
+ ] + select({
+ "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"],
+ "//conditions:default": [],
+ })`,
+ "implementation_deps": `[
+ ":static_dep_for_both",
+ ":static_dep_for_shared",
+ ] + select({
+ "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
+ "//conditions:default": [],
+ })`,
+ "implementation_dynamic_deps": `select({
+ "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ "srcs": `[
+ "both.cpp",
+ "sharedonly.cpp",
+ ] + select({
+ "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": ["android_shared.cpp"],
+ "//conditions:default": [],
+ })`,
+ "whole_archive_deps": `select({
+ "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ },
+ )
}
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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
- dir: "foo/bar",
+ 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": "",
@@ -728,66 +793,65 @@
`,
},
blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- local_includes = ["."],
- shared = {
- "srcs": [
- "shared_source.cpp",
- "shared_source.cc",
- ":shared_filegroup_cpp_srcs",
- ],
- "srcs_as": [
- "shared_source.s",
- "shared_source.S",
- ":shared_filegroup_as_srcs",
- ],
- "srcs_c": [
- "shared_source.c",
- ":shared_filegroup_c_srcs",
- ],
- },
- srcs = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+ "local_includes": `["."]`,
+ "srcs": `[
"both_source.cpp",
"both_source.cc",
":both_filegroup_cpp_srcs",
- ],
- srcs_as = [
+ "static_source.cpp",
+ "static_source.cc",
+ ":static_filegroup_cpp_srcs",
+ ]`,
+ "srcs_as": `[
"both_source.s",
"both_source.S",
":both_filegroup_as_srcs",
- ],
- srcs_c = [
+ "static_source.s",
+ "static_source.S",
+ ":static_filegroup_as_srcs",
+ ]`,
+ "srcs_c": `[
"both_source.c",
":both_filegroup_c_srcs",
- ],
- static = {
- "srcs": [
- "static_source.cpp",
- "static_source.cc",
- ":static_filegroup_cpp_srcs",
- ],
- "srcs_as": [
- "static_source.s",
- "static_source.S",
- ":static_filegroup_as_srcs",
- ],
- "srcs_c": [
- "static_source.c",
- ":static_filegroup_c_srcs",
- ],
- },
-)`},
- })
+ "static_source.c",
+ ":static_filegroup_c_srcs",
+ ]`,
+ }),
+ makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ "local_includes": `["."]`,
+ "srcs": `[
+ "both_source.cpp",
+ "both_source.cc",
+ ":both_filegroup_cpp_srcs",
+ "shared_source.cpp",
+ "shared_source.cc",
+ ":shared_filegroup_cpp_srcs",
+ ]`,
+ "srcs_as": `[
+ "both_source.s",
+ "both_source.S",
+ ":both_filegroup_as_srcs",
+ "shared_source.s",
+ "shared_source.S",
+ ":shared_filegroup_as_srcs",
+ ]`,
+ "srcs_c": `[
+ "both_source.c",
+ ":both_filegroup_c_srcs",
+ "shared_source.c",
+ ":shared_filegroup_c_srcs",
+ ]`,
+ })}})
}
func TestCcLibraryNonConfiguredVersionScript(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library non-configured version script",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
- dir: "foo/bar",
+ description: "cc_library non-configured version script",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ dir: "foo/bar",
filesystem: map[string]string{
"foo/bar/Android.bp": `
cc_library {
@@ -800,21 +864,21 @@
`,
},
blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- srcs = ["a.cpp"],
- version_script = "v.map",
-)`},
- })
+ expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ "additional_linker_inputs": `["v.map"]`,
+ "linkopts": `["-Wl,--version-script,$(location v.map)"]`,
+ "srcs": `["a.cpp"]`,
+ }),
+ },
+ )
}
func TestCcLibraryConfiguredVersionScript(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library configured version script",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
- dir: "foo/bar",
+ description: "cc_library configured version script",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ dir: "foo/bar",
filesystem: map[string]string{
"foo/bar/Android.bp": `
cc_library {
@@ -835,24 +899,28 @@
`,
},
blueprint: soongCcLibraryPreamble,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- srcs = ["a.cpp"],
- version_script = select({
- "//build/bazel/platforms/arch:arm": "arm.map",
- "//build/bazel/platforms/arch:arm64": "arm64.map",
- "//conditions:default": None,
- }),
-)`},
- })
+ expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ "additional_linker_inputs": `select({
+ "//build/bazel/platforms/arch:arm": ["arm.map"],
+ "//build/bazel/platforms/arch:arm64": ["arm64.map"],
+ "//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)"],
+ "//conditions:default": [],
+ })`,
+ "srcs": `["a.cpp"]`,
+ }),
+ },
+ )
}
func TestCcLibrarySharedLibs(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library shared_libs",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library shared_libs",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "mylib",
@@ -865,24 +933,53 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- implementation_dynamic_deps = [":mylib"],
-)`},
- })
+ expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ "implementation_dynamic_deps": `[":mylib"]`,
+ }),
+ },
+ )
}
-func TestCcLibraryPackRelocations(t *testing.T) {
+func TestCcLibraryFeatures(t *testing.T) {
+ expected_targets := []string{}
+ expected_targets = append(expected_targets, makeCcLibraryTargets("a", attrNameToString{
+ "features": `[
+ "disable_pack_relocations",
+ "-no_undefined_symbols",
+ ]`,
+ "srcs": `["a.cpp"]`,
+ })...)
+ expected_targets = append(expected_targets, makeCcLibraryTargets("b", attrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/arch:x86_64": [
+ "disable_pack_relocations",
+ "-no_undefined_symbols",
+ ],
+ "//conditions:default": [],
+ })`,
+ "srcs": `["b.cpp"]`,
+ })...)
+ expected_targets = append(expected_targets, makeCcLibraryTargets("c", attrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/os:darwin": [
+ "disable_pack_relocations",
+ "-no_undefined_symbols",
+ ],
+ "//conditions:default": [],
+ })`,
+ "srcs": `["c.cpp"]`,
+ })...)
+
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library pack_relocations test",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ 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,
}
@@ -892,6 +989,7 @@
arch: {
x86_64: {
pack_relocations: false,
+ allow_undefined_symbols: true,
},
},
include_build_directory: false,
@@ -903,38 +1001,20 @@
target: {
darwin: {
pack_relocations: false,
+ allow_undefined_symbols: true,
},
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- linkopts = ["-Wl,--pack-dyn-relocs=none"],
- srcs = ["a.cpp"],
-)`, `cc_library(
- name = "b",
- linkopts = select({
- "//build/bazel/platforms/arch:x86_64": ["-Wl,--pack-dyn-relocs=none"],
- "//conditions:default": [],
- }),
- srcs = ["b.cpp"],
-)`, `cc_library(
- name = "c",
- linkopts = select({
- "//build/bazel/platforms/os:darwin": ["-Wl,--pack-dyn-relocs=none"],
- "//conditions:default": [],
- }),
- srcs = ["c.cpp"],
-)`},
+ expectedBazelTargets: expected_targets,
})
}
func TestCcLibrarySpacesInCopts(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library spaces in copts",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library spaces in copts",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "a",
@@ -942,22 +1022,21 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- copts = [
+ expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ "copts": `[
"-include",
"header.h",
- ],
-)`},
- })
+ ]`,
+ }),
+ },
+ )
}
func TestCcLibraryCppFlagsGoesIntoCopts(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library cppflags usage",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library cppflags usage",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `cc_library {
name: "a",
srcs: ["a.cpp"],
@@ -979,10 +1058,9 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- copts = ["-Wall"],
- cppflags = [
+ expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ "copts": `["-Wall"]`,
+ "cppflags": `[
"-fsigned-char",
"-pedantic",
] + select({
@@ -991,51 +1069,18 @@
}) + select({
"//build/bazel/platforms/os:android": ["-DANDROID=1"],
"//conditions:default": [],
- }),
- srcs = ["a.cpp"],
-)`},
- })
-}
-
-func TestCcLibraryLabelAttributeGetTargetProperties(t *testing.T) {
- runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library GetTargetProperties on a LabelAttribute",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
- blueprint: soongCcLibraryPreamble + `
-cc_library {
- name: "a",
- srcs: ["a.cpp"],
- target: {
- android_arm: {
- version_script: "android_arm.map",
- },
- linux_bionic_arm64: {
- version_script: "linux_bionic_arm64.map",
- },
- },
- include_build_directory: false,
-}
- `,
- expectedBazelTargets: []string{`cc_library(
- name = "a",
- srcs = ["a.cpp"],
- version_script = select({
- "//build/bazel/platforms/os_arch:android_arm": "android_arm.map",
- "//build/bazel/platforms/os_arch:linux_bionic_arm64": "linux_bionic_arm64.map",
- "//conditions:default": None,
- }),
-)`},
- })
+ })`,
+ "srcs": `["a.cpp"]`,
+ }),
+ },
+ )
}
func TestCcLibraryExcludeLibs(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
- filesystem: map[string]string{},
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ filesystem: map[string]string{},
blueprint: soongCcLibraryStaticPreamble + `
cc_library {
name: "foo_static",
@@ -1110,47 +1155,115 @@
bazel_module: { bp2build_available: false },
}
`,
- expectedBazelTargets: []string{
- `cc_library(
- name = "foo_static",
- implementation_deps = select({
+ expectedBazelTargets: makeCcLibraryTargets("foo_static", attrNameToString{
+ "implementation_deps": `select({
"//build/bazel/platforms/arch:arm": [],
"//conditions:default": [":arm_static_lib_excludes_bp2build_cc_library_static"],
}) + select({
"//build/bazel/product_variables:malloc_not_svelte": [],
"//conditions:default": [":malloc_not_svelte_static_lib_excludes_bp2build_cc_library_static"],
- }),
- implementation_dynamic_deps = select({
+ })`,
+ "implementation_dynamic_deps": `select({
"//build/bazel/platforms/arch:arm": [],
"//conditions:default": [":arm_shared_lib_excludes"],
}) + select({
"//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_shared_lib"],
"//conditions:default": [],
- }),
- srcs_c = ["common.c"],
- whole_archive_deps = select({
+ })`,
+ "srcs_c": `["common.c"]`,
+ "whole_archive_deps": `select({
"//build/bazel/platforms/arch:arm": [],
"//conditions:default": [":arm_whole_static_lib_excludes_bp2build_cc_library_static"],
}) + select({
"//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib_bp2build_cc_library_static"],
"//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes_bp2build_cc_library_static"],
- }),
-)`,
- },
- })
+ })`,
+ }),
+ },
+ )
}
func TestCCLibraryNoCrtTrue(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - simple example",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library - nocrt: true emits attribute",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
filesystem: map[string]string{
"impl.cpp": "",
},
blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "some-headers" }
+cc_library {
+ name: "foo-lib",
+ srcs: ["impl.cpp"],
+ nocrt: true,
+ include_build_directory: false,
+}
+`,
+ expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ "link_crt": `False`,
+ "srcs": `["impl.cpp"]`,
+ }),
+ },
+ )
+}
+
+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{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "foo-lib",
+ srcs: ["impl.cpp"],
+ nocrt: false,
+ include_build_directory: false,
+}
+`,
+ 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{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "foo-lib",
+ srcs: ["impl.cpp"],
+ arch: {
+ arm: {
+ nocrt: true,
+ },
+ x86: {
+ nocrt: false,
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ expectedErr: fmt.Errorf("module \"foo-lib\": nocrt is not supported for arch variants"),
+ })
+}
+
+func TestCCLibraryNoLibCrtTrue(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ filesystem: map[string]string{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1158,23 +1271,48 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "foo-lib",
- srcs = ["impl.cpp"],
- use_libcrt = False,
-)`}})
+ expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ "srcs": `["impl.cpp"]`,
+ "use_libcrt": `False`,
+ }),
+ })
}
-func TestCCLibraryNoCrtFalse(t *testing.T) {
+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,
+ }
+ sharedAttrs := attrNameToString{}
+ staticAttrs := attrNameToString{}
+ for key, val := range attrs {
+ if _, staticOnly := STATIC_ONLY_ATTRS[key]; !staticOnly {
+ sharedAttrs[key] = val
+ }
+ if _, sharedOnly := SHARED_ONLY_ATTRS[key]; !sharedOnly {
+ staticAttrs[key] = val
+ }
+ }
+ 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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
filesystem: map[string]string{
"impl.cpp": "",
},
blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "some-headers" }
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
@@ -1182,18 +1320,17 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "foo-lib",
- srcs = ["impl.cpp"],
- use_libcrt = True,
-)`}})
+ expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ "srcs": `["impl.cpp"]`,
+ "use_libcrt": `True`,
+ }),
+ })
}
-func TestCCLibraryNoCrtArchVariant(t *testing.T) {
+func TestCCLibraryNoLibCrtArchVariant(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
filesystem: map[string]string{
"impl.cpp": "",
},
@@ -1212,22 +1349,21 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "foo-lib",
- srcs = ["impl.cpp"],
- use_libcrt = select({
+ 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,
- }),
-)`}})
+ })`,
+ }),
+ })
}
-func TestCCLibraryNoCrtArchVariantWithDefault(t *testing.T) {
+func TestCCLibraryNoLibCrtArchAndTargetVariant(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
filesystem: map[string]string{
"impl.cpp": "",
},
@@ -1235,7 +1371,6 @@
cc_library {
name: "foo-lib",
srcs: ["impl.cpp"],
- no_libcrt: false,
arch: {
arm: {
no_libcrt: true,
@@ -1244,26 +1379,108 @@
no_libcrt: true,
},
},
+ target: {
+ darwin: {
+ no_libcrt: true,
+ }
+ },
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "foo-lib",
- srcs = ["impl.cpp"],
- use_libcrt = select({
- "//build/bazel/platforms/arch:arm": False,
- "//build/bazel/platforms/arch:x86": False,
- "//conditions:default": True,
- }),
-)`}})
+ 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,
+ })`,
+ }),
+ })
+}
+
+func TestCCLibraryNoLibCrtArchAndTargetVariantConflict(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ filesystem: map[string]string{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "foo-lib",
+ srcs: ["impl.cpp"],
+ arch: {
+ arm: {
+ no_libcrt: true,
+ },
+ // This is expected to override the value for darwin_x86_64.
+ x86_64: {
+ no_libcrt: true,
+ },
+ },
+ target: {
+ darwin: {
+ no_libcrt: false,
+ }
+ },
+ 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_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,
+ })`,
+ }),
+ })
}
func TestCcLibraryStrip(t *testing.T) {
+ expectedTargets := []string{}
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("all", attrNameToString{
+ "strip": `{
+ "all": True,
+ }`,
+ })...)
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols", attrNameToString{
+ "strip": `{
+ "keep_symbols": True,
+ }`,
+ })...)
+ 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{
+ "strip": `{
+ "keep_symbols_list": ["symbol"],
+ }`,
+ })...)
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("none", attrNameToString{
+ "strip": `{
+ "none": True,
+ }`,
+ })...)
+ expectedTargets = append(expectedTargets, makeCcLibraryTargets("nothing", attrNameToString{})...)
+
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library strip args",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library strip args",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "nothing",
@@ -1305,43 +1522,15 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "all",
- strip = {
- "all": True,
- },
-)`, `cc_library(
- name = "keep_symbols",
- strip = {
- "keep_symbols": True,
- },
-)`, `cc_library(
- name = "keep_symbols_and_debug_frame",
- strip = {
- "keep_symbols_and_debug_frame": True,
- },
-)`, `cc_library(
- name = "keep_symbols_list",
- strip = {
- "keep_symbols_list": ["symbol"],
- },
-)`, `cc_library(
- name = "none",
- strip = {
- "none": True,
- },
-)`, `cc_library(
- name = "nothing",
-)`},
+ expectedBazelTargets: expectedTargets,
})
}
func TestCcLibraryStripWithArch(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library strip args",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library strip args",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "multi-arch",
@@ -1367,9 +1556,8 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "multi-arch",
- strip = {
+ expectedBazelTargets: makeCcLibraryTargets("multi-arch", attrNameToString{
+ "strip": `{
"keep_symbols": select({
"//build/bazel/platforms/arch:arm64": True,
"//conditions:default": None,
@@ -1385,17 +1573,17 @@
],
"//conditions:default": [],
}),
- },
-)`},
- })
+ }`,
+ }),
+ },
+ )
}
func TestCcLibrary_SystemSharedLibsRootEmpty(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library system_shared_libs empty at root",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library system_shared_libs empty at root",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "root_empty",
@@ -1403,19 +1591,18 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "root_empty",
- system_dynamic_deps = [],
-)`},
- })
+ expectedBazelTargets: makeCcLibraryTargets("root_empty", attrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ },
+ )
}
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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library system_shared_libs empty for static variant",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "static_empty",
@@ -1425,21 +1612,20 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "static_empty",
- static = {
- "system_dynamic_deps": [],
- },
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "static_empty_bp2build_cc_library_static", attrNameToString{
+ "system_dynamic_deps": "[]",
+ }),
+ 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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library system_shared_libs empty for shared variant",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "shared_empty",
@@ -1449,21 +1635,20 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "shared_empty",
- shared = {
- "system_dynamic_deps": [],
- },
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", attrNameToString{}),
+ makeBazelTarget("cc_library_shared", "shared_empty", attrNameToString{
+ "system_dynamic_deps": "[]",
+ }),
+ },
})
}
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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library system_shared_libs empty for shared, bionic variant",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "shared_empty",
@@ -1477,12 +1662,12 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "shared_empty",
- shared = {
- "system_dynamic_deps": [],
- },
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", attrNameToString{}),
+ makeBazelTarget("cc_library_shared", "shared_empty", attrNameToString{
+ "system_dynamic_deps": "[]",
+ }),
+ },
})
}
@@ -1492,10 +1677,9 @@
// 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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library system_shared_libs empty for linux_bionic variant",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "target_linux_bionic_empty",
@@ -1507,19 +1691,18 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "target_linux_bionic_empty",
- system_dynamic_deps = [],
-)`},
- })
+ expectedBazelTargets: makeCcLibraryTargets("target_linux_bionic_empty", attrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ },
+ )
}
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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library system_shared_libs empty for bionic variant",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "target_bionic_empty",
@@ -1531,19 +1714,18 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "target_bionic_empty",
- system_dynamic_deps = [],
-)`},
- })
+ expectedBazelTargets: makeCcLibraryTargets("target_bionic_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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ description: "cc_library system_shared_libs set for shared and root",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
blueprint: soongCcLibraryPreamble + `
cc_library {
name: "libc",
@@ -1563,25 +1745,27 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "foo",
- shared = {
- "system_dynamic_deps": [":libm"],
- },
- system_dynamic_deps = [":libc"],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ "system_dynamic_deps": `[":libc"]`,
+ }),
+ makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ "system_dynamic_deps": `[
+ ":libc",
+ ":libm",
+ ]`,
+ }),
+ },
})
}
func TestCcLibraryOsSelects(t *testing.T) {
runCcLibraryTestCase(t, bp2buildTestCase{
- description: "cc_library - selects for all os targets",
- moduleTypeUnderTest: "cc_library",
- moduleTypeUnderTestFactory: cc.LibraryFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library - selects for all os targets",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ filesystem: map[string]string{},
blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "some-headers" }
cc_library {
name: "foo-lib",
srcs: ["base.cpp"],
@@ -1611,13 +1795,12 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library(
- name = "foo-lib",
- srcs = ["base.cpp"] + select({
+ expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+ "srcs": `["base.cpp"] + select({
"//build/bazel/platforms/os:android": [
- "android.cpp",
- "bionic.cpp",
"linux.cpp",
+ "bionic.cpp",
+ "android.cpp",
],
"//build/bazel/platforms/os:darwin": ["darwin.cpp"],
"//build/bazel/platforms/os:linux": [
@@ -1625,8 +1808,8 @@
"linux_glibc.cpp",
],
"//build/bazel/platforms/os:linux_bionic": [
- "bionic.cpp",
"linux.cpp",
+ "bionic.cpp",
],
"//build/bazel/platforms/os:linux_musl": [
"linux.cpp",
@@ -1634,6 +1817,630 @@
],
"//build/bazel/platforms/os:windows": ["windows.cpp"],
"//conditions:default": [],
- }),
-)`}})
+ })`,
+ }),
+ },
+ )
+}
+
+func TestLibcryptoHashInjection(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ description: "cc_library - libcrypto hash injection",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ filesystem: map[string]string{},
+ blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "libcrypto",
+ target: {
+ android: {
+ inject_bssl_hash: true,
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ expectedBazelTargets: makeCcLibraryTargets("libcrypto", attrNameToString{
+ "inject_bssl_hash": `select({
+ "//build/bazel/platforms/os:android": True,
+ "//conditions:default": None,
+ })`,
+ }),
+ },
+ )
+}
+
+func TestCcLibraryCppStdWithGnuExtensions_ConvertsToFeatureAttr(t *testing.T) {
+ type testCase struct {
+ cpp_std string
+ c_std string
+ gnu_extensions string
+ bazel_cpp_std string
+ bazel_c_std string
+ }
+
+ testCases := []testCase{
+ // Existing usages of cpp_std in AOSP are:
+ // experimental, c++11, c++17, c++2a, c++98, gnu++11, gnu++17
+ //
+ // 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: "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"},
+ // 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: "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: "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: "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: "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: "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: "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: "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)
+ }
+
+ 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
+%s // c_std: *string
+%s // gnu_extensions: *bool
+ include_build_directory: false,
+}
+`, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
+ 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(`
+cc_library_static {
+ name: "%s_static",
+%s // cpp_std: *string
+%s // c_std: *string
+%s // gnu_extensions: *bool
+ include_build_directory: false,
+}
+`, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
+ 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(`
+cc_library_shared {
+ name: "%s_shared",
+%s // cpp_std: *string
+%s // c_std: *string
+%s // gnu_extensions: *bool
+ include_build_directory: false,
+}
+`, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
+ 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 {
+ name: "foo",
+ srcs: ["foo.proto"],
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ "srcs": `["foo.proto"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryProtoNoCanonicalPathFromRoot(t *testing.T) {
+ 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{
+ "srcs": `["foo.proto"]`,
+ "strip_import_prefix": `""`,
+ }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryProtoExplicitCanonicalPathFromRoot(t *testing.T) {
+ 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{
+ "srcs": `["foo.proto"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryProtoFull(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ srcs: ["foo.proto"],
+ proto: {
+ type: "full",
+ },
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ "srcs": `["foo.proto"]`,
+ }), makeBazelTarget("cc_proto_library", "foo_cc_proto", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }), 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"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryProtoLite(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ srcs: ["foo.proto"],
+ proto: {
+ type: "lite",
+ },
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ "srcs": `["foo.proto"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryProtoExportHeaders(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ srcs: ["foo.proto"],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ "srcs": `["foo.proto"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryProtoFilegroups(t *testing.T) {
+ 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") +
+ simpleModuleDoNotConvertBp2build("filegroup", "proto-srcs-d") + `
+cc_library {
+ name: "a",
+ srcs: [":a_fg_proto"],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}
+
+cc_library {
+ name: "b",
+ srcs: [":b_protos"],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}
+
+cc_library {
+ name: "c",
+ srcs: [":c-proto-srcs"],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}
+
+cc_library {
+ name: "d",
+ srcs: [":proto-srcs-d"],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("proto_library", "a_proto", attrNameToString{
+ "srcs": `[":a_fg_proto"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", attrNameToString{
+ "deps": `[":a_proto"]`,
+ }), 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{
+ "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{
+ "srcs": `[":b_protos"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "b_cc_proto_lite", attrNameToString{
+ "deps": `[":b_proto"]`,
+ }), 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{
+ "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{
+ "srcs": `[":c-proto-srcs"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "c_cc_proto_lite", attrNameToString{
+ "deps": `[":c_proto"]`,
+ }), 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{
+ "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{
+ "srcs": `[":proto-srcs-d"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "d_cc_proto_lite", attrNameToString{
+ "deps": `[":d_proto"]`,
+ }), 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{
+ "dynamic_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"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryDisabledArchAndTarget(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ srcs: ["foo.cpp"],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ linux_glibc_x86: {
+ enabled: false,
+ },
+ },
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+ "srcs": `["foo.cpp"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os_arch:darwin_arm64": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:darwin_x86_64": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:linux_glibc_x86": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:windows_x86": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:windows_x86_64": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ })
+}
+
+func TestCcLibraryDisabledArchAndTargetWithDefault(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ srcs: ["foo.cpp"],
+ enabled: false,
+ target: {
+ darwin: {
+ enabled: true,
+ },
+ windows: {
+ enabled: false,
+ },
+ linux_glibc_x86: {
+ enabled: false,
+ },
+ },
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+ "srcs": `["foo.cpp"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os_arch:darwin_arm64": [],
+ "//build/bazel/platforms/os_arch:darwin_x86_64": [],
+ "//conditions:default": ["@platforms//:incompatible"],
+ })`,
+ }),
+ })
+}
+
+func TestCcLibrarySharedDisabled(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ srcs: ["foo.cpp"],
+ enabled: false,
+ shared: {
+ enabled: true,
+ },
+ target: {
+ android: {
+ shared: {
+ enabled: false,
+ },
+ }
+ },
+ include_build_directory: false,
+}`,
+ 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{
+ "srcs": `["foo.cpp"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticDisabledForSomeArch(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ srcs: ["foo.cpp"],
+ shared: {
+ enabled: false
+ },
+ target: {
+ darwin: {
+ enabled: true,
+ },
+ windows: {
+ enabled: false,
+ },
+ linux_glibc_x86: {
+ shared: {
+ enabled: true,
+ },
+ },
+ },
+ include_build_directory: false,
+}`,
+ 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{
+ "srcs": `["foo.cpp"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os_arch:darwin_arm64": [],
+ "//build/bazel/platforms/os_arch:darwin_x86_64": [],
+ "//build/bazel/platforms/os_arch:linux_glibc_x86": [],
+ "//conditions:default": ["@platforms//:incompatible"],
+ })`,
+ }),
+ }})
+}
+
+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{
+ "foo/bar/Android.bp": `
+cc_library {
+ name: "a",
+ stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: true },
+ include_build_directory: false,
+}
+`,
+ },
+ blueprint: soongCcLibraryPreamble,
+ expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+ "stubs_symbol_file": `"a.map.txt"`,
+ "stubs_versions": `[
+ "28",
+ "29",
+ "current",
+ ]`,
+ }),
+ },
+ )
+}
+
+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}"]`,
+ }),
+ })
}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index e43672b..e4cfa35 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -26,17 +26,6 @@
soongCcLibraryHeadersPreamble = `
cc_defaults {
name: "linux_bionic_supported",
-}
-
-toolchain_library {
- name: "libclang_rt.builtins-x86_64-android",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- vendor_ramdisk_available: true,
- product_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- src: "",
}`
)
@@ -68,7 +57,6 @@
func registerCcLibraryHeadersModuleTypes(ctx android.RegistrationContext) {
cc.RegisterCCBuildComponents(ctx)
- ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
}
func runCcLibraryHeadersTestCase(t *testing.T, tc bp2buildTestCase) {
@@ -78,10 +66,9 @@
func TestCcLibraryHeadersSimple(t *testing.T) {
runCcLibraryHeadersTestCase(t, bp2buildTestCase{
- description: "cc_library_headers test",
- moduleTypeUnderTest: "cc_library_headers",
- moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+ description: "cc_library_headers test",
+ moduleTypeUnderTest: "cc_library_headers",
+ moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
filesystem: map[string]string{
"lib-1/lib1a.h": "",
"lib-1/lib1b.h": "",
@@ -128,9 +115,9 @@
// TODO: Also support export_header_lib_headers
}`,
- expectedBazelTargets: []string{`cc_library_headers(
- name = "foo_headers",
- export_includes = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+ "export_includes": `[
"dir-1",
"dir-2",
] + select({
@@ -138,22 +125,22 @@
"//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 = [
+ })`,
+ "implementation_deps": `[
":lib-1",
":lib-2",
- ],
-)`},
+ ]`,
+ }),
+ },
})
}
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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
- filesystem: map[string]string{},
+ 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",
@@ -191,27 +178,27 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_headers(
- name = "foo_headers",
- implementation_deps = [":base-lib"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+ "implementation_deps": `[":base-lib"] + 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:windows": [":windows-lib"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
- filesystem: map[string]string{},
+ 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",
@@ -231,27 +218,27 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_headers(
- name = "foo_headers",
- deps = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+ "deps": `select({
"//build/bazel/platforms/os:android": [":exported-lib"],
"//conditions:default": [],
- }),
- implementation_deps = select({
+ })`,
+ "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,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
- filesystem: map[string]string{},
+ 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: [
@@ -288,9 +275,9 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_headers(
- name = "foo_headers",
- export_system_includes = ["shared_include_dir"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+ "export_system_includes": `["shared_include_dir"] + select({
"//build/bazel/platforms/arch:arm": ["arm_include_dir"],
"//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"],
"//conditions:default": [],
@@ -299,17 +286,17 @@
"//build/bazel/platforms/os:darwin": ["darwin_include_dir"],
"//build/bazel/platforms/os:linux": ["linux_include_dir"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryHeadersNoCrtIgnored(t *testing.T) {
runCcLibraryHeadersTestCase(t, bp2buildTestCase{
- description: "cc_library_headers test",
- moduleTypeUnderTest: "cc_library_headers",
- moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+ description: "cc_library_headers test",
+ moduleTypeUnderTest: "cc_library_headers",
+ moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
filesystem: map[string]string{
"lib-1/lib1a.h": "",
"lib-1/lib1b.h": "",
@@ -330,9 +317,10 @@
no_libcrt: true,
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_headers(
- name = "lib-1",
- export_includes = ["lib-1"],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_headers", "lib-1", attrNameToString{
+ "export_includes": `["lib-1"]`,
+ }),
+ },
})
}
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 3dcfbd7..78192fe 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -15,6 +15,7 @@
package bp2build
import (
+ "fmt"
"testing"
"android/soong/android"
@@ -29,22 +30,21 @@
func registerCcLibrarySharedModuleTypes(ctx android.RegistrationContext) {
cc.RegisterCCBuildComponents(ctx)
- ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+ ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
}
func runCcLibrarySharedTestCase(t *testing.T, tc bp2buildTestCase) {
t.Helper()
+ (&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",
- moduleTypeUnderTest: "cc_library_shared",
- moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+ 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": "",
@@ -139,52 +139,50 @@
// TODO: Also support export_header_lib_headers
}`,
- expectedBazelTargets: []string{`cc_library_shared(
- name = "foo_shared",
- absolute_includes = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ "absolute_includes": `[
"include_dir_1",
"include_dir_2",
- ],
- copts = [
+ ]`,
+ "copts": `[
"-Dflag1",
"-Dflag2",
- ],
- export_includes = [
+ ]`,
+ "export_includes": `[
"export_include_dir_1",
"export_include_dir_2",
- ],
- implementation_deps = [
+ ]`,
+ "implementation_deps": `[
":header_lib_1",
":header_lib_2",
- ],
- implementation_dynamic_deps = [
+ ]`,
+ "implementation_dynamic_deps": `[
":shared_lib_1",
":shared_lib_2",
- ],
- local_includes = [
+ ]`,
+ "local_includes": `[
"local_include_dir_1",
"local_include_dir_2",
".",
- ],
- srcs = [
+ ]`,
+ "srcs": `[
"foo_shared1.cc",
"foo_shared2.cc",
- ],
- whole_archive_deps = [
+ ]`,
+ "whole_archive_deps": `[
":whole_static_lib_1",
":whole_static_lib_2",
- ],
-)`},
+ ]`,
+ }),
+ },
})
}
func TestCcLibrarySharedArchSpecificSharedLib(t *testing.T) {
runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared arch-specific shared_libs with whole_static_libs",
- moduleTypeUnderTest: "cc_library_shared",
- moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library_shared arch-specific shared_libs with whole_static_libs",
+ filesystem: map[string]string{},
blueprint: soongCcLibrarySharedPreamble + `
cc_library_static {
name: "static_dep",
@@ -199,27 +197,25 @@
arch: { arm64: { shared_libs: ["shared_dep"], whole_static_libs: ["static_dep"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_shared(
- name = "foo_shared",
- implementation_dynamic_deps = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ "implementation_dynamic_deps": `select({
"//build/bazel/platforms/arch:arm64": [":shared_dep"],
"//conditions:default": [],
- }),
- whole_archive_deps = select({
+ })`,
+ "whole_archive_deps": `select({
"//build/bazel/platforms/arch:arm64": [":static_dep"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibrarySharedOsSpecificSharedLib(t *testing.T) {
- runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_shared os-specific shared_libs",
- moduleTypeUnderTest: "cc_library_shared",
- moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
- filesystem: map[string]string{},
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ description: "cc_library_shared os-specific shared_libs",
+ filesystem: map[string]string{},
blueprint: soongCcLibrarySharedPreamble + `
cc_library_shared {
name: "shared_dep",
@@ -230,23 +226,21 @@
target: { android: { shared_libs: ["shared_dep"], } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_shared(
- name = "foo_shared",
- implementation_dynamic_deps = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ "implementation_dynamic_deps": `select({
"//build/bazel/platforms/os:android": [":shared_dep"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibrarySharedBaseArchOsSpecificSharedLib(t *testing.T) {
runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared base, arch, and os-specific shared_libs",
- moduleTypeUnderTest: "cc_library_shared",
- moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library_shared base, arch, and os-specific shared_libs",
+ filesystem: map[string]string{},
blueprint: soongCcLibrarySharedPreamble + `
cc_library_shared {
name: "shared_dep",
@@ -267,25 +261,23 @@
arch: { arm64: { shared_libs: ["shared_dep3"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_shared(
- name = "foo_shared",
- implementation_dynamic_deps = [":shared_dep"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ "implementation_dynamic_deps": `[":shared_dep"] + select({
"//build/bazel/platforms/arch:arm64": [":shared_dep3"],
"//conditions:default": [],
}) + select({
"//build/bazel/platforms/os:android": [":shared_dep2"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibrarySharedSimpleExcludeSrcs(t *testing.T) {
runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared simple exclude_srcs",
- moduleTypeUnderTest: "cc_library_shared",
- moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+ description: "cc_library_shared simple exclude_srcs",
filesystem: map[string]string{
"common.c": "",
"foo-a.c": "",
@@ -298,23 +290,21 @@
exclude_srcs: ["foo-excluded.c"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_shared(
- name = "foo_shared",
- srcs_c = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ "srcs_c": `[
"common.c",
"foo-a.c",
- ],
-)`},
+ ]`,
+ }),
+ },
})
}
func TestCcLibrarySharedStrip(t *testing.T) {
runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared stripping",
- moduleTypeUnderTest: "cc_library_shared",
- moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library_shared stripping",
+ filesystem: map[string]string{},
blueprint: soongCcLibrarySharedPreamble + `
cc_library_shared {
name: "foo_shared",
@@ -327,9 +317,9 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_shared(
- name = "foo_shared",
- strip = {
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ "strip": `{
"all": True,
"keep_symbols": False,
"keep_symbols_and_debug_frame": True,
@@ -338,17 +328,15 @@
"sym2",
],
"none": False,
- },
-)`},
+ }`,
+ }),
+ },
})
}
func TestCcLibrarySharedVersionScript(t *testing.T) {
runCcLibrarySharedTestCase(t, bp2buildTestCase{
- description: "cc_library_shared version script",
- moduleTypeUnderTest: "cc_library_shared",
- moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+ description: "cc_library_shared version script",
filesystem: map[string]string{
"version_script": "",
},
@@ -358,9 +346,149 @@
version_script: "version_script",
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_shared(
- name = "foo_shared",
- version_script = "version_script",
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ "additional_linker_inputs": `["version_script"]`,
+ "linkopts": `["-Wl,--version-script,$(location version_script)"]`,
+ }),
+ },
})
}
+
+func TestCcLibrarySharedNoCrtTrue(t *testing.T) {
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ description: "cc_library_shared - nocrt: true emits attribute",
+ filesystem: map[string]string{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library_shared {
+ name: "foo_shared",
+ srcs: ["impl.cpp"],
+ nocrt: true,
+ include_build_directory: false,
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ "link_crt": `False`,
+ "srcs": `["impl.cpp"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedNoCrtFalse(t *testing.T) {
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ description: "cc_library_shared - nocrt: false doesn't emit attribute",
+ filesystem: map[string]string{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library_shared {
+ name: "foo_shared",
+ srcs: ["impl.cpp"],
+ nocrt: false,
+ include_build_directory: false,
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_shared", "foo_shared", attrNameToString{
+ "srcs": `["impl.cpp"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedNoCrtArchVariant(t *testing.T) {
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ description: "cc_library_shared - nocrt in select",
+ filesystem: map[string]string{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library_shared {
+ name: "foo_shared",
+ srcs: ["impl.cpp"],
+ arch: {
+ arm: {
+ nocrt: true,
+ },
+ x86: {
+ nocrt: false,
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ expectedErr: fmt.Errorf("module \"foo_shared\": nocrt is not supported for arch variants"),
+ })
+}
+
+func TestCcLibrarySharedProto(t *testing.T) {
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ blueprint: soongCcProtoPreamble + `cc_library_shared {
+ name: "foo",
+ srcs: ["foo.proto"],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ "srcs": `["foo.proto"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+ "dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedUseVersionLib(t *testing.T) {
+ runCcLibrarySharedTestCase(t, bp2buildTestCase{
+ 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",
+ }),
+ },
+ })
+}
+
+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{
+ "foo/bar/Android.bp": `
+cc_library_shared {
+ name: "a",
+ stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+ bazel_module: { bp2build_available: true },
+ include_build_directory: false,
+}
+`,
+ },
+ blueprint: soongCcLibraryPreamble,
+ expectedBazelTargets: []string{makeBazelTarget("cc_library_shared", "a", attrNameToString{
+ "stubs_symbol_file": `"a.map.txt"`,
+ "stubs_versions": `[
+ "28",
+ "29",
+ "current",
+ ]`,
+ }),
+ },
+ },
+ )
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index f02ce4d..205bf4d 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -27,17 +27,6 @@
soongCcLibraryStaticPreamble = `
cc_defaults {
name: "linux_bionic_supported",
-}
-
-toolchain_library {
- name: "libclang_rt.builtins-x86_64-android",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- vendor_ramdisk_available: true,
- product_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- src: "",
}`
)
@@ -65,12 +54,10 @@
t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
}
}
-
}
func registerCcLibraryStaticModuleTypes(ctx android.RegistrationContext) {
cc.RegisterCCBuildComponents(ctx)
- ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
// Required for system_shared_libs dependencies.
@@ -79,15 +66,15 @@
func runCcLibraryStaticTestCase(t *testing.T, tc bp2buildTestCase) {
t.Helper()
+
+ (&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",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ 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": "",
@@ -182,49 +169,47 @@
// TODO: Also support export_header_lib_headers
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- absolute_includes = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "absolute_includes": `[
"include_dir_1",
"include_dir_2",
- ],
- copts = [
+ ]`,
+ "copts": `[
"-Dflag1",
"-Dflag2",
- ],
- export_includes = [
+ ]`,
+ "export_includes": `[
"export_include_dir_1",
"export_include_dir_2",
- ],
- implementation_deps = [
+ ]`,
+ "implementation_deps": `[
":header_lib_1",
":header_lib_2",
":static_lib_1",
":static_lib_2",
- ],
- local_includes = [
+ ]`,
+ "local_includes": `[
"local_include_dir_1",
"local_include_dir_2",
".",
- ],
- srcs = [
+ ]`,
+ "srcs": `[
"foo_static1.cc",
"foo_static2.cc",
- ],
- whole_archive_deps = [
+ ]`,
+ "whole_archive_deps": `[
":whole_static_lib_1",
":whole_static_lib_2",
- ],
-)`},
+ ]`,
+ }),
+ },
})
}
func TestCcLibraryStaticSubpackage(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static subpackage test",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static subpackage test",
filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
@@ -247,20 +232,18 @@
"subpackage",
],
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- absolute_includes = ["subpackage"],
- local_includes = ["."],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "absolute_includes": `["subpackage"]`,
+ "local_includes": `["."]`,
+ }),
+ },
})
}
func TestCcLibraryStaticExportIncludeDir(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static export include dir",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static export include dir",
filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
@@ -273,19 +256,17 @@
export_include_dirs: ["subpackage"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- export_includes = ["subpackage"],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "export_includes": `["subpackage"]`,
+ }),
+ },
})
}
func TestCcLibraryStaticExportSystemIncludeDir(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static export system include dir",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static export system include dir",
filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
@@ -298,20 +279,18 @@
export_system_include_dirs: ["subpackage"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- export_system_includes = ["subpackage"],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "export_system_includes": `["subpackage"]`,
+ }),
+ },
})
}
func TestCcLibraryStaticManyIncludeDirs(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static include_dirs, local_include_dirs, export_include_dirs (b/183742505)",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
- dir: "subpackage",
+ 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": `
@@ -335,28 +314,25 @@
"subpackage3/subsubpackage/header.h": "",
},
blueprint: soongCcLibraryStaticPreamble,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- absolute_includes = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "absolute_includes": `[
"subpackage/subsubpackage",
"subpackage2",
"subpackage3/subsubpackage",
- ],
- export_includes = ["./exported_subsubpackage"],
- local_includes = [
+ ]`,
+ "export_includes": `["./exported_subsubpackage"]`,
+ "local_includes": `[
"subsubpackage2",
".",
- ],
-)`},
+ ]`,
+ })},
})
}
func TestCcLibraryStaticIncludeBuildDirectoryDisabled(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static include_build_directory disabled",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static include_build_directory disabled",
filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
@@ -370,20 +346,18 @@
local_include_dirs: ["subpackage2"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- absolute_includes = ["subpackage"],
- local_includes = ["subpackage2"],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "absolute_includes": `["subpackage"]`,
+ "local_includes": `["subpackage2"]`,
+ }),
+ },
})
}
func TestCcLibraryStaticIncludeBuildDirectoryEnabled(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static include_build_directory enabled",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static include_build_directory enabled",
filesystem: map[string]string{
// subpackage with subdirectory
"subpackage/Android.bp": "",
@@ -399,24 +373,22 @@
local_include_dirs: ["subpackage2"],
include_build_directory: true,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- absolute_includes = ["subpackage"],
- local_includes = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "absolute_includes": `["subpackage"]`,
+ "local_includes": `[
"subpackage2",
".",
- ],
-)`},
+ ]`,
+ }),
+ },
})
}
func TestCcLibraryStaticArchSpecificStaticLib(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch-specific static_libs",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library_static arch-specific static_libs",
+ filesystem: map[string]string{},
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "static_dep",
@@ -431,27 +403,25 @@
arch: { arm64: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- implementation_deps = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "implementation_deps": `select({
"//build/bazel/platforms/arch:arm64": [":static_dep"],
"//conditions:default": [],
- }),
- whole_archive_deps = select({
+ })`,
+ "whole_archive_deps": `select({
"//build/bazel/platforms/arch:arm64": [":static_dep2"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryStaticOsSpecificStaticLib(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static os-specific static_libs",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library_static os-specific static_libs",
+ filesystem: map[string]string{},
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "static_dep",
@@ -466,27 +436,25 @@
target: { android: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- implementation_deps = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "implementation_deps": `select({
"//build/bazel/platforms/os:android": [":static_dep"],
"//conditions:default": [],
- }),
- whole_archive_deps = select({
+ })`,
+ "whole_archive_deps": `select({
"//build/bazel/platforms/os:android": [":static_dep2"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryStaticBaseArchOsSpecificStaticLib(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static base, arch and os-specific static_libs",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library_static base, arch and os-specific static_libs",
+ filesystem: map[string]string{},
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "static_dep",
@@ -512,26 +480,24 @@
arch: { arm64: { static_libs: ["static_dep4"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- implementation_deps = [":static_dep"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "implementation_deps": `[":static_dep"] + select({
"//build/bazel/platforms/arch:arm64": [":static_dep4"],
"//conditions:default": [],
}) + select({
"//build/bazel/platforms/os:android": [":static_dep3"],
"//conditions:default": [],
- }),
- whole_archive_deps = [":static_dep2"],
-)`},
+ })`,
+ "whole_archive_deps": `[":static_dep2"]`,
+ }),
+ },
})
}
func TestCcLibraryStaticSimpleExcludeSrcs(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static simple exclude_srcs",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static simple exclude_srcs",
filesystem: map[string]string{
"common.c": "",
"foo-a.c": "",
@@ -544,22 +510,20 @@
exclude_srcs: ["foo-excluded.c"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- srcs_c = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs_c": `[
"common.c",
"foo-a.c",
- ],
-)`},
+ ]`,
+ }),
+ },
})
}
func TestCcLibraryStaticOneArchSrcs(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static one arch specific srcs",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static one arch specific srcs",
filesystem: map[string]string{
"common.c": "",
"foo-arm.c": "",
@@ -571,22 +535,20 @@
arch: { arm: { srcs: ["foo-arm.c"] } },
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- srcs_c = ["common.c"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": ["foo-arm.c"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryStaticOneArchSrcsExcludeSrcs(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static one arch specific srcs and exclude_srcs",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static one arch specific srcs and exclude_srcs",
filesystem: map[string]string{
"common.c": "",
"for-arm.c": "",
@@ -603,22 +565,20 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- srcs_c = ["common.c"] + select({
+ 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"],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryStaticTwoArchExcludeSrcs(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch specific exclude_srcs for 2 architectures",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static arch specific exclude_srcs for 2 architectures",
filesystem: map[string]string{
"common.c": "",
"for-arm.c": "",
@@ -637,31 +597,30 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- srcs_c = ["common.c"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": [
- "for-arm.c",
"not-for-x86.c",
+ "for-arm.c",
],
"//build/bazel/platforms/arch:x86": [
- "for-x86.c",
"not-for-arm.c",
+ "for-x86.c",
],
"//conditions:default": [
"not-for-arm.c",
"not-for-x86.c",
],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
+
func TestCcLibraryStaticFourArchExcludeSrcs(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch specific exclude_srcs for 4 architectures",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static arch specific exclude_srcs for 4 architectures",
filesystem: map[string]string{
"common.c": "",
"for-arm.c": "",
@@ -687,32 +646,32 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- srcs_c = ["common.c"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": [
- "for-arm.c",
"not-for-arm64.c",
"not-for-x86.c",
"not-for-x86_64.c",
+ "for-arm.c",
],
"//build/bazel/platforms/arch:arm64": [
- "for-arm64.c",
"not-for-arm.c",
"not-for-x86.c",
"not-for-x86_64.c",
+ "for-arm64.c",
],
"//build/bazel/platforms/arch:x86": [
- "for-x86.c",
"not-for-arm.c",
"not-for-arm64.c",
"not-for-x86_64.c",
+ "for-x86.c",
],
"//build/bazel/platforms/arch:x86_64": [
- "for-x86_64.c",
"not-for-arm.c",
"not-for-arm64.c",
"not-for-x86.c",
+ "for-x86_64.c",
],
"//conditions:default": [
"not-for-arm.c",
@@ -720,17 +679,15 @@
"not-for-x86.c",
"not-for-x86_64.c",
],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryStaticOneArchEmpty(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static one arch empty",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static one arch empty",
filesystem: map[string]string{
"common.cc": "",
"foo-no-arm.cc": "",
@@ -746,22 +703,20 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- srcs = ["common.cc"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs": `["common.cc"] + select({
"//build/bazel/platforms/arch:arm": [],
"//conditions:default": ["foo-no-arm.cc"],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryStaticOneArchEmptyOtherSet(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static one arch empty other set",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static one arch empty other set",
filesystem: map[string]string{
"common.cc": "",
"foo-no-arm.cc": "",
@@ -779,27 +734,25 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- srcs = ["common.cc"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs": `["common.cc"] + select({
"//build/bazel/platforms/arch:arm": [],
"//build/bazel/platforms/arch:x86": [
"foo-no-arm.cc",
"x86-only.cc",
],
"//conditions:default": ["foo-no-arm.cc"],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryStaticMultipleDepSameName(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static multiple dep same name panic",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library_static multiple dep same name panic",
+ filesystem: map[string]string{},
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "static_dep",
@@ -810,19 +763,17 @@
static_libs: ["static_dep", "static_dep"],
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- implementation_deps = [":static_dep"],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "implementation_deps": `[":static_dep"]`,
+ }),
+ },
})
}
func TestCcLibraryStaticOneMultilibSrcsExcludeSrcs(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static 1 multilib srcs and exclude_srcs",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static 1 multilib srcs and exclude_srcs",
filesystem: map[string]string{
"common.c": "",
"for-lib32.c": "",
@@ -837,23 +788,21 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- srcs_c = ["common.c"] + select({
+ 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"],
"//conditions:default": ["not-for-lib32.c"],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryStaticTwoMultilibSrcsExcludeSrcs(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static 2 multilib srcs and exclude_srcs",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static 2 multilib srcs and exclude_srcs",
filesystem: map[string]string{
"common.c": "",
"for-lib32.c": "",
@@ -863,7 +812,7 @@
},
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
- name: "foo_static2",
+ name: "foo_static",
srcs: ["common.c", "not-for-*.c"],
multilib: {
lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] },
@@ -871,40 +820,38 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static2",
- srcs_c = ["common.c"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": [
- "for-lib32.c",
"not-for-lib64.c",
+ "for-lib32.c",
],
"//build/bazel/platforms/arch:arm64": [
- "for-lib64.c",
"not-for-lib32.c",
+ "for-lib64.c",
],
"//build/bazel/platforms/arch:x86": [
- "for-lib32.c",
"not-for-lib64.c",
+ "for-lib32.c",
],
"//build/bazel/platforms/arch:x86_64": [
- "for-lib64.c",
"not-for-lib32.c",
+ "for-lib64.c",
],
"//conditions:default": [
"not-for-lib32.c",
"not-for-lib64.c",
],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibrarySTaticArchMultilibSrcsExcludeSrcs(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch and multilib srcs and exclude_srcs",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static arch and multilib srcs and exclude_srcs",
filesystem: map[string]string{
"common.c": "",
"for-arm.c": "",
@@ -923,7 +870,7 @@
},
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
- name: "foo_static3",
+ name: "foo_static",
srcs: ["common.c", "not-for-*.c"],
exclude_srcs: ["not-for-everything.c"],
arch: {
@@ -938,40 +885,40 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static3",
- srcs_c = ["common.c"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs_c": `["common.c"] + select({
"//build/bazel/platforms/arch:arm": [
+ "not-for-arm64.c",
+ "not-for-lib64.c",
+ "not-for-x86.c",
+ "not-for-x86_64.c",
"for-arm.c",
"for-lib32.c",
- "not-for-arm64.c",
- "not-for-lib64.c",
- "not-for-x86.c",
- "not-for-x86_64.c",
],
"//build/bazel/platforms/arch:arm64": [
- "for-arm64.c",
- "for-lib64.c",
"not-for-arm.c",
"not-for-lib32.c",
"not-for-x86.c",
"not-for-x86_64.c",
+ "for-arm64.c",
+ "for-lib64.c",
],
"//build/bazel/platforms/arch:x86": [
- "for-lib32.c",
- "for-x86.c",
"not-for-arm.c",
"not-for-arm64.c",
"not-for-lib64.c",
"not-for-x86_64.c",
+ "for-x86.c",
+ "for-lib32.c",
],
"//build/bazel/platforms/arch:x86_64": [
- "for-lib64.c",
- "for-x86_64.c",
"not-for-arm.c",
"not-for-arm64.c",
"not-for-lib32.c",
"not-for-x86.c",
+ "for-x86_64.c",
+ "for-lib64.c",
],
"//conditions:default": [
"not-for-arm.c",
@@ -981,104 +928,137 @@
"not-for-x86.c",
"not-for-x86_64.c",
],
- }),
-)`},
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticGeneratedHeadersAllPartitions(t *testing.T) {
+ runCcLibraryStaticTestCase(t, bp2buildTestCase{
+ blueprint: soongCcLibraryStaticPreamble + `
+genrule {
+ name: "generated_hdr",
+ cmd: "nothing to see here",
+ bazel_module: { bp2build_available: false },
+}
+
+genrule {
+ name: "export_generated_hdr",
+ cmd: "nothing to see here",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+ name: "foo_static",
+ srcs: ["cpp_src.cpp", "as_src.S", "c_src.c"],
+ generated_headers: ["generated_hdr", "export_generated_hdr"],
+ export_generated_headers: ["export_generated_hdr"],
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "export_includes": `["."]`,
+ "local_includes": `["."]`,
+ "hdrs": `[":export_generated_hdr"]`,
+ "srcs": `[
+ "cpp_src.cpp",
+ ":generated_hdr",
+ ]`,
+ "srcs_as": `[
+ "as_src.S",
+ ":generated_hdr",
+ ]`,
+ "srcs_c": `[
+ "c_src.c",
+ ":generated_hdr",
+ ]`,
+ }),
+ },
})
}
func TestCcLibraryStaticArchSrcsExcludeSrcsGeneratedFiles(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch srcs/exclude_srcs with generated files",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static arch srcs/exclude_srcs with generated files",
filesystem: map[string]string{
"common.cpp": "",
"for-x86.cpp": "",
"not-for-x86.cpp": "",
"not-for-everything.cpp": "",
- "dep/Android.bp": `
-genrule {
- name: "generated_src_other_pkg",
- out: ["generated_src_other_pkg.cpp"],
- cmd: "nothing to see here",
-}
-
-genrule {
- name: "generated_hdr_other_pkg",
- out: ["generated_hdr_other_pkg.cpp"],
- cmd: "nothing to see here",
-}
-
-genrule {
- name: "generated_hdr_other_pkg_x86",
- out: ["generated_hdr_other_pkg_x86.cpp"],
- cmd: "nothing to see here",
-}`,
+ "dep/Android.bp": simpleModuleDoNotConvertBp2build("genrule", "generated_src_other_pkg") +
+ simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg") +
+ simpleModuleDoNotConvertBp2build("genrule", "generated_src_other_pkg_x86") +
+ simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg_x86") +
+ simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg_android"),
},
- blueprint: soongCcLibraryStaticPreamble + `
-genrule {
- name: "generated_src",
- out: ["generated_src.cpp"],
- cmd: "nothing to see here",
-}
-
-genrule {
- name: "generated_src_x86",
- out: ["generated_src_x86.cpp"],
- cmd: "nothing to see here",
-}
-
-genrule {
- name: "generated_hdr",
- out: ["generated_hdr.h"],
- cmd: "nothing to see here",
-}
-
+ blueprint: soongCcLibraryStaticPreamble +
+ simpleModuleDoNotConvertBp2build("genrule", "generated_src") +
+ simpleModuleDoNotConvertBp2build("genrule", "generated_src_not_x86") +
+ simpleModuleDoNotConvertBp2build("genrule", "generated_src_android") +
+ simpleModuleDoNotConvertBp2build("genrule", "generated_hdr") + `
cc_library_static {
- name: "foo_static3",
- srcs: ["common.cpp", "not-for-*.cpp"],
- exclude_srcs: ["not-for-everything.cpp"],
- generated_sources: ["generated_src", "generated_src_other_pkg"],
- generated_headers: ["generated_hdr", "generated_hdr_other_pkg"],
- arch: {
- x86: {
- srcs: ["for-x86.cpp"],
- exclude_srcs: ["not-for-x86.cpp"],
- generated_sources: ["generated_src_x86"],
- generated_headers: ["generated_hdr_other_pkg_x86"],
- },
- },
+ name: "foo_static",
+ srcs: ["common.cpp", "not-for-*.cpp"],
+ exclude_srcs: ["not-for-everything.cpp"],
+ generated_sources: ["generated_src", "generated_src_other_pkg", "generated_src_not_x86"],
+ generated_headers: ["generated_hdr", "generated_hdr_other_pkg"],
+ export_generated_headers: ["generated_hdr_other_pkg"],
+ arch: {
+ x86: {
+ srcs: ["for-x86.cpp"],
+ exclude_srcs: ["not-for-x86.cpp"],
+ generated_headers: ["generated_hdr_other_pkg_x86"],
+ exclude_generated_sources: ["generated_src_not_x86"],
+ export_generated_headers: ["generated_hdr_other_pkg_x86"],
+ },
+ },
+ target: {
+ android: {
+ generated_sources: ["generated_src_android"],
+ generated_headers: ["generated_hdr_other_pkg_android"],
+ export_generated_headers: ["generated_hdr_other_pkg_android"],
+ },
+ },
+
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static3",
- srcs = [
- "//dep:generated_hdr_other_pkg",
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs": `[
+ "common.cpp",
+ ":generated_src",
"//dep:generated_src_other_pkg",
":generated_hdr",
- ":generated_src",
- "common.cpp",
] + select({
- "//build/bazel/platforms/arch:x86": [
- "//dep:generated_hdr_other_pkg_x86",
- ":generated_src_x86",
- "for-x86.cpp",
+ "//build/bazel/platforms/arch:x86": ["for-x86.cpp"],
+ "//conditions:default": [
+ "not-for-x86.cpp",
+ ":generated_src_not_x86",
],
- "//conditions:default": ["not-for-x86.cpp"],
- }),
-)`},
+ }) + select({
+ "//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({
+ "//build/bazel/platforms/os:android": ["//dep:generated_hdr_other_pkg_android"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ "export_absolute_includes": `["dep"]`,
+ }),
+ },
})
}
func TestCcLibraryStaticGetTargetProperties(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static complex GetTargetProperties",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static complex GetTargetProperties",
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
@@ -1107,9 +1087,9 @@
},
include_build_directory: false,
}`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- srcs_c = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "srcs_c": `select({
"//build/bazel/platforms/os:android": ["android_src.c"],
"//conditions:default": [],
}) + select({
@@ -1120,17 +1100,15 @@
"//build/bazel/platforms/os_arch:linux_bionic_arm64": ["linux_bionic_arm64_src.c"],
"//build/bazel/platforms/os_arch:linux_bionic_x86_64": ["linux_bionic_x86_64_src.c"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestCcLibraryStaticProductVariableSelects(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static product variable selects",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static product variable selects",
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
@@ -1148,9 +1126,9 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- copts = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "copts": `select({
"//build/bazel/product_variables:binder32bit": ["-Wbinder32bit"],
"//conditions:default": [],
}) + select({
@@ -1159,19 +1137,17 @@
}) + select({
"//build/bazel/product_variables:malloc_zero_contents": ["-Wmalloc_zero_contents"],
"//conditions:default": [],
- }),
- srcs_c = ["common.c"],
-)`},
+ })`,
+ "srcs_c": `["common.c"]`,
+ }),
+ },
})
}
func TestCcLibraryStaticProductVariableArchSpecificSelects(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static arch-specific product variable selects",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library_static arch-specific product variable selects",
+ filesystem: map[string]string{},
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
@@ -1210,9 +1186,9 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- copts = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
+ "copts": `select({
"//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
"//conditions:default": [],
}) + select({
@@ -1227,19 +1203,17 @@
}) + select({
"//build/bazel/product_variables:malloc_not_svelte-x86": ["-Wlib32_malloc_not_svelte"],
"//conditions:default": [],
- }),
- srcs_c = ["common.c"],
-)`},
+ })`,
+ "srcs_c": `["common.c"]`,
+ }),
+ },
})
}
func TestCcLibraryStaticProductVariableStringReplacement(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static product variable string replacement",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
- filesystem: map[string]string{},
+ description: "cc_library_static product variable string replacement",
+ filesystem: map[string]string{},
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "foo_static",
@@ -1251,23 +1225,21 @@
},
include_build_directory: false,
} `,
- expectedBazelTargets: []string{`cc_library_static(
- name = "foo_static",
- asflags = select({
+ 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": [],
- }),
- srcs_as = ["common.S"],
-)`},
+ })`,
+ "srcs_as": `["common.S"]`,
+ }),
+ },
})
}
func TestStaticLibrary_SystemSharedLibsRootEmpty(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_lib empty root",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static system_shared_lib empty root",
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "root_empty",
@@ -1275,19 +1247,17 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "root_empty",
- system_dynamic_deps = [],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "root_empty", attrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ },
})
}
func TestStaticLibrary_SystemSharedLibsStaticEmpty(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_lib empty static default",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static system_shared_lib empty static default",
blueprint: soongCcLibraryStaticPreamble + `
cc_defaults {
name: "static_empty_defaults",
@@ -1301,19 +1271,17 @@
defaults: ["static_empty_defaults"],
}
`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "static_empty",
- system_dynamic_deps = [],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "static_empty", attrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ },
})
}
func TestStaticLibrary_SystemSharedLibsBionicEmpty(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_lib empty for bionic variant",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static system_shared_lib empty for bionic variant",
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "target_bionic_empty",
@@ -1325,10 +1293,11 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "target_bionic_empty",
- system_dynamic_deps = [],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "target_bionic_empty", attrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ },
})
}
@@ -1338,10 +1307,7 @@
// 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",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ description: "cc_library_static system_shared_lib empty for linux_bionic variant",
blueprint: soongCcLibraryStaticPreamble + `
cc_library_static {
name: "target_linux_bionic_empty",
@@ -1353,22 +1319,19 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "target_linux_bionic_empty",
- system_dynamic_deps = [],
-)`},
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "target_linux_bionic_empty", attrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ },
})
}
func TestStaticLibrary_SystemSharedLibsBionic(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_libs set for bionic variant",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
- blueprint: soongCcLibraryStaticPreamble + `
-cc_library{name: "libc"}
-
+ description: "cc_library_static system_shared_libs set for bionic variant",
+ blueprint: soongCcLibraryStaticPreamble +
+ simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
cc_library_static {
name: "target_bionic",
target: {
@@ -1379,27 +1342,24 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "target_bionic",
- system_dynamic_deps = select({
+ 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"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
})
}
func TestStaticLibrary_SystemSharedLibsLinuxRootAndLinuxBionic(t *testing.T) {
runCcLibraryStaticTestCase(t, bp2buildTestCase{
- description: "cc_library_static system_shared_libs set for root and linux_bionic variant",
- moduleTypeUnderTest: "cc_library_static",
- moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
- blueprint: soongCcLibraryStaticPreamble + `
-cc_library{name: "libc"}
-cc_library{name: "libm"}
-
+ 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_static {
name: "target_linux_bionic",
system_shared_libs: ["libc"],
@@ -1411,12 +1371,117 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_library_static(
- name = "target_linux_bionic",
- system_dynamic_deps = [":libc"] + select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "target_linux_bionic", attrNameToString{
+ "system_dynamic_deps": `[":libc"] + select({
"//build/bazel/platforms/os:linux_bionic": [":libm"],
"//conditions:default": [],
- }),
-)`},
+ })`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarystatic_SystemSharedLibUsedAsDep(t *testing.T) {
+ runCcLibraryStaticTestCase(t, bp2buildTestCase{
+ description: "cc_library_static system_shared_lib empty for linux_bionic variant",
+ blueprint: soongCcLibraryStaticPreamble +
+ simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+cc_library_static {
+ name: "used_in_bionic_oses",
+ target: {
+ android: {
+ shared_libs: ["libc"],
+ },
+ linux_bionic: {
+ shared_libs: ["libc"],
+ },
+ },
+ include_build_directory: false,
+}
+
+cc_library_static {
+ name: "all",
+ shared_libs: ["libc"],
+ include_build_directory: false,
+}
+
+cc_library_static {
+ name: "keep_for_empty_system_shared_libs",
+ shared_libs: ["libc"],
+ system_shared_libs: [],
+ include_build_directory: false,
+}
+`,
+ 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"],
+ })`,
+ }),
+ makeBazelTarget("cc_library_static", "keep_for_empty_system_shared_libs", attrNameToString{
+ "implementation_dynamic_deps": `[":libc"]`,
+ "system_dynamic_deps": `[]`,
+ }),
+ makeBazelTarget("cc_library_static", "used_in_bionic_oses", attrNameToString{}),
+ },
+ })
+}
+
+func TestCcLibraryStaticProto(t *testing.T) {
+ runCcLibraryStaticTestCase(t, bp2buildTestCase{
+ blueprint: soongCcProtoPreamble + `cc_library_static {
+ name: "foo",
+ srcs: ["foo.proto"],
+ proto: {
+ export_proto_headers: true,
+ },
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+ "srcs": `["foo.proto"]`,
+ }), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+ "deps": `[":foo_proto"]`,
+ }), makeBazelTarget("cc_library_static", "foo", attrNameToString{
+ "deps": `[":libprotobuf-cpp-lite"]`,
+ "whole_archive_deps": `[":foo_cc_proto_lite"]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticUseVersionLib(t *testing.T) {
+ runCcLibraryStaticTestCase(t, bp2buildTestCase{
+ blueprint: soongCcProtoPreamble + `cc_library_static {
+ name: "foo",
+ use_version_lib: true,
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_library_static", "foo", attrNameToString{
+ "use_version_lib": "True",
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticStdInFlags(t *testing.T) {
+ 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{
+ "conlyflags": `["-std=conly"]`,
+ "cppflags": `["-std=cpp"]`,
+ }),
+ },
})
}
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 63a0b8a..0a6c317 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -28,15 +28,14 @@
func runCcObjectTestCase(t *testing.T, tc bp2buildTestCase) {
t.Helper()
+ (&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",
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ description: "simple cc_object generates cc_object with include header dep",
filesystem: map[string]string{
"a/b/foo.h": "",
"a/b/bar.h": "",
@@ -58,30 +57,27 @@
exclude_srcs: ["a/b/exclude.c"],
}
`,
- expectedBazelTargets: []string{`cc_object(
- name = "foo",
- copts = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_object", "foo", attrNameToString{
+ "copts": `[
"-fno-addrsig",
"-Wno-gcc-compat",
"-Wall",
"-Werror",
- ],
- local_includes = [
+ ]`,
+ "local_includes": `[
"include",
".",
- ],
- srcs = ["a/b/c.c"],
- system_dynamic_deps = [],
-)`,
+ ]`,
+ "srcs": `["a/b/c.c"]`,
+ "system_dynamic_deps": `[]`,
+ }),
},
})
}
func TestCcObjectDefaults(t *testing.T) {
runCcObjectTestCase(t, bp2buildTestCase{
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
@@ -101,33 +97,26 @@
cc_defaults {
name: "foo_bar_defaults",
cflags: [
- "-Wno-gcc-compat",
- "-Wall",
"-Werror",
],
}
`,
- expectedBazelTargets: []string{`cc_object(
- name = "foo",
- copts = [
- "-Wno-gcc-compat",
- "-Wall",
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_object", "foo", attrNameToString{
+ "copts": `[
"-Werror",
"-fno-addrsig",
- ],
- local_includes = ["."],
- srcs = ["a/b/c.c"],
- system_dynamic_deps = [],
-)`,
+ ]`,
+ "local_includes": `["."]`,
+ "srcs": `["a/b/c.c"]`,
+ "system_dynamic_deps": `[]`,
+ }),
}})
}
func TestCcObjectCcObjetDepsInObjs(t *testing.T) {
runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object with cc_object deps in objs props",
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ description: "cc_object with cc_object deps in objs props",
filesystem: map[string]string{
"a/b/c.c": "",
"x/y/z.c": "",
@@ -147,28 +136,24 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_object(
- name = "bar",
- copts = ["-fno-addrsig"],
- srcs = ["x/y/z.c"],
- system_dynamic_deps = [],
-)`, `cc_object(
- name = "foo",
- copts = ["-fno-addrsig"],
- deps = [":bar"],
- srcs = ["a/b/c.c"],
- system_dynamic_deps = [],
-)`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_object", "bar", attrNameToString{
+ "copts": `["-fno-addrsig"]`,
+ "srcs": `["x/y/z.c"]`,
+ "system_dynamic_deps": `[]`,
+ }), makeBazelTarget("cc_object", "foo", attrNameToString{
+ "copts": `["-fno-addrsig"]`,
+ "deps": `[":bar"]`,
+ "srcs": `["a/b/c.c"]`,
+ "system_dynamic_deps": `[]`,
+ }),
},
})
}
func TestCcObjectIncludeBuildDirFalse(t *testing.T) {
runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object with include_build_dir: false",
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ description: "cc_object with include_build_dir: false",
filesystem: map[string]string{
"a/b/c.c": "",
"x/y/z.c": "",
@@ -180,22 +165,19 @@
include_build_directory: false,
}
`,
- expectedBazelTargets: []string{`cc_object(
- name = "foo",
- copts = ["-fno-addrsig"],
- srcs = ["a/b/c.c"],
- system_dynamic_deps = [],
-)`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_object", "foo", attrNameToString{
+ "copts": `["-fno-addrsig"]`,
+ "srcs": `["a/b/c.c"]`,
+ "system_dynamic_deps": `[]`,
+ }),
},
})
}
func TestCcObjectProductVariable(t *testing.T) {
runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object with product variable",
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ description: "cc_object with product variable",
blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
@@ -208,26 +190,23 @@
srcs: ["src.S"],
}
`,
- expectedBazelTargets: []string{`cc_object(
- name = "foo",
- asflags = select({
+ expectedBazelTargets: []string{
+ makeBazelTarget("cc_object", "foo", attrNameToString{
+ "asflags": `select({
"//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
"//conditions:default": [],
- }),
- copts = ["-fno-addrsig"],
- srcs_as = ["src.S"],
- system_dynamic_deps = [],
-)`,
+ })`,
+ "copts": `["-fno-addrsig"]`,
+ "srcs_as": `["src.S"]`,
+ "system_dynamic_deps": `[]`,
+ }),
},
})
}
func TestCcObjectCflagsOneArch(t *testing.T) {
runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting cflags for one arch",
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ description: "cc_object setting cflags for one arch",
blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
@@ -244,28 +223,24 @@
}
`,
expectedBazelTargets: []string{
- `cc_object(
- name = "foo",
- copts = ["-fno-addrsig"] + select({
+ makeBazelTarget("cc_object", "foo", attrNameToString{
+ "copts": `["-fno-addrsig"] + select({
"//build/bazel/platforms/arch:x86": ["-fPIC"],
"//conditions:default": [],
- }),
- srcs = ["a.cpp"] + select({
+ })`,
+ "srcs": `["a.cpp"] + select({
"//build/bazel/platforms/arch:arm": ["arch/arm/file.cpp"],
"//conditions:default": [],
- }),
- system_dynamic_deps = [],
-)`,
+ })`,
+ "system_dynamic_deps": `[]`,
+ }),
},
})
}
func TestCcObjectCflagsFourArch(t *testing.T) {
runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting cflags for 4 architectures",
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ description: "cc_object setting cflags for 4 architectures",
blueprint: `cc_object {
name: "foo",
system_shared_libs: [],
@@ -292,34 +267,30 @@
}
`,
expectedBazelTargets: []string{
- `cc_object(
- name = "foo",
- copts = ["-fno-addrsig"] + select({
+ makeBazelTarget("cc_object", "foo", attrNameToString{
+ "copts": `["-fno-addrsig"] + select({
"//build/bazel/platforms/arch:arm": ["-Wall"],
"//build/bazel/platforms/arch:arm64": ["-Wall"],
"//build/bazel/platforms/arch:x86": ["-fPIC"],
"//build/bazel/platforms/arch:x86_64": ["-fPIC"],
"//conditions:default": [],
- }),
- srcs = ["base.cpp"] + select({
+ })`,
+ "srcs": `["base.cpp"] + select({
"//build/bazel/platforms/arch:arm": ["arm.cpp"],
"//build/bazel/platforms/arch:arm64": ["arm64.cpp"],
"//build/bazel/platforms/arch:x86": ["x86.cpp"],
"//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"],
"//conditions:default": [],
- }),
- system_dynamic_deps = [],
-)`,
+ })`,
+ "system_dynamic_deps": `[]`,
+ }),
},
})
}
func TestCcObjectLinkerScript(t *testing.T) {
runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting linker_script",
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ description: "cc_object setting linker_script",
blueprint: `cc_object {
name: "foo",
srcs: ["base.cpp"],
@@ -328,22 +299,18 @@
}
`,
expectedBazelTargets: []string{
- `cc_object(
- name = "foo",
- copts = ["-fno-addrsig"],
- linker_script = "bunny.lds",
- srcs = ["base.cpp"],
-)`,
+ makeBazelTarget("cc_object", "foo", attrNameToString{
+ "copts": `["-fno-addrsig"]`,
+ "linker_script": `"bunny.lds"`,
+ "srcs": `["base.cpp"]`,
+ }),
},
})
}
func TestCcObjectDepsAndLinkerScriptSelects(t *testing.T) {
runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting deps and linker_script across archs",
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ description: "cc_object setting deps and linker_script across archs",
blueprint: `cc_object {
name: "foo",
srcs: ["base.cpp"],
@@ -389,33 +356,29 @@
}
`,
expectedBazelTargets: []string{
- `cc_object(
- name = "foo",
- copts = ["-fno-addrsig"],
- deps = select({
+ makeBazelTarget("cc_object", "foo", attrNameToString{
+ "copts": `["-fno-addrsig"]`,
+ "deps": `select({
"//build/bazel/platforms/arch:arm": [":arm_obj"],
"//build/bazel/platforms/arch:x86": [":x86_obj"],
"//build/bazel/platforms/arch:x86_64": [":x86_64_obj"],
"//conditions:default": [],
- }),
- linker_script = select({
+ })`,
+ "linker_script": `select({
"//build/bazel/platforms/arch:arm": "arm.lds",
"//build/bazel/platforms/arch:x86": "x86.lds",
"//build/bazel/platforms/arch:x86_64": "x86_64.lds",
"//conditions:default": None,
- }),
- srcs = ["base.cpp"],
-)`,
+ })`,
+ "srcs": `["base.cpp"]`,
+ }),
},
})
}
func TestCcObjectSelectOnLinuxAndBionicArchs(t *testing.T) {
runCcObjectTestCase(t, bp2buildTestCase{
- description: "cc_object setting srcs based on linux and bionic archs",
- moduleTypeUnderTest: "cc_object",
- moduleTypeUnderTestFactory: cc.ObjectFactory,
- moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ description: "cc_object setting srcs based on linux and bionic archs",
blueprint: `cc_object {
name: "foo",
srcs: ["base.cpp"],
@@ -434,24 +397,23 @@
}
`,
expectedBazelTargets: []string{
- `cc_object(
- name = "foo",
- copts = ["-fno-addrsig"],
- srcs = ["base.cpp"] + select({
+ makeBazelTarget("cc_object", "foo", attrNameToString{
+ "copts": `["-fno-addrsig"]`,
+ "srcs": `["base.cpp"] + select({
"//build/bazel/platforms/os_arch:android_arm64": [
- "bionic_arm64.cpp",
"linux_arm64.cpp",
+ "bionic_arm64.cpp",
],
"//build/bazel/platforms/os_arch:android_x86": ["linux_x86.cpp"],
"//build/bazel/platforms/os_arch:linux_bionic_arm64": [
- "bionic_arm64.cpp",
"linux_arm64.cpp",
+ "bionic_arm64.cpp",
],
"//build/bazel/platforms/os_arch:linux_glibc_x86": ["linux_x86.cpp"],
"//build/bazel/platforms/os_arch:linux_musl_x86": ["linux_x86.cpp"],
"//conditions:default": [],
- }),
-)`,
+ })`,
+ }),
},
})
}
diff --git a/bp2build/cc_prebuilt_library_shared_test.go b/bp2build/cc_prebuilt_library_shared_test.go
new file mode 100644
index 0000000..ef2fddc
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_shared_test.go
@@ -0,0 +1,61 @@
+package bp2build
+
+import (
+ "testing"
+
+ "android/soong/cc"
+)
+
+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{
+ "libf.so": "",
+ },
+ blueprint: `
+cc_prebuilt_library_shared {
+ name: "libtest",
+ srcs: ["libf.so"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+ "shared_library": `"libf.so"`,
+ }),
+ },
+ })
+}
+
+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{
+ "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("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,
+ })`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 005f13d..d37a523 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -1,10 +1,12 @@
package bp2build
import (
- "android/soong/android"
- "android/soong/bazel"
"fmt"
"reflect"
+
+ "android/soong/android"
+ "android/soong/bazel"
+ "android/soong/starlark_fmt"
)
// Configurability support for bp2build.
@@ -48,6 +50,12 @@
}
}
+ // if there is a select, use the base value as the conditions default value
+ if len(ret) > 0 {
+ ret[bazel.ConditionsDefaultSelectKey] = value
+ value = reflect.Zero(value.Type())
+ }
+
return value, []selects{ret}
}
@@ -83,13 +91,15 @@
}
archSelects := map[string]reflect.Value{}
defaultVal := configToLabels[bazel.ConditionsDefaultConfigKey]
+ // Skip empty list values unless ether EmitEmptyList is true, or these values differ from the default.
+ emitEmptyList := list.EmitEmptyList || len(defaultVal.Includes) > 0
for config, labels := range configToLabels {
// Omit any entries in the map which match the default value, for brevity.
if config != bazel.ConditionsDefaultConfigKey && labels.Equals(defaultVal) {
continue
}
selectKey := axis.SelectKey(config)
- if use, value := labelListSelectValue(selectKey, labels); use {
+ if use, value := labelListSelectValue(selectKey, labels, emitEmptyList); use {
archSelects[selectKey] = value
}
}
@@ -101,8 +111,8 @@
return value, ret
}
-func labelListSelectValue(selectKey string, list bazel.LabelList) (bool, reflect.Value) {
- if selectKey == bazel.ConditionsDefaultSelectKey || len(list.Includes) > 0 {
+func labelListSelectValue(selectKey string, list bazel.LabelList, emitEmptyList bool) (bool, reflect.Value) {
+ if selectKey == bazel.ConditionsDefaultSelectKey || emitEmptyList || len(list.Includes) > 0 {
return true, reflect.ValueOf(list.Includes)
} else if len(list.Excludes) > 0 {
// if there is still an excludes -- we need to have an empty list for this select & use the
@@ -123,6 +133,7 @@
var value reflect.Value
var configurableAttrs []selects
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) {
@@ -131,14 +142,21 @@
defaultSelectValue = &emptyBazelList
case bazel.LabelListAttribute:
value, configurableAttrs = getLabelListValues(list)
+ emitZeroValues = list.EmitEmptyList
defaultSelectValue = &emptyBazelList
if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) {
shouldPrintDefault = true
}
case bazel.LabelAttribute:
+ if err := list.Collapse(); err != nil {
+ return "", err
+ }
value, configurableAttrs = getLabelValue(list)
defaultSelectValue = &bazelNone
case bazel.BoolAttribute:
+ if err := list.Collapse(); err != nil {
+ return "", err
+ }
value, configurableAttrs = getBoolValue(list)
defaultSelectValue = &bazelNone
default:
@@ -148,7 +166,7 @@
var err error
ret := ""
if value.Kind() != reflect.Invalid {
- s, err := prettyPrint(value, indent)
+ s, err := prettyPrint(value, indent, false) // never emit zero values for the base value
if err != nil {
return ret, err
}
@@ -157,7 +175,7 @@
}
// Convenience function to append selects components to an attribute value.
appendSelects := func(selectsData selects, defaultValue *string, s string) (string, error) {
- selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent)
+ selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent, emitZeroValues)
if err != nil {
return "", err
}
@@ -184,7 +202,7 @@
// prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
// to construct a select map for any kind of attribute type.
-func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int) (string, error) {
+func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int, emitZeroValues bool) (string, error) {
if selectMap == nil {
return "", nil
}
@@ -196,11 +214,12 @@
continue
}
value := selectMap[selectKey]
- if isZero(value) {
- // Ignore zero values to not generate empty lists.
+ if isZero(value) && !emitZeroValues && isZero(selectMap[bazel.ConditionsDefaultSelectKey]) {
+ // Ignore zero values to not generate empty lists. However, always note zero values if
+ // the default value is non-zero.
continue
}
- s, err := prettyPrintSelectEntry(value, selectKey, indent)
+ s, err := prettyPrintSelectEntry(value, selectKey, indent, true)
if err != nil {
return "", err
}
@@ -221,7 +240,7 @@
ret += selects
// Handle the default condition
- s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent)
+ s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent, emitZeroValues)
if err != nil {
return "", err
}
@@ -232,10 +251,10 @@
} else if defaultValue != nil {
// Print an explicit empty list (the default value) even if the value is
// empty, to avoid errors about not finding a configuration that matches.
- ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
+ ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
}
- ret += makeIndent(indent)
+ ret += starlark_fmt.Indention(indent)
ret += "})"
return ret, nil
@@ -243,9 +262,9 @@
// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
// with a provided key.
-func prettyPrintSelectEntry(value reflect.Value, key string, indent int) (string, error) {
- s := makeIndent(indent + 1)
- v, err := prettyPrint(value, indent+1)
+func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
+ s := starlark_fmt.Indention(indent + 1)
+ v, err := prettyPrint(value, indent+1, emitZeroValues)
if err != nil {
return "", err
}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index d34a4ba..96c12d3 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -1,6 +1,7 @@
package bp2build
import (
+ "encoding/json"
"fmt"
"reflect"
"strings"
@@ -17,14 +18,23 @@
Contents string
}
-func CreateSoongInjectionFiles(metrics CodegenMetrics) []BazelFile {
+func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []BazelFile {
var files []BazelFile
files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
- files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars()))
+ files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars(cfg)))
files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
+ files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
+
+ apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
+ if err != nil {
+ panic(err)
+ }
+ files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
+ files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
+
return files
}
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index dfa1a9e..629ca9b 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -17,6 +17,8 @@
import (
"sort"
"testing"
+
+ "android/soong/android"
)
type bazelFilepath struct {
@@ -80,7 +82,8 @@
}
func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
- files := CreateSoongInjectionFiles(CodegenMetrics{})
+ testConfig := android.TestConfig("", make(map[string]string), "", make(map[string][]byte))
+ files := CreateSoongInjectionFiles(testConfig, CodegenMetrics{})
expectedFilePaths := []bazelFilepath{
{
@@ -95,6 +98,18 @@
dir: "metrics",
basename: "converted_modules.txt",
},
+ {
+ dir: "product_config",
+ basename: "soong_config_variables.bzl",
+ },
+ {
+ dir: "api_levels",
+ basename: GeneratedBuildFileName,
+ },
+ {
+ dir: "api_levels",
+ basename: "api_levels.json",
+ },
}
if len(files) != len(expectedFilePaths) {
diff --git a/bp2build/filegroup_conversion_test.go b/bp2build/filegroup_conversion_test.go
index ad99236..b43cf53 100644
--- a/bp2build/filegroup_conversion_test.go
+++ b/bp2build/filegroup_conversion_test.go
@@ -23,6 +23,8 @@
func runFilegroupTestCase(t *testing.T, tc bp2buildTestCase) {
t.Helper()
+ (&tc).moduleTypeUnderTest = "filegroup"
+ (&tc).moduleTypeUnderTestFactory = android.FileGroupFactory
runBp2BuildTestCase(t, registerFilegroupModuleTypes, tc)
}
@@ -30,11 +32,8 @@
func TestFilegroupSameNameAsFile_OneFile(t *testing.T) {
runFilegroupTestCase(t, bp2buildTestCase{
- description: "filegroup - same name as file, with one file",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- filesystem: map[string]string{},
+ description: "filegroup - same name as file, with one file",
+ filesystem: map[string]string{},
blueprint: `
filegroup {
name: "foo",
@@ -46,11 +45,8 @@
func TestFilegroupSameNameAsFile_MultipleFiles(t *testing.T) {
runFilegroupTestCase(t, bp2buildTestCase{
- description: "filegroup - same name as file, with multiple files",
- moduleTypeUnderTest: "filegroup",
- moduleTypeUnderTestFactory: android.FileGroupFactory,
- moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- filesystem: map[string]string{},
+ description: "filegroup - same name as file, with multiple files",
+ filesystem: map[string]string{},
blueprint: `
filegroup {
name: "foo",
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index f3bc1ba..9244b99 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -16,71 +16,141 @@
import (
"android/soong/android"
+ "android/soong/cc"
"android/soong/genrule"
- "strings"
+ "android/soong/java"
+ "fmt"
"testing"
)
-func TestGenruleBp2Build(t *testing.T) {
- otherGenruleBp := map[string]string{
- "other/Android.bp": `genrule {
+func registerGenruleModuleTypes(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("genrule_defaults", func() android.Module { return genrule.DefaultsFactory() })
+}
+
+func runGenruleTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ (&tc).moduleTypeUnderTest = "genrule"
+ (&tc).moduleTypeUnderTestFactory = genrule.GenRuleFactory
+ runBp2BuildTestCase(t, registerGenruleModuleTypes, tc)
+}
+
+func otherGenruleBp(genruleTarget string) map[string]string {
+ return map[string]string{
+ "other/Android.bp": fmt.Sprintf(`%s {
name: "foo.tool",
out: ["foo_tool.out"],
srcs: ["foo_tool.in"],
cmd: "cp $(in) $(out)",
}
-genrule {
+%s {
name: "other.tool",
out: ["other_tool.out"],
srcs: ["other_tool.in"],
cmd: "cp $(in) $(out)",
-}`,
+}`, genruleTarget, genruleTarget),
+ }
+}
+
+func TestGenruleCliVariableReplacement(t *testing.T) {
+ testCases := []struct {
+ moduleType string
+ factory android.ModuleFactory
+ genDir string
+ }{
+ {
+ moduleType: "genrule",
+ factory: genrule.GenRuleFactory,
+ genDir: "$(GENDIR)",
+ },
+ {
+ moduleType: "cc_genrule",
+ factory: cc.GenRuleFactory,
+ genDir: "$(RULEDIR)",
+ },
+ {
+ moduleType: "java_genrule",
+ factory: java.GenRuleFactory,
+ genDir: "$(RULEDIR)",
+ },
+ {
+ moduleType: "java_genrule_host",
+ factory: java.GenRuleFactoryHost,
+ genDir: "$(RULEDIR)",
+ },
}
- testCases := []bp2buildTestCase{
- {
- description: "genrule with command line variable replacements",
- moduleTypeUnderTest: "genrule",
- moduleTypeUnderTestFactory: genrule.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
- blueprint: `genrule {
+ bp := `%s {
name: "foo.tool",
out: ["foo_tool.out"],
srcs: ["foo_tool.in"],
cmd: "cp $(in) $(out)",
- bazel_module: { bp2build_available: true },
+ bazel_module: { bp2build_available: false },
}
-genrule {
+%s {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
tools: [":foo.tool"],
cmd: "$(location :foo.tool) --genDir=$(genDir) arg $(in) $(out)",
bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{
- `genrule(
- name = "foo",
- cmd = "$(location :foo.tool) --genDir=$(GENDIR) arg $(SRCS) $(OUTS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = [":foo.tool"],
-)`,
- `genrule(
- name = "foo.tool",
- cmd = "cp $(SRCS) $(OUTS)",
- outs = ["foo_tool.out"],
- srcs = ["foo_tool.in"],
-)`,
- },
+}`
+
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
+ "cmd": fmt.Sprintf(`"$(location :foo.tool) --genDir=%s arg $(SRCS) $(OUTS)"`, tc.genDir),
+ "outs": `["foo.out"]`,
+ "srcs": `["foo.in"]`,
+ "tools": `[":foo.tool"]`,
+ }
+
+ 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),
+ }
+
+ 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,
+ })
+ })
+ }
+}
+
+func TestGenruleLocationsLabel(t *testing.T) {
+ testCases := []struct {
+ moduleType string
+ factory android.ModuleFactory
+ }{
+ {
+ moduleType: "genrule",
+ factory: genrule.GenRuleFactory,
},
{
- description: "genrule using $(locations :label)",
- moduleTypeUnderTest: "genrule",
- moduleTypeUnderTestFactory: genrule.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
- blueprint: `genrule {
+ moduleType: "cc_genrule",
+ factory: cc.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule",
+ factory: java.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule_host",
+ factory: java.GenRuleFactoryHost,
+ },
+ }
+
+ bp := `%s {
name: "foo.tools",
out: ["foo_tool.out", "foo_tool2.out"],
srcs: ["foo_tool.in"],
@@ -88,221 +158,385 @@
bazel_module: { bp2build_available: true },
}
-genrule {
+%s {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
tools: [":foo.tools"],
cmd: "$(locations :foo.tools) -s $(out) $(in)",
bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(locations :foo.tools) -s $(OUTS) $(SRCS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = [":foo.tools"],
-)`,
- `genrule(
- name = "foo.tools",
- cmd = "cp $(SRCS) $(OUTS)",
- outs = [
+}`
+
+ for _, tc := range testCases {
+ fooAttrs := attrNameToString{
+ "cmd": `"$(locations :foo.tools) -s $(OUTS) $(SRCS)"`,
+ "outs": `["foo.out"]`,
+ "srcs": `["foo.in"]`,
+ "tools": `[":foo.tools"]`,
+ }
+ fooToolsAttrs := attrNameToString{
+ "cmd": `"cp $(SRCS) $(OUTS)"`,
+ "outs": `[
"foo_tool.out",
"foo_tool2.out",
- ],
- srcs = ["foo_tool.in"],
-)`,
- },
+ ]`,
+ "srcs": `["foo_tool.in"]`,
+ }
+
+ 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),
+ }
+
+ 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,
+ })
+ })
+ }
+}
+
+func TestGenruleLocationsAbsoluteLabel(t *testing.T) {
+ testCases := []struct {
+ moduleType string
+ factory android.ModuleFactory
+ }{
+ {
+ moduleType: "genrule",
+ factory: genrule.GenRuleFactory,
},
{
- description: "genrule using $(locations //absolute:label)",
- moduleTypeUnderTest: "genrule",
- moduleTypeUnderTestFactory: genrule.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
- blueprint: `genrule {
+ moduleType: "cc_genrule",
+ factory: cc.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule",
+ factory: java.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule_host",
+ factory: java.GenRuleFactoryHost,
+ },
+ }
+
+ bp := `%s {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
tool_files: [":foo.tool"],
cmd: "$(locations :foo.tool) -s $(out) $(in)",
bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = ["//other:foo.tool"],
-)`,
- },
- filesystem: otherGenruleBp,
+}`
+
+ for _, tc := range testCases {
+ 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),
+ }
+
+ 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),
+ })
+ })
+ }
+}
+
+func TestGenruleSrcsLocationsAbsoluteLabel(t *testing.T) {
+ testCases := []struct {
+ moduleType string
+ factory android.ModuleFactory
+ }{
+ {
+ moduleType: "genrule",
+ factory: genrule.GenRuleFactory,
},
{
- description: "genrule srcs using $(locations //absolute:label)",
- moduleTypeUnderTest: "genrule",
- moduleTypeUnderTestFactory: genrule.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
- blueprint: `genrule {
+ moduleType: "cc_genrule",
+ factory: cc.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule",
+ factory: java.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule_host",
+ factory: java.GenRuleFactoryHost,
+ },
+ }
+
+ bp := `%s {
name: "foo",
out: ["foo.out"],
srcs: [":other.tool"],
tool_files: [":foo.tool"],
cmd: "$(locations :foo.tool) -s $(out) $(location :other.tool)",
bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)",
- outs = ["foo.out"],
- srcs = ["//other:other.tool"],
- tools = ["//other:foo.tool"],
-)`,
- },
- filesystem: otherGenruleBp,
+}`
+
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
+ "cmd": `"$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)"`,
+ "outs": `["foo.out"]`,
+ "srcs": `["//other:other.tool"]`,
+ "tools": `["//other:foo.tool"]`,
+ }
+
+ 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),
+ }
+
+ 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),
+ })
+ })
+ }
+}
+
+func TestGenruleLocationLabelShouldSubstituteFirstToolLabel(t *testing.T) {
+ testCases := []struct {
+ moduleType string
+ factory android.ModuleFactory
+ }{
+ {
+ moduleType: "genrule",
+ factory: genrule.GenRuleFactory,
},
{
- description: "genrule using $(location) label should substitute first tool label automatically",
- moduleTypeUnderTest: "genrule",
- moduleTypeUnderTestFactory: genrule.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
- blueprint: `genrule {
+ moduleType: "cc_genrule",
+ factory: cc.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule",
+ factory: java.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule_host",
+ factory: java.GenRuleFactoryHost,
+ },
+ }
+
+ bp := `%s {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
tool_files: [":foo.tool", ":other.tool"],
cmd: "$(location) -s $(out) $(in)",
bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(location //other:foo.tool) -s $(OUTS) $(SRCS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = [
+}`
+
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
+ "cmd": `"$(location //other:foo.tool) -s $(OUTS) $(SRCS)"`,
+ "outs": `["foo.out"]`,
+ "srcs": `["foo.in"]`,
+ "tools": `[
"//other:foo.tool",
"//other:other.tool",
- ],
-)`,
- },
- filesystem: otherGenruleBp,
+ ]`,
+ }
+
+ 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),
+ }
+
+ 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),
+ })
+ })
+ }
+}
+
+func TestGenruleLocationsLabelShouldSubstituteFirstToolLabel(t *testing.T) {
+ testCases := []struct {
+ moduleType string
+ factory android.ModuleFactory
+ }{
+ {
+ moduleType: "genrule",
+ factory: genrule.GenRuleFactory,
},
{
- description: "genrule using $(locations) label should substitute first tool label automatically",
- moduleTypeUnderTest: "genrule",
- moduleTypeUnderTestFactory: genrule.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
- blueprint: `genrule {
+ moduleType: "cc_genrule",
+ factory: cc.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule",
+ factory: java.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule_host",
+ factory: java.GenRuleFactoryHost,
+ },
+ }
+
+ bp := `%s {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
- tools: [":foo.tool", ":other.tool"],
+ tool_files: [":foo.tool", ":other.tool"],
cmd: "$(locations) -s $(out) $(in)",
bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
- tools = [
+}`
+
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
+ "cmd": `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
+ "outs": `["foo.out"]`,
+ "srcs": `["foo.in"]`,
+ "tools": `[
"//other:foo.tool",
"//other:other.tool",
- ],
-)`,
- },
- filesystem: otherGenruleBp,
+ ]`,
+ }
+
+ 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),
+ }
+
+ 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),
+ })
+ })
+ }
+}
+
+func TestGenruleWithoutToolsOrToolFiles(t *testing.T) {
+ testCases := []struct {
+ moduleType string
+ factory android.ModuleFactory
+ }{
+ {
+ moduleType: "genrule",
+ factory: genrule.GenRuleFactory,
},
{
- description: "genrule without tools or tool_files can convert successfully",
- moduleTypeUnderTest: "genrule",
- moduleTypeUnderTestFactory: genrule.GenRuleFactory,
- moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
- blueprint: `genrule {
+ moduleType: "cc_genrule",
+ factory: cc.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule",
+ factory: java.GenRuleFactory,
+ },
+ {
+ moduleType: "java_genrule_host",
+ factory: java.GenRuleFactoryHost,
+ },
+ }
+
+ bp := `%s {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
cmd: "cp $(in) $(out)",
bazel_module: { bp2build_available: true },
-}`,
- expectedBazelTargets: []string{`genrule(
- name = "foo",
- cmd = "cp $(SRCS) $(OUTS)",
- outs = ["foo.out"],
- srcs = ["foo.in"],
-)`,
- },
- },
- }
+}`
- dir := "."
- for _, testCase := range testCases {
- fs := make(map[string][]byte)
- toParse := []string{
- "Android.bp",
- }
- 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)
- ctx := android.NewTestContext(config)
- ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
- ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
- ctx.RegisterForBazelConversion()
-
- _, errs := ctx.ParseFileList(dir, toParse)
- if errored(t, testCase, errs) {
- continue
- }
- _, errs = ctx.ResolveDependencies(config)
- if errored(t, testCase, errs) {
- continue
+ for _, tc := range testCases {
+ moduleAttrs := attrNameToString{
+ "cmd": `"cp $(SRCS) $(OUTS)"`,
+ "outs": `["foo.out"]`,
+ "srcs": `["foo.in"]`,
}
- checkDir := dir
- if testCase.dir != "" {
- checkDir = testCase.dir
+ if tc.moduleType == "java_genrule_host" {
+ moduleAttrs["target_compatible_with"] = `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`
}
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
- bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
- android.FailIfErrored(t, err)
- if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
- t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
- } else {
- for i, target := range bazelTargets {
- if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
- t.Errorf(
- "%s: Expected generated Bazel target to be '%s', got '%s'",
- testCase.description,
- w,
- g,
- )
- }
- }
+ expectedBazelTargets := []string{
+ makeBazelTarget("genrule", "foo", moduleAttrs),
}
+
+ 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,
+ })
+ })
}
}
-func TestBp2BuildInlinesDefaults(t *testing.T) {
- testCases := []struct {
- moduleTypesUnderTest map[string]android.ModuleFactory
- bp2buildMutatorsUnderTest map[string]bp2buildMutator
- bp string
- expectedBazelTarget string
- description string
- }{
+func TestGenruleBp2BuildInlinesDefaults(t *testing.T) {
+ testCases := []bp2buildTestCase{
{
- moduleTypesUnderTest: map[string]android.ModuleFactory{
- "genrule": genrule.GenRuleFactory,
- "genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
- },
- bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
- "genrule": genrule.GenruleBp2Build,
- },
- bp: `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)",
}
@@ -314,23 +548,17 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTarget: `genrule(
- name = "gen",
- cmd = "do-something $(SRCS) $(OUTS)",
- outs = ["out"],
- srcs = ["in1"],
-)`,
- description: "genrule applies properties from a genrule_defaults dependency if not specified",
+ expectedBazelTargets: []string{
+ makeBazelTarget("genrule", "gen", attrNameToString{
+ "cmd": `"do-something $(SRCS) $(OUTS)"`,
+ "outs": `["out"]`,
+ "srcs": `["in1"]`,
+ }),
+ },
},
{
- moduleTypesUnderTest: map[string]android.ModuleFactory{
- "genrule": genrule.GenRuleFactory,
- "genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
- },
- bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
- "genrule": genrule.GenruleBp2Build,
- },
- bp: `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"],
@@ -345,29 +573,23 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTarget: `genrule(
- name = "gen",
- cmd = "do-something $(SRCS) $(OUTS)",
- outs = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("genrule", "gen", attrNameToString{
+ "cmd": `"do-something $(SRCS) $(OUTS)"`,
+ "outs": `[
"out-from-defaults",
"out",
- ],
- srcs = [
+ ]`,
+ "srcs": `[
"in-from-defaults",
"in1",
- ],
-)`,
- description: "genrule does merges properties from a genrule_defaults dependency, latest-first",
+ ]`,
+ }),
+ },
},
{
- moduleTypesUnderTest: map[string]android.ModuleFactory{
- "genrule": genrule.GenRuleFactory,
- "genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
- },
- bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
- "genrule": genrule.GenruleBp2Build,
- },
- bp: `genrule_defaults {
+ description: "genrule applies properties from list of genrule_defaults",
+ blueprint: `genrule_defaults {
name: "gen_defaults1",
cmd: "cp $(in) $(out)",
}
@@ -384,23 +606,17 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTarget: `genrule(
- name = "gen",
- cmd = "cp $(SRCS) $(OUTS)",
- outs = ["out"],
- srcs = ["in1"],
-)`,
- description: "genrule applies properties from list of genrule_defaults",
+ expectedBazelTargets: []string{
+ makeBazelTarget("genrule", "gen", attrNameToString{
+ "cmd": `"cp $(SRCS) $(OUTS)"`,
+ "outs": `["out"]`,
+ "srcs": `["in1"]`,
+ }),
+ },
},
{
- moduleTypesUnderTest: map[string]android.ModuleFactory{
- "genrule": genrule.GenRuleFactory,
- "genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
- },
- bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
- "genrule": genrule.GenruleBp2Build,
- },
- bp: `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.
@@ -427,55 +643,26 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTarget: `genrule(
- name = "gen",
- cmd = "cmd1 $(SRCS) $(OUTS)",
- outs = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("genrule", "gen", attrNameToString{
+ "cmd": `"cmd1 $(SRCS) $(OUTS)"`,
+ "outs": `[
"out-from-3",
"out-from-2",
"out",
- ],
- srcs = [
+ ]`,
+ "srcs": `[
"srcs-from-3",
"in1",
- ],
-)`,
- description: "genrule applies properties from genrule_defaults transitively",
+ ]`,
+ }),
+ },
},
}
- dir := "."
for _, testCase := range testCases {
- config := android.TestConfig(buildDir, nil, testCase.bp, nil)
- ctx := android.NewTestContext(config)
- for m, factory := range testCase.moduleTypesUnderTest {
- ctx.RegisterModuleType(m, factory)
- }
- for mutator, f := range testCase.bp2buildMutatorsUnderTest {
- ctx.RegisterBp2BuildMutator(mutator, f)
- }
- ctx.RegisterForBazelConversion()
-
- _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.ResolveDependencies(config)
- android.FailIfErrored(t, errs)
-
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
- bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
- android.FailIfErrored(t, err)
- if actualCount := len(bazelTargets); actualCount != 1 {
- t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount)
- }
-
- actualBazelTarget := bazelTargets[0]
- if actualBazelTarget.content != testCase.expectedBazelTarget {
- t.Errorf(
- "%s: Expected generated Bazel target to be '%s', got '%s'",
- testCase.description,
- testCase.expectedBazelTarget,
- actualBazelTarget.content,
- )
- }
+ t.Run(testCase.description, func(t *testing.T) {
+ runGenruleTestCase(t, testCase)
+ })
}
}
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
new file mode 100644
index 0000000..65136d9
--- /dev/null
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -0,0 +1,69 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/java"
+)
+
+func runJavaBinaryHostTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ (&tc).moduleTypeUnderTest = "java_binary_host"
+ (&tc).moduleTypeUnderTestFactory = java.BinaryHostFactory
+ runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("cc_library_host_shared", cc.LibraryHostSharedFactory)
+ }, tc)
+}
+
+var fs = map[string]string{
+ "test.mf": "Main-Class: com.android.test.MainClass",
+ "other/Android.bp": `cc_library_host_shared {
+ name: "jni-lib-1",
+ stl: "none",
+}`,
+}
+
+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 {
+ name: "java-binary-host-1",
+ srcs: ["a.java", "b.java"],
+ exclude_srcs: ["b.java"],
+ manifest: "test.mf",
+ jni_libs: ["jni-lib-1"],
+ javacflags: ["-Xdoclint:all/protected"],
+ bazel_module: { bp2build_available: true },
+}`,
+ 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"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/java_import_conversion_test.go b/bp2build/java_import_conversion_test.go
new file mode 100644
index 0000000..2f7211c
--- /dev/null
+++ b/bp2build/java_import_conversion_test.go
@@ -0,0 +1,52 @@
+// 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"
+
+ "testing"
+)
+
+func runJavaImportTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ runBp2BuildTestCase(t, registerJavaImportModuleTypes, tc)
+}
+
+func registerJavaImportModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestMinimalJavaImport(t *testing.T) {
+ runJavaImportTestCase(t, bp2buildTestCase{
+ description: "Java import - simple example",
+ moduleTypeUnderTest: "java_import",
+ moduleTypeUnderTestFactory: java.ImportFactory,
+ filesystem: map[string]string{
+ "import.jar": "",
+ },
+ blueprint: `
+java_import {
+ name: "example_import",
+ jars: ["import.jar"],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_import", "example_import", attrNameToString{
+ "jars": `["import.jar"]`,
+ }),
+ }})
+}
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
new file mode 100644
index 0000000..5c65ec2
--- /dev/null
+++ b/bp2build/java_library_conversion_test.go
@@ -0,0 +1,57 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func runJavaLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ (&tc).moduleTypeUnderTest = "java_library"
+ (&tc).moduleTypeUnderTestFactory = java.LibraryFactory
+ runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+func TestJavaLibrary(t *testing.T) {
+ 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"],
+ 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_library", "java-lib-1", attrNameToString{
+ "srcs": `["a.java"]`,
+ "deps": `[":java-lib-2"]`,
+ }),
+ makeBazelTarget("java_library", "java-lib-2", attrNameToString{
+ "srcs": `["b.java"]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/java_library_host_conversion_test.go b/bp2build/java_library_host_conversion_test.go
new file mode 100644
index 0000000..73abdd2
--- /dev/null
+++ b/bp2build/java_library_host_conversion_test.go
@@ -0,0 +1,65 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func runJavaLibraryHostTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ (&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 {
+ name: "java-lib-host-1",
+ srcs: ["a.java", "b.java"],
+ exclude_srcs: ["b.java"],
+ libs: ["java-lib-host-2"],
+ bazel_module: { bp2build_available: true },
+}
+
+java_library_host {
+ name: "java-lib-host-2",
+ srcs: ["c.java"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_library", "java-lib-host-1", attrNameToString{
+ "srcs": `["a.java"]`,
+ "deps": `[":java-lib-host-2"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ makeBazelTarget("java_library", "java-lib-host-2", attrNameToString{
+ "srcs": `["c.java"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/java_plugin_conversion_test.go b/bp2build/java_plugin_conversion_test.go
new file mode 100644
index 0000000..ff13bb0
--- /dev/null
+++ b/bp2build/java_plugin_conversion_test.go
@@ -0,0 +1,72 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func runJavaPluginTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ (&tc).moduleTypeUnderTest = "java_plugin"
+ (&tc).moduleTypeUnderTestFactory = java.PluginFactory
+ runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("java_library", java.LibraryFactory)
+ }, tc)
+}
+
+func TestJavaPlugin(t *testing.T) {
+ runJavaPluginTestCase(t, bp2buildTestCase{
+ description: "java_plugin with srcs, libs, static_libs",
+ blueprint: `java_plugin {
+ name: "java-plug-1",
+ srcs: ["a.java", "b.java"],
+ libs: ["java-lib-1"],
+ static_libs: ["java-lib-2"],
+ bazel_module: { bp2build_available: true },
+}
+
+java_library {
+ name: "java-lib-1",
+ srcs: ["b.java"],
+ bazel_module: { bp2build_available: false },
+}
+
+java_library {
+ name: "java-lib-2",
+ srcs: ["c.java"],
+ bazel_module: { bp2build_available: false },
+}`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_plugin", "java-plug-1", attrNameToString{
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ "deps": `[
+ ":java-lib-1",
+ ":java-lib-2",
+ ]`,
+ "srcs": `[
+ "a.java",
+ "b.java",
+ ]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 1cc4143..8a0b1c9 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -1,62 +1,144 @@
package bp2build
import (
- "android/soong/android"
"fmt"
+ "os"
+ "path/filepath"
"strings"
+
+ "android/soong/android"
+ "android/soong/shared"
+ "android/soong/ui/metrics/bp2build_metrics_proto"
+ "github.com/google/blueprint"
)
// Simple metrics struct to collect information about a Blueprint to BUILD
// conversion process.
type CodegenMetrics struct {
// Total number of Soong modules converted to generated targets
- generatedModuleCount int
+ generatedModuleCount uint64
// Total number of Soong modules converted to handcrafted targets
- handCraftedModuleCount int
+ handCraftedModuleCount uint64
// Total number of unconverted Soong modules
- unconvertedModuleCount int
+ unconvertedModuleCount uint64
// Counts of generated Bazel targets per Bazel rule class
- ruleClassCount map[string]int
+ ruleClassCount map[string]uint64
+ // List of modules with unconverted deps
+ // NOTE: NOT in the .proto
moduleWithUnconvertedDepsMsgs []string
+ // List of modules with missing deps
+ // NOTE: NOT in the .proto
+ moduleWithMissingDepsMsgs []string
+
+ // List of converted modules
convertedModules []string
+
+ // Counts of converted modules by module type.
+ convertedModuleTypeCount map[string]uint64
+
+ // Counts of total modules by module type.
+ totalModuleTypeCount map[string]uint64
+}
+
+// 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,
+ }
}
// Print the codegen metrics to stdout.
func (metrics *CodegenMetrics) Print() {
- generatedTargetCount := 0
+ generatedTargetCount := uint64(0)
for _, ruleClass := range android.SortedStringKeys(metrics.ruleClassCount) {
count := metrics.ruleClassCount[ruleClass]
fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count)
generatedTargetCount += count
}
fmt.Printf(
- "[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n With %d modules with unconverted deps \n\t%s",
+ `[bp2build] Converted %d Android.bp modules to %d total generated BUILD targets. Included %d handcrafted BUILD targets. There are %d total Android.bp modules.
+%d converted modules have unconverted deps:
+ %s
+%d converted modules have missing deps:
+ %s
+`,
+ metrics.generatedModuleCount,
generatedTargetCount,
metrics.handCraftedModuleCount,
metrics.TotalModuleCount(),
len(metrics.moduleWithUnconvertedDepsMsgs),
- strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"))
+ strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"),
+ len(metrics.moduleWithMissingDepsMsgs),
+ strings.Join(metrics.moduleWithMissingDepsMsgs, "\n\t"),
+ )
+}
+
+const bp2buildMetricsFilename = "bp2build_metrics.pb"
+
+// fail prints $PWD to stderr, followed by the given printf string and args (vals),
+// then the given alert, and then exits with 1 for failure
+func fail(err error, alertFmt string, vals ...interface{}) {
+ cwd, wderr := os.Getwd()
+ if wderr != nil {
+ cwd = "FAILED TO GET $PWD: " + wderr.Error()
+ }
+ fmt.Fprintf(os.Stderr, "\nIn "+cwd+":\n"+alertFmt+"\n"+err.Error()+"\n", vals...)
+ os.Exit(1)
+}
+
+// Write the bp2build-protoized codegen metrics into the given directory
+func (metrics *CodegenMetrics) Write(dir string) {
+ if _, err := os.Stat(dir); os.IsNotExist(err) {
+ // The metrics dir doesn't already exist, so create it (and parents)
+ if err := os.MkdirAll(dir, 0755); err != nil { // rx for all; w for user
+ fail(err, "Failed to `mkdir -p` %s", dir)
+ }
+ } else if err != nil {
+ fail(err, "Failed to `stat` %s", dir)
+ }
+ metricsFile := filepath.Join(dir, bp2buildMetricsFilename)
+ if err := metrics.dump(metricsFile); err != nil {
+ fail(err, "Error outputting %s", metricsFile)
+ }
+ if _, err := os.Stat(metricsFile); err != nil {
+ fail(err, "MISSING BP2BUILD METRICS OUTPUT: Failed to `stat` %s", metricsFile)
+ } else {
+ fmt.Printf("\nWrote bp2build metrics to: %s\n", metricsFile)
+ }
}
func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) {
metrics.ruleClassCount[ruleClass] += 1
}
-func (metrics *CodegenMetrics) IncrementUnconvertedCount() {
+func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) {
metrics.unconvertedModuleCount += 1
+ metrics.totalModuleTypeCount[moduleType] += 1
}
-func (metrics *CodegenMetrics) TotalModuleCount() int {
+func (metrics *CodegenMetrics) TotalModuleCount() uint64 {
return metrics.handCraftedModuleCount +
metrics.generatedModuleCount +
metrics.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)
+}
+
type ConversionType int
const (
@@ -64,10 +146,12 @@
Handcrafted
)
-func (metrics *CodegenMetrics) AddConvertedModule(moduleName string, conversionType ConversionType) {
+func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, conversionType ConversionType) {
// Undo prebuilt_ module name prefix modifications
- moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName)
+ moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
metrics.convertedModules = append(metrics.convertedModules, moduleName)
+ metrics.convertedModuleTypeCount[moduleType] += 1
+ metrics.totalModuleTypeCount[moduleType] += 1
if conversionType == Handcrafted {
metrics.handCraftedModuleCount += 1
diff --git a/bp2build/performance_test.go b/bp2build/performance_test.go
index 3283952..c4bbae2 100644
--- a/bp2build/performance_test.go
+++ b/bp2build/performance_test.go
@@ -29,6 +29,10 @@
"testing"
)
+const (
+ performance_test_dir = "."
+)
+
func genCustomModule(i int, convert bool) string {
var conversionString string
if convert {
@@ -76,34 +80,83 @@
return strings.Join(bp, "\n\n")
}
+type testConfig struct {
+ config android.Config
+ ctx *android.TestContext
+ codegenCtx *CodegenContext
+}
+
+func (tc testConfig) parse() []error {
+ _, errs := tc.ctx.ParseFileList(performance_test_dir, []string{"Android.bp"})
+ return errs
+}
+
+func (tc testConfig) resolveDependencies() []error {
+ _, errs := tc.ctx.ResolveDependencies(tc.config)
+ return errs
+}
+
+func (tc testConfig) convert() {
+ generateBazelTargetsForDir(tc.codegenCtx, performance_test_dir)
+}
+
+func setup(builddir string, tcSize float64) testConfig {
+ config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+ ctx := android.NewTestContext(config)
+
+ registerCustomModuleForBp2buildConversion(ctx)
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ return testConfig{
+ config,
+ ctx,
+ codegenCtx,
+ }
+}
+
var pctToConvert = []float64{0.0, 0.01, 0.05, 0.10, 0.25, 0.5, 0.75, 1.0}
+// This is not intended to test performance, but to verify performance infra continues to work
+func TestConvertManyModulesFull(t *testing.T) {
+ for _, tcSize := range pctToConvert {
+
+ t.Run(fmt.Sprintf("pctConverted %f", tcSize), func(t *testing.T) {
+ testConfig := setup(buildDir, tcSize)
+
+ errs := testConfig.parse()
+ if len(errs) > 0 {
+ t.Fatalf("Unexpected errors: %s", errs)
+ }
+
+ errs = testConfig.resolveDependencies()
+ if len(errs) > 0 {
+ t.Fatalf("Unexpected errors: %s", errs)
+ }
+
+ testConfig.convert()
+ })
+ }
+}
+
func BenchmarkManyModulesFull(b *testing.B) {
- dir := "."
for _, tcSize := range pctToConvert {
b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
- // setup we don't want to measure
- config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
- ctx := android.NewTestContext(config)
-
- registerCustomModuleForBp2buildConversion(ctx)
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ testConfig := setup(buildDir, tcSize)
b.StartTimer()
- _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ errs := testConfig.parse()
if len(errs) > 0 {
b.Fatalf("Unexpected errors: %s", errs)
}
- _, errs = ctx.ResolveDependencies(config)
+ errs = testConfig.resolveDependencies()
if len(errs) > 0 {
b.Fatalf("Unexpected errors: %s", errs)
}
- generateBazelTargetsForDir(codegenCtx, dir)
+ testConfig.convert()
b.StopTimer()
}
})
@@ -111,63 +164,53 @@
}
func BenchmarkManyModulesResolveDependencies(b *testing.B) {
- dir := "."
for _, tcSize := range pctToConvert {
b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
// setup we don't want to measure
- config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
- ctx := android.NewTestContext(config)
+ testConfig := setup(buildDir, tcSize)
- registerCustomModuleForBp2buildConversion(ctx)
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-
- _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ errs := testConfig.parse()
if len(errs) > 0 {
b.Fatalf("Unexpected errors: %s", errs)
}
b.StartTimer()
- _, errs = ctx.ResolveDependencies(config)
+ errs = testConfig.resolveDependencies()
b.StopTimer()
if len(errs) > 0 {
b.Fatalf("Unexpected errors: %s", errs)
}
- generateBazelTargetsForDir(codegenCtx, dir)
+ testConfig.convert()
}
})
}
}
func BenchmarkManyModulesGenerateBazelTargetsForDir(b *testing.B) {
- dir := "."
for _, tcSize := range pctToConvert {
b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
// setup we don't want to measure
- config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
- ctx := android.NewTestContext(config)
+ testConfig := setup(buildDir, tcSize)
- registerCustomModuleForBp2buildConversion(ctx)
- codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-
- _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ errs := testConfig.parse()
if len(errs) > 0 {
b.Fatalf("Unexpected errors: %s", errs)
}
- _, errs = ctx.ResolveDependencies(config)
+ errs = testConfig.resolveDependencies()
if len(errs) > 0 {
b.Fatalf("Unexpected errors: %s", errs)
}
b.StartTimer()
- generateBazelTargetsForDir(codegenCtx, dir)
+ testConfig.convert()
b.StopTimer()
}
})
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
index 4e25d1b..3a5d5bb 100644
--- a/bp2build/prebuilt_etc_conversion_test.go
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -23,6 +23,8 @@
func runPrebuiltEtcTestCase(t *testing.T, tc bp2buildTestCase) {
t.Helper()
+ (&tc).moduleTypeUnderTest = "prebuilt_etc"
+ (&tc).moduleTypeUnderTestFactory = etc.PrebuiltEtcFactory
runBp2BuildTestCase(t, registerPrebuiltEtcModuleTypes, tc)
}
@@ -31,11 +33,8 @@
func TestPrebuiltEtcSimple(t *testing.T) {
runPrebuiltEtcTestCase(t, bp2buildTestCase{
- description: "prebuilt_etc - simple example",
- moduleTypeUnderTest: "prebuilt_etc",
- moduleTypeUnderTestFactory: etc.PrebuiltEtcFactory,
- moduleTypeUnderTestBp2BuildMutator: etc.PrebuiltEtcBp2Build,
- filesystem: map[string]string{},
+ description: "prebuilt_etc - simple example",
+ filesystem: map[string]string{},
blueprint: `
prebuilt_etc {
name: "apex_tz_version",
@@ -45,11 +44,87 @@
installable: false,
}
`,
- expectedBazelTargets: []string{`prebuilt_etc(
- name = "apex_tz_version",
- filename = "tz_version",
- installable = False,
- src = "version/tz_version",
- sub_dir = "tz",
-)`}})
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_etc", "apex_tz_version", attrNameToString{
+ "filename": `"tz_version"`,
+ "installable": `False`,
+ "src": `"version/tz_version"`,
+ "sub_dir": `"tz"`,
+ })}})
+}
+
+func TestPrebuiltEtcArchVariant(t *testing.T) {
+ runPrebuiltEtcTestCase(t, bp2buildTestCase{
+ description: "prebuilt_etc - arch variant",
+ filesystem: map[string]string{},
+ blueprint: `
+prebuilt_etc {
+ name: "apex_tz_version",
+ src: "version/tz_version",
+ filename: "tz_version",
+ sub_dir: "tz",
+ installable: false,
+ arch: {
+ arm: {
+ src: "arm",
+ },
+ arm64: {
+ src: "arm64",
+ },
+ }
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_etc", "apex_tz_version", attrNameToString{
+ "filename": `"tz_version"`,
+ "installable": `False`,
+ "src": `select({
+ "//build/bazel/platforms/arch:arm": "arm",
+ "//build/bazel/platforms/arch:arm64": "arm64",
+ "//conditions:default": "version/tz_version",
+ })`,
+ "sub_dir": `"tz"`,
+ })}})
+}
+
+func TestPrebuiltEtcArchAndTargetVariant(t *testing.T) {
+ runPrebuiltEtcTestCase(t, bp2buildTestCase{
+ description: "prebuilt_etc - arch variant",
+ filesystem: map[string]string{},
+ blueprint: `
+prebuilt_etc {
+ name: "apex_tz_version",
+ src: "version/tz_version",
+ filename: "tz_version",
+ sub_dir: "tz",
+ installable: false,
+ arch: {
+ arm: {
+ src: "arm",
+ },
+ arm64: {
+ src: "darwin_or_arm64",
+ },
+ },
+ target: {
+ darwin: {
+ src: "darwin_or_arm64",
+ }
+ },
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("prebuilt_etc", "apex_tz_version", attrNameToString{
+ "filename": `"tz_version"`,
+ "installable": `False`,
+ "src": `select({
+ "//build/bazel/platforms/os_arch:android_arm": "arm",
+ "//build/bazel/platforms/os_arch:android_arm64": "darwin_or_arm64",
+ "//build/bazel/platforms/os_arch:darwin_arm64": "darwin_or_arm64",
+ "//build/bazel/platforms/os_arch:darwin_x86_64": "darwin_or_arm64",
+ "//build/bazel/platforms/os_arch:linux_bionic_arm64": "darwin_or_arm64",
+ "//conditions:default": "version/tz_version",
+ })`,
+ "sub_dir": `"tz"`,
+ })}})
}
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index 5b4829e..dfa11d1 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -7,7 +7,8 @@
"android/soong/python"
)
-func runBp2BuildTestCaseWithLibs(t *testing.T, tc bp2buildTestCase) {
+func runBp2BuildTestCaseWithPythonLibraries(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
ctx.RegisterModuleType("python_library_host", python.PythonLibraryHostFactory)
@@ -15,11 +16,10 @@
}
func TestPythonBinaryHostSimple(t *testing.T) {
- runBp2BuildTestCaseWithLibs(t, bp2buildTestCase{
- description: "simple python_binary_host converts to a native py_binary",
- moduleTypeUnderTest: "python_binary_host",
- moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
- moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+ 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": "",
@@ -39,29 +39,32 @@
python_library_host {
name: "bar",
srcs: ["b/e.py"],
- bazel_module: { bp2build_available: true },
+ bazel_module: { bp2build_available: false },
}`,
- expectedBazelTargets: []string{`py_binary(
- name = "foo",
- data = ["files/data.txt"],
- deps = [":bar"],
- main = "a.py",
- srcs = [
+ expectedBazelTargets: []string{
+ makeBazelTarget("py_binary", "foo", attrNameToString{
+ "data": `["files/data.txt"]`,
+ "deps": `[":bar"]`,
+ "main": `"a.py"`,
+ "srcs": `[
"a.py",
"b/c.py",
"b/d.py",
- ],
-)`,
+ ]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
},
})
}
func TestPythonBinaryHostPy2(t *testing.T) {
runBp2BuildTestCaseSimple(t, bp2buildTestCase{
- description: "py2 python_binary_host",
- moduleTypeUnderTest: "python_binary_host",
- moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
- moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+ description: "py2 python_binary_host",
+ moduleTypeUnderTest: "python_binary_host",
+ moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
blueprint: `python_binary_host {
name: "foo",
srcs: ["a.py"],
@@ -77,21 +80,24 @@
bazel_module: { bp2build_available: true },
}
`,
- expectedBazelTargets: []string{`py_binary(
- name = "foo",
- python_version = "PY2",
- srcs = ["a.py"],
-)`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("py_binary", "foo", attrNameToString{
+ "python_version": `"PY2"`,
+ "srcs": `["a.py"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
},
})
}
func TestPythonBinaryHostPy3(t *testing.T) {
runBp2BuildTestCaseSimple(t, bp2buildTestCase{
- description: "py3 python_binary_host",
- moduleTypeUnderTest: "python_binary_host",
- moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
- moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+ description: "py3 python_binary_host",
+ moduleTypeUnderTest: "python_binary_host",
+ moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
blueprint: `python_binary_host {
name: "foo",
srcs: ["a.py"],
@@ -109,20 +115,22 @@
`,
expectedBazelTargets: []string{
// python_version is PY3 by default.
- `py_binary(
- name = "foo",
- srcs = ["a.py"],
-)`,
+ makeBazelTarget("py_binary", "foo", attrNameToString{
+ "srcs": `["a.py"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
},
})
}
func TestPythonBinaryHostArchVariance(t *testing.T) {
runBp2BuildTestCaseSimple(t, bp2buildTestCase{
- description: "test arch variants",
- moduleTypeUnderTest: "python_binary_host",
- moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
- moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+ description: "test arch variants",
+ moduleTypeUnderTest: "python_binary_host",
+ moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
filesystem: map[string]string{
"dir/arm.py": "",
"dir/x86.py": "",
@@ -139,14 +147,17 @@
},
}`,
expectedBazelTargets: []string{
- `py_binary(
- name = "foo-arm",
- srcs = select({
+ makeBazelTarget("py_binary", "foo-arm", attrNameToString{
+ "srcs": `select({
"//build/bazel/platforms/arch:arm": ["arm.py"],
"//build/bazel/platforms/arch:x86": ["x86.py"],
"//conditions:default": [],
- }),
-)`,
+ })`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
},
})
}
diff --git a/bp2build/python_library_conversion_test.go b/bp2build/python_library_conversion_test.go
index 7f983ad..356d52e 100644
--- a/bp2build/python_library_conversion_test.go
+++ b/bp2build/python_library_conversion_test.go
@@ -11,38 +11,79 @@
// TODO(alexmarquez): Should be lifted into a generic Bp2Build file
type PythonLibBp2Build func(ctx android.TopDownMutatorContext)
-func TestPythonLibrary(t *testing.T) {
- testPythonLib(t, "python_library",
- python.PythonLibraryFactory, python.PythonLibraryBp2Build,
- func(ctx android.RegistrationContext) {})
+type pythonLibBp2BuildTestCase struct {
+ description string
+ filesystem map[string]string
+ blueprint string
+ expectedBazelTargets []testBazelTarget
}
-func TestPythonLibraryHost(t *testing.T) {
- testPythonLib(t, "python_library_host",
- python.PythonLibraryHostFactory, python.PythonLibraryHostBp2Build,
- func(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
- })
+func convertPythonLibTestCaseToBp2build_Host(tc pythonLibBp2BuildTestCase) bp2buildTestCase {
+ for i := range tc.expectedBazelTargets {
+ tc.expectedBazelTargets[i].attrs["target_compatible_with"] = `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`
+ }
+
+ return convertPythonLibTestCaseToBp2build(tc)
}
-func testPythonLib(t *testing.T, modType string,
- factory android.ModuleFactory, mutator PythonLibBp2Build,
- registration func(ctx android.RegistrationContext)) {
+func convertPythonLibTestCaseToBp2build(tc pythonLibBp2BuildTestCase) bp2buildTestCase {
+ var bp2BuildTargets []string
+ for _, t := range tc.expectedBazelTargets {
+ bp2BuildTargets = append(bp2BuildTargets, makeBazelTarget(t.typ, t.name, t.attrs))
+ }
+ return bp2buildTestCase{
+ description: tc.description,
+ filesystem: tc.filesystem,
+ blueprint: tc.blueprint,
+ expectedBazelTargets: bp2BuildTargets,
+ }
+}
+
+func runPythonLibraryTestCase(t *testing.T, tc pythonLibBp2BuildTestCase) {
t.Helper()
- // Simple
- runBp2BuildTestCase(t, registration, bp2buildTestCase{
- description: fmt.Sprintf("simple %s converts to a native py_library", modType),
- moduleTypeUnderTest: modType,
- moduleTypeUnderTestFactory: factory,
- moduleTypeUnderTestBp2BuildMutator: mutator,
- filesystem: map[string]string{
- "a.py": "",
- "b/c.py": "",
- "b/d.py": "",
- "b/e.py": "",
- "files/data.txt": "",
- },
- blueprint: fmt.Sprintf(`%s {
+ 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
+
+ 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) {
+ ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
+ },
+ testCase)
+}
+
+func runPythonLibraryTestCases(t *testing.T, tc pythonLibBp2BuildTestCase) {
+ t.Helper()
+ runPythonLibraryTestCase(t, tc)
+ runPythonLibraryHostTestCase(t, tc)
+}
+
+func TestSimplePythonLib(t *testing.T) {
+ testCases := []pythonLibBp2BuildTestCase{
+ {
+ description: "simple %s converts to a native py_library",
+ filesystem: map[string]string{
+ "a.py": "",
+ "b/c.py": "",
+ "b/d.py": "",
+ "b/e.py": "",
+ "files/data.txt": "",
+ },
+ blueprint: `%s {
name: "foo",
srcs: ["**/*.py"],
exclude_srcs: ["b/e.py"],
@@ -54,28 +95,27 @@
name: "bar",
srcs: ["b/e.py"],
bazel_module: { bp2build_available: false },
- }`, modType),
- expectedBazelTargets: []string{`py_library(
- name = "foo",
- data = ["files/data.txt"],
- deps = [":bar"],
- srcs = [
+ }`,
+ expectedBazelTargets: []testBazelTarget{
+ {
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "data": `["files/data.txt"]`,
+ "deps": `[":bar"]`,
+ "srcs": `[
"a.py",
"b/c.py",
"b/d.py",
- ],
- srcs_version = "PY3",
-)`,
+ ]`,
+ "srcs_version": `"PY3"`,
+ },
+ },
+ },
},
- })
-
- // PY2
- runBp2BuildTestCaseSimple(t, bp2buildTestCase{
- description: fmt.Sprintf("py2 %s converts to a native py_library", modType),
- moduleTypeUnderTest: modType,
- moduleTypeUnderTestFactory: factory,
- moduleTypeUnderTestBp2BuildMutator: mutator,
- blueprint: fmt.Sprintf(`%s {
+ {
+ description: "py2 %s converts to a native py_library",
+ blueprint: `%s {
name: "foo",
srcs: ["a.py"],
version: {
@@ -88,22 +128,21 @@
},
bazel_module: { bp2build_available: true },
-}`, modType),
- expectedBazelTargets: []string{`py_library(
- name = "foo",
- srcs = ["a.py"],
- srcs_version = "PY2",
-)`,
+}`,
+ expectedBazelTargets: []testBazelTarget{
+ {
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `["a.py"]`,
+ "srcs_version": `"PY2"`,
+ },
+ },
+ },
},
- })
-
- // PY3
- runBp2BuildTestCaseSimple(t, bp2buildTestCase{
- description: fmt.Sprintf("py3 %s converts to a native py_library", modType),
- moduleTypeUnderTest: modType,
- moduleTypeUnderTestFactory: factory,
- moduleTypeUnderTestBp2BuildMutator: mutator,
- blueprint: fmt.Sprintf(`%s {
+ {
+ description: "py3 %s converts to a native py_library",
+ blueprint: `%s {
name: "foo",
srcs: ["a.py"],
version: {
@@ -116,22 +155,21 @@
},
bazel_module: { bp2build_available: true },
-}`, modType),
- expectedBazelTargets: []string{`py_library(
- name = "foo",
- srcs = ["a.py"],
- srcs_version = "PY3",
-)`,
+}`,
+ expectedBazelTargets: []testBazelTarget{
+ {
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `["a.py"]`,
+ "srcs_version": `"PY3"`,
+ },
+ },
+ },
},
- })
-
- // Both
- runBp2BuildTestCaseSimple(t, bp2buildTestCase{
- description: fmt.Sprintf("py2&3 %s converts to a native py_library", modType),
- moduleTypeUnderTest: modType,
- moduleTypeUnderTestFactory: factory,
- moduleTypeUnderTestBp2BuildMutator: mutator,
- blueprint: fmt.Sprintf(`%s {
+ {
+ description: "py2&3 %s converts to a native py_library",
+ blueprint: `%s {
name: "foo",
srcs: ["a.py"],
version: {
@@ -144,44 +182,35 @@
},
bazel_module: { bp2build_available: true },
-}`, modType),
- expectedBazelTargets: []string{
- // srcs_version is PY2ANDPY3 by default.
- `py_library(
- name = "foo",
- srcs = ["a.py"],
-)`,
+}`,
+ expectedBazelTargets: []testBazelTarget{
+ {
+ // srcs_version is PY2ANDPY3 by default.
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `["a.py"]`,
+ },
+ },
+ },
},
- })
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ runPythonLibraryTestCases(t, tc)
+ })
+ }
}
-func TestPythonLibraryArchVariance(t *testing.T) {
- testPythonArchVariance(t, "python_library", "py_library",
- python.PythonLibraryFactory, python.PythonLibraryBp2Build,
- func(ctx android.RegistrationContext) {})
-}
-
-func TestPythonLibraryHostArchVariance(t *testing.T) {
- testPythonArchVariance(t, "python_library_host", "py_library",
- python.PythonLibraryHostFactory, python.PythonLibraryHostBp2Build,
- func(ctx android.RegistrationContext) {})
-}
-
-// TODO: refactor python_binary_conversion_test to use this
-func testPythonArchVariance(t *testing.T, modType, bazelTarget string,
- factory android.ModuleFactory, mutator PythonLibBp2Build,
- registration func(ctx android.RegistrationContext)) {
- t.Helper()
- runBp2BuildTestCase(t, registration, bp2buildTestCase{
- description: fmt.Sprintf("test %s arch variants", modType),
- moduleTypeUnderTest: modType,
- moduleTypeUnderTestFactory: factory,
- moduleTypeUnderTestBp2BuildMutator: mutator,
+func TestPythonArchVariance(t *testing.T) {
+ runPythonLibraryTestCases(t, pythonLibBp2BuildTestCase{
+ description: "test %s arch variants",
filesystem: map[string]string{
"dir/arm.py": "",
"dir/x86.py": "",
},
- blueprint: fmt.Sprintf(`%s {
+ blueprint: `%s {
name: "foo",
arch: {
arm: {
@@ -191,17 +220,20 @@
srcs: ["x86.py"],
},
},
- }`, modType),
- expectedBazelTargets: []string{
- fmt.Sprintf(`%s(
- name = "foo",
- srcs = select({
+ }`,
+ expectedBazelTargets: []testBazelTarget{
+ {
+ typ: "py_library",
+ name: "foo",
+ attrs: attrNameToString{
+ "srcs": `select({
"//build/bazel/platforms/arch:arm": ["arm.py"],
"//build/bazel/platforms/arch:x86": ["x86.py"],
"//conditions:default": [],
- }),
- srcs_version = "PY3",
-)`, bazelTarget),
+ })`,
+ "srcs_version": `"PY3"`,
+ },
+ },
},
})
}
diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go
index 82e0a14..ac89087 100644
--- a/bp2build/sh_conversion_test.go
+++ b/bp2build/sh_conversion_test.go
@@ -55,18 +55,38 @@
func TestShBinarySimple(t *testing.T) {
runShBinaryTestCase(t, bp2buildTestCase{
- description: "sh_binary test",
- moduleTypeUnderTest: "sh_binary",
- moduleTypeUnderTestFactory: sh.ShBinaryFactory,
- moduleTypeUnderTestBp2BuildMutator: sh.ShBinaryBp2Build,
+ 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{
+ "srcs": `["foo.sh"]`,
+ "filename": `"foo.exe"`,
+ "sub_dir": `"sub"`,
+ })},
+ })
+}
+
+func TestShBinaryDefaults(t *testing.T) {
+ 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{`sh_binary(
- name = "foo",
- srcs = ["foo.sh"],
-)`},
+ 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
new file mode 100644
index 0000000..b1e1fb2
--- /dev/null
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -0,0 +1,985 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "testing"
+)
+
+func runSoongConfigModuleTypeTest(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ 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)
+
+ ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+}
+
+func TestSoongConfigModuleType(t *testing.T) {
+ bp := `
+soong_config_module_type {
+ name: "custom_cc_library_static",
+ module_type: "cc_library_static",
+ config_namespace: "acme",
+ bool_variables: ["feature1"],
+ properties: ["cflags"],
+}
+
+custom_cc_library_static {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+ soong_config_variables: {
+ feature1: {
+ conditions_default: {
+ cflags: ["-DDEFAULT1"],
+ },
+ cflags: ["-DFEATURE1"],
+ },
+ },
+}
+`
+
+ 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"],
+ "//conditions:default": ["-DDEFAULT1"],
+ }),
+ local_includes = ["."],
+)`}})
+}
+
+func TestSoongConfigModuleTypeImport(t *testing.T) {
+ configBp := `
+soong_config_module_type {
+ name: "custom_cc_library_static",
+ module_type: "cc_library_static",
+ config_namespace: "acme",
+ bool_variables: ["feature1"],
+ properties: ["cflags"],
+}
+`
+ bp := `
+soong_config_module_type_import {
+ from: "foo/bar/SoongConfig.bp",
+ module_types: ["custom_cc_library_static"],
+}
+
+custom_cc_library_static {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+ soong_config_variables: {
+ feature1: {
+ conditions_default: {
+ cflags: ["-DDEFAULT1"],
+ },
+ cflags: ["-DFEATURE1"],
+ },
+ },
+}
+`
+
+ 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(
+ name = "foo",
+ copts = select({
+ "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
+ "//conditions:default": ["-DDEFAULT1"],
+ }),
+ local_includes = ["."],
+)`}})
+}
+
+func TestSoongConfigModuleType_StringVar(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "board",
+ values: ["soc_a", "soc_b", "soc_c"],
+}
+
+soong_config_module_type {
+ name: "custom_cc_library_static",
+ module_type: "cc_library_static",
+ config_namespace: "acme",
+ variables: ["board"],
+ properties: ["cflags"],
+}
+
+custom_cc_library_static {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+ soong_config_variables: {
+ board: {
+ soc_a: {
+ cflags: ["-DSOC_A"],
+ },
+ soc_b: {
+ cflags: ["-DSOC_B"],
+ },
+ soc_c: {},
+ conditions_default: {
+ cflags: ["-DSOC_DEFAULT"]
+ },
+ },
+ },
+}
+`
+
+ 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"],
+ "//conditions:default": ["-DSOC_DEFAULT"],
+ }),
+ local_includes = ["."],
+)`}})
+}
+
+func TestSoongConfigModuleType_StringAndBoolVar(t *testing.T) {
+ bp := `
+soong_config_bool_variable {
+ name: "feature1",
+}
+
+soong_config_bool_variable {
+ name: "feature2",
+}
+
+soong_config_string_variable {
+ name: "board",
+ values: ["soc_a", "soc_b", "soc_c"],
+}
+
+soong_config_module_type {
+ name: "custom_cc_library_static",
+ module_type: "cc_library_static",
+ config_namespace: "acme",
+ variables: ["feature1", "feature2", "board"],
+ properties: ["cflags"],
+}
+
+custom_cc_library_static {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+ soong_config_variables: {
+ feature1: {
+ conditions_default: {
+ cflags: ["-DDEFAULT1"],
+ },
+ cflags: ["-DFEATURE1"],
+ },
+ feature2: {
+ cflags: ["-DFEATURE2"],
+ conditions_default: {
+ cflags: ["-DDEFAULT2"],
+ },
+ },
+ board: {
+ soc_a: {
+ cflags: ["-DSOC_A"],
+ },
+ soc_b: {
+ cflags: ["-DSOC_B"],
+ },
+ soc_c: {},
+ conditions_default: {
+ cflags: ["-DSOC_DEFAULT"]
+ },
+ },
+ },
+}`
+
+ 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"],
+ "//conditions:default": ["-DSOC_DEFAULT"],
+ }) + select({
+ "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
+ "//conditions:default": ["-DDEFAULT1"],
+ }) + select({
+ "//build/bazel/product_variables:acme__feature2": ["-DFEATURE2"],
+ "//conditions:default": ["-DDEFAULT2"],
+ }),
+ local_includes = ["."],
+)`}})
+}
+
+func TestSoongConfigModuleType_StringVar_LabelListDeps(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "board",
+ values: ["soc_a", "soc_b", "soc_c"],
+}
+
+soong_config_module_type {
+ name: "custom_cc_library_static",
+ module_type: "cc_library_static",
+ config_namespace: "acme",
+ variables: ["board"],
+ properties: ["cflags", "static_libs"],
+}
+
+custom_cc_library_static {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+ soong_config_variables: {
+ board: {
+ soc_a: {
+ cflags: ["-DSOC_A"],
+ static_libs: ["soc_a_dep"],
+ },
+ soc_b: {
+ cflags: ["-DSOC_B"],
+ static_libs: ["soc_b_dep"],
+ },
+ soc_c: {},
+ conditions_default: {
+ cflags: ["-DSOC_DEFAULT"],
+ static_libs: ["soc_default_static_dep"],
+ },
+ },
+ },
+}`
+
+ otherDeps := `
+cc_library_static { name: "soc_a_dep", bazel_module: { bp2build_available: false } }
+cc_library_static { name: "soc_b_dep", bazel_module: { bp2build_available: false } }
+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{
+ "foo/bar/Android.bp": otherDeps,
+ },
+ 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"],
+ "//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"],
+ "//conditions:default": ["//foo/bar:soc_default_static_dep"],
+ }),
+ local_includes = ["."],
+)`}})
+}
+
+func TestSoongConfigModuleType_Defaults_SingleNamespace(t *testing.T) {
+ bp := `
+soong_config_module_type {
+ name: "vendor_foo_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "vendor_foo",
+ bool_variables: ["feature"],
+ properties: ["cflags", "cppflags"],
+}
+
+vendor_foo_cc_defaults {
+ name: "foo_defaults_1",
+ soong_config_variables: {
+ feature: {
+ cflags: ["-cflag_feature_1"],
+ conditions_default: {
+ cflags: ["-cflag_default_1"],
+ },
+ },
+ },
+}
+
+vendor_foo_cc_defaults {
+ name: "foo_defaults_2",
+ defaults: ["foo_defaults_1"],
+ soong_config_variables: {
+ feature: {
+ cflags: ["-cflag_feature_2"],
+ conditions_default: {
+ cflags: ["-cflag_default_2"],
+ },
+ },
+ },
+}
+
+cc_library_static {
+ name: "lib",
+ defaults: ["foo_defaults_2"],
+ bazel_module: { bp2build_available: 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(
+ name = "lib",
+ copts = select({
+ "//build/bazel/product_variables:vendor_foo__feature": [
+ "-cflag_feature_2",
+ "-cflag_feature_1",
+ ],
+ "//conditions:default": [
+ "-cflag_default_2",
+ "-cflag_default_1",
+ ],
+ }),
+ local_includes = ["."],
+)`}})
+}
+
+func TestSoongConfigModuleType_MultipleDefaults_SingleNamespace(t *testing.T) {
+ bp := `
+soong_config_module_type {
+ name: "foo_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "acme",
+ bool_variables: ["feature"],
+ properties: ["cflags"],
+}
+
+soong_config_module_type {
+ name: "bar_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "acme",
+ bool_variables: ["feature"],
+ properties: ["cflags", "asflags"],
+}
+
+foo_cc_defaults {
+ name: "foo_defaults",
+ soong_config_variables: {
+ feature: {
+ cflags: ["-cflag_foo"],
+ conditions_default: {
+ cflags: ["-cflag_default_foo"],
+ },
+ },
+ },
+}
+
+bar_cc_defaults {
+ name: "bar_defaults",
+ srcs: ["file.S"],
+ soong_config_variables: {
+ feature: {
+ cflags: ["-cflag_bar"],
+ asflags: ["-asflag_bar"],
+ conditions_default: {
+ asflags: ["-asflag_default_bar"],
+ cflags: ["-cflag_default_bar"],
+ },
+ },
+ },
+}
+
+cc_library_static {
+ name: "lib",
+ defaults: ["foo_defaults", "bar_defaults"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+ name: "lib2",
+ defaults: ["bar_defaults", "foo_defaults"],
+ bazel_module: { bp2build_available: 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(
+ name = "lib",
+ asflags = select({
+ "//build/bazel/product_variables:acme__feature": ["-asflag_bar"],
+ "//conditions:default": ["-asflag_default_bar"],
+ }),
+ copts = select({
+ "//build/bazel/product_variables:acme__feature": [
+ "-cflag_foo",
+ "-cflag_bar",
+ ],
+ "//conditions:default": [
+ "-cflag_default_foo",
+ "-cflag_default_bar",
+ ],
+ }),
+ local_includes = ["."],
+ srcs_as = ["file.S"],
+)`,
+ `cc_library_static(
+ name = "lib2",
+ asflags = select({
+ "//build/bazel/product_variables:acme__feature": ["-asflag_bar"],
+ "//conditions:default": ["-asflag_default_bar"],
+ }),
+ copts = select({
+ "//build/bazel/product_variables:acme__feature": [
+ "-cflag_bar",
+ "-cflag_foo",
+ ],
+ "//conditions:default": [
+ "-cflag_default_bar",
+ "-cflag_default_foo",
+ ],
+ }),
+ local_includes = ["."],
+ srcs_as = ["file.S"],
+)`}})
+}
+
+func TestSoongConfigModuleType_Defaults_MultipleNamespaces(t *testing.T) {
+ bp := `
+soong_config_module_type {
+ name: "vendor_foo_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "vendor_foo",
+ bool_variables: ["feature"],
+ properties: ["cflags"],
+}
+
+soong_config_module_type {
+ name: "vendor_bar_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "vendor_bar",
+ bool_variables: ["feature"],
+ properties: ["cflags"],
+}
+
+soong_config_module_type {
+ name: "vendor_qux_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "vendor_qux",
+ bool_variables: ["feature"],
+ properties: ["cflags"],
+}
+
+vendor_foo_cc_defaults {
+ name: "foo_defaults",
+ soong_config_variables: {
+ feature: {
+ cflags: ["-DVENDOR_FOO_FEATURE"],
+ conditions_default: {
+ cflags: ["-DVENDOR_FOO_DEFAULT"],
+ },
+ },
+ },
+}
+
+vendor_bar_cc_defaults {
+ name: "bar_defaults",
+ soong_config_variables: {
+ feature: {
+ cflags: ["-DVENDOR_BAR_FEATURE"],
+ conditions_default: {
+ cflags: ["-DVENDOR_BAR_DEFAULT"],
+ },
+ },
+ },
+}
+
+vendor_qux_cc_defaults {
+ name: "qux_defaults",
+ defaults: ["bar_defaults"],
+ soong_config_variables: {
+ feature: {
+ cflags: ["-DVENDOR_QUX_FEATURE"],
+ conditions_default: {
+ cflags: ["-DVENDOR_QUX_DEFAULT"],
+ },
+ },
+ },
+}
+
+cc_library_static {
+ name: "lib",
+ defaults: ["foo_defaults", "qux_defaults"],
+ bazel_module: { bp2build_available: 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(
+ name = "lib",
+ copts = select({
+ "//build/bazel/product_variables:vendor_bar__feature": ["-DVENDOR_BAR_FEATURE"],
+ "//conditions:default": ["-DVENDOR_BAR_DEFAULT"],
+ }) + select({
+ "//build/bazel/product_variables:vendor_foo__feature": ["-DVENDOR_FOO_FEATURE"],
+ "//conditions:default": ["-DVENDOR_FOO_DEFAULT"],
+ }) + select({
+ "//build/bazel/product_variables:vendor_qux__feature": ["-DVENDOR_QUX_FEATURE"],
+ "//conditions:default": ["-DVENDOR_QUX_DEFAULT"],
+ }),
+ local_includes = ["."],
+)`}})
+}
+
+func TestSoongConfigModuleType_Defaults(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: {
+ static_libs: [
+ "lib_a",
+ ],
+ },
+ conditions_default: {
+ shared_libs: [
+ "lib_a",
+ ],
+ },
+ },
+ },
+}
+
+library_linking_strategy_cc_defaults {
+ name: "library_linking_strategy_merged_defaults",
+ defaults: ["library_linking_strategy_lib_a_defaults"],
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {
+ static_libs: [
+ "lib_b",
+ ],
+ },
+ 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"],
+}`
+
+ 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",
+ deps = select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [
+ "//foo/bar:lib_b_bp2build_cc_library_static",
+ "//foo/bar:lib_a_bp2build_cc_library_static",
+ ],
+ "//conditions:default": [],
+ }),
+ dynamic_deps = select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": [
+ "//foo/bar:lib_b",
+ "//foo/bar:lib_a",
+ ],
+ }),
+ local_includes = ["."],
+ srcs = ["library_linking_strategy.cc"],
+)`}})
+}
+
+func TestSoongConfigModuleType_Defaults_Another(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_sample_defaults",
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {
+ static_libs: [
+ "lib_a",
+ "lib_b",
+ ],
+ },
+ conditions_default: {
+ shared_libs: [
+ "lib_a",
+ "lib_b",
+ ],
+ },
+ },
+ },
+}
+
+cc_binary {
+ name: "library_linking_strategy_sample_binary",
+ srcs: ["library_linking_strategy.cc"],
+ defaults: ["library_linking_strategy_sample_defaults"],
+}`
+
+ otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+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{
+ "foo/bar/Android.bp": otherDeps,
+ },
+ expectedBazelTargets: []string{`cc_binary(
+ name = "library_linking_strategy_sample_binary",
+ deps = select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [
+ "//foo/bar:lib_a_bp2build_cc_library_static",
+ "//foo/bar:lib_b_bp2build_cc_library_static",
+ ],
+ "//conditions:default": [],
+ }),
+ dynamic_deps = select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": [
+ "//foo/bar:lib_a",
+ "//foo/bar:lib_b",
+ ],
+ }),
+ local_includes = ["."],
+ srcs = ["library_linking_strategy.cc"],
+)`}})
+}
+
+func TestSoongConfigModuleType_Defaults_UnusedProps(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "alphabet",
+ values: [
+ "a",
+ "b",
+ "c", // unused
+ ],
+}
+
+soong_config_module_type {
+ name: "alphabet_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ variables: ["alphabet"],
+ properties: [
+ "cflags", // unused
+ "shared_libs",
+ "static_libs",
+ ],
+}
+
+alphabet_cc_defaults {
+ name: "alphabet_sample_cc_defaults",
+ soong_config_variables: {
+ alphabet: {
+ a: {
+ shared_libs: [
+ "lib_a",
+ ],
+ },
+ b: {
+ shared_libs: [
+ "lib_b",
+ ],
+ },
+ conditions_default: {
+ static_libs: [
+ "lib_default",
+ ],
+ },
+ },
+ },
+}
+
+cc_binary {
+ name: "alphabet_binary",
+ srcs: ["main.cc"],
+ defaults: ["alphabet_sample_cc_defaults"],
+}`
+
+ 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 = "alphabet_binary",
+ deps = select({
+ "//build/bazel/product_variables:android__alphabet__a": [],
+ "//build/bazel/product_variables:android__alphabet__b": [],
+ "//conditions:default": ["//foo/bar:lib_default_bp2build_cc_library_static"],
+ }),
+ dynamic_deps = select({
+ "//build/bazel/product_variables:android__alphabet__a": ["//foo/bar:lib_a"],
+ "//build/bazel/product_variables:android__alphabet__b": ["//foo/bar:lib_b"],
+ "//conditions:default": [],
+ }),
+ local_includes = ["."],
+ srcs = ["main.cc"],
+)`}})
+}
+
+func TestSoongConfigModuleType_ProductVariableConfigWithPlatformConfig(t *testing.T) {
+ bp := `
+soong_config_bool_variable {
+ name: "special_build",
+}
+
+soong_config_module_type {
+ name: "alphabet_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "alphabet_module",
+ bool_variables: ["special_build"],
+ properties: ["enabled"],
+}
+
+alphabet_cc_defaults {
+ name: "alphabet_sample_cc_defaults",
+ soong_config_variables: {
+ special_build: {
+ enabled: true,
+ },
+ },
+}
+
+cc_binary {
+ name: "alphabet_binary",
+ srcs: ["main.cc"],
+ defaults: ["alphabet_sample_cc_defaults"],
+ enabled: false,
+ arch: {
+ x86_64: {
+ enabled: false,
+ },
+ },
+ target: {
+ darwin: {
+ 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(
+ name = "alphabet_binary",
+ local_includes = ["."],
+ srcs = ["main.cc"],
+ target_compatible_with = ["//build/bazel/product_variables:alphabet_module__special_build"] + select({
+ "//build/bazel/platforms/os_arch:android_x86_64": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:darwin_arm64": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:darwin_x86_64": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:linux_bionic_x86_64": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:linux_glibc_x86_64": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:linux_musl_x86_64": ["@platforms//:incompatible"],
+ "//build/bazel/platforms/os_arch:windows_x86_64": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ }),
+)`}})
+}
+
+func TestSoongConfigModuleType_ProductVariableConfigOverridesEnable(t *testing.T) {
+ bp := `
+soong_config_bool_variable {
+ name: "special_build",
+}
+
+soong_config_module_type {
+ name: "alphabet_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "alphabet_module",
+ bool_variables: ["special_build"],
+ properties: ["enabled"],
+}
+
+alphabet_cc_defaults {
+ name: "alphabet_sample_cc_defaults",
+ soong_config_variables: {
+ special_build: {
+ enabled: true,
+ },
+ },
+}
+
+cc_binary {
+ name: "alphabet_binary",
+ srcs: ["main.cc"],
+ defaults: ["alphabet_sample_cc_defaults"],
+ 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(
+ name = "alphabet_binary",
+ local_includes = ["."],
+ srcs = ["main.cc"],
+ target_compatible_with = ["//build/bazel/product_variables:alphabet_module__special_build"],
+)`}})
+}
+
+func TestSoongConfigModuleType_ProductVariableIgnoredIfEnabledByDefault(t *testing.T) {
+ bp := `
+soong_config_bool_variable {
+ name: "special_build",
+}
+
+soong_config_module_type {
+ name: "alphabet_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "alphabet_module",
+ bool_variables: ["special_build"],
+ properties: ["enabled"],
+}
+
+alphabet_cc_defaults {
+ name: "alphabet_sample_cc_defaults",
+ soong_config_variables: {
+ special_build: {
+ enabled: true,
+ },
+ },
+}
+
+cc_binary {
+ name: "alphabet_binary",
+ srcs: ["main.cc"],
+ 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(
+ name = "alphabet_binary",
+ local_includes = ["."],
+ srcs = ["main.cc"],
+)`}})
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 6c322ee..53b60fa 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -43,7 +43,7 @@
if len(errs) != 1 {
return false
}
- if errs[0].Error() == expectedErr.Error() {
+ if strings.Contains(errs[0].Error(), expectedErr.Error()) {
return true
}
@@ -74,16 +74,15 @@
}
type bp2buildTestCase struct {
- description string
- moduleTypeUnderTest string
- moduleTypeUnderTestFactory android.ModuleFactory
- moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
- blueprint string
- expectedBazelTargets []string
- filesystem map[string]string
- dir string
- expectedErr error
- unconvertedDepsMode unconvertedDepsMode
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ blueprint string
+ expectedBazelTargets []string
+ filesystem map[string]string
+ dir string
+ expectedErr error
+ unconvertedDepsMode unconvertedDepsMode
}
func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
@@ -105,7 +104,6 @@
registerModuleTypes(ctx)
ctx.RegisterModuleType(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestFactory)
ctx.RegisterBp2BuildConfig(bp2buildConfig)
- ctx.RegisterBp2BuildMutator(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterForBazelConversion()
_, parseErrs := ctx.ParseFileList(dir, toParse)
@@ -129,14 +127,18 @@
codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
codegenCtx.unconvertedDepMode = tc.unconvertedDepsMode
bazelTargets, errs := generateBazelTargetsForDir(codegenCtx, checkDir)
- if tc.expectedErr != nil && checkError(t, errs, tc.expectedErr) {
- return
+ if tc.expectedErr != nil {
+ if checkError(t, errs, tc.expectedErr) {
+ return
+ } else {
+ t.Errorf("Expected error: %q, got: %q", tc.expectedErr, errs)
+ }
} else {
android.FailIfErrored(t, errs)
}
if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount {
- t.Errorf("%s: Expected %d bazel target, got `%d``",
- tc.description, expectedCount, actualCount)
+ t.Errorf("%s: Expected %d bazel target (%s), got `%d`` (%s)",
+ tc.description, expectedCount, tc.expectedBazelTargets, actualCount, bazelTargets)
} else {
for i, target := range bazelTargets {
if w, g := tc.expectedBazelTargets[i], target.content; w != g {
@@ -149,15 +151,15 @@
}
type nestedProps struct {
- Nested_prop string
+ Nested_prop *string
}
type EmbeddedProps struct {
- Embedded_prop string
+ Embedded_prop *string
}
type OtherEmbeddedProps struct {
- Other_embedded_prop string
+ Other_embedded_prop *string
}
type customProps struct {
@@ -178,6 +180,9 @@
Arch_paths []string `android:"path,arch_variant"`
Arch_paths_exclude []string `android:"path,arch_variant"`
+
+ // Prop used to indicate this conversion should be 1 module -> multiple targets
+ One_to_many_prop *bool
}
type customModule struct {
@@ -262,86 +267,80 @@
}
type EmbeddedAttr struct {
- Embedded_attr string
+ Embedded_attr *string
}
type OtherEmbeddedAttr struct {
- Other_embedded_attr string
+ Other_embedded_attr *string
}
type customBazelModuleAttributes struct {
EmbeddedAttr
*OtherEmbeddedAttr
- String_prop string
+ String_ptr_prop *string
String_list_prop []string
Arch_paths bazel.LabelListAttribute
}
-func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
- if m, ok := ctx.Module().(*customModule); ok {
- if !m.ConvertWithBp2build(ctx) {
- return
- }
+func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ paths := bazel.LabelListAttribute{}
- paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.props.Arch_paths, m.props.Arch_paths_exclude))
+ if p := m.props.One_to_many_prop; p != nil && *p {
+ customBp2buildOneToMany(ctx, m)
+ return
+ }
- 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))
- }
+ 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))
}
}
-
- paths.ResolveExcludes()
-
- attrs := &customBazelModuleAttributes{
- String_prop: m.props.String_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}
- }
-
- props := bazel.BazelTargetModuleProperties{
- Rule_class: "custom",
- }
-
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
}
+
+ paths.ResolveExcludes()
+
+ attrs := &customBazelModuleAttributes{
+ 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}
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "custom",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
}
// A bp2build mutator that uses load statements and creates a 1:M mapping from
// module to target.
-func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) {
- if m, ok := ctx.Module().(*customModule); ok {
- if !m.ConvertWithBp2build(ctx) {
- return
- }
+func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) {
- baseName := m.Name()
- attrs := &customBazelModuleAttributes{}
+ baseName := m.Name()
+ attrs := &customBazelModuleAttributes{}
- myLibraryProps := bazel.BazelTargetModuleProperties{
- Rule_class: "my_library",
- Bzl_load_location: "//build/bazel/rules:rules.bzl",
- }
- ctx.CreateBazelTargetModule(myLibraryProps, android.CommonAttributes{Name: baseName}, attrs)
-
- protoLibraryProps := bazel.BazelTargetModuleProperties{
- Rule_class: "proto_library",
- Bzl_load_location: "//build/bazel/rules:proto.bzl",
- }
- ctx.CreateBazelTargetModule(protoLibraryProps, android.CommonAttributes{Name: baseName + "_proto_library_deps"}, attrs)
-
- myProtoLibraryProps := bazel.BazelTargetModuleProperties{
- Rule_class: "my_proto_library",
- Bzl_load_location: "//build/bazel/rules:proto.bzl",
- }
- ctx.CreateBazelTargetModule(myProtoLibraryProps, android.CommonAttributes{Name: baseName + "_my_proto_library_deps"}, attrs)
+ myLibraryProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "my_library",
+ Bzl_load_location: "//build/bazel/rules:rules.bzl",
}
+ ctx.CreateBazelTargetModule(myLibraryProps, android.CommonAttributes{Name: baseName}, attrs)
+
+ protoLibraryProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "proto_library",
+ Bzl_load_location: "//build/bazel/rules:proto.bzl",
+ }
+ ctx.CreateBazelTargetModule(protoLibraryProps, android.CommonAttributes{Name: baseName + "_proto_library_deps"}, attrs)
+
+ myProtoLibraryProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "my_proto_library",
+ Bzl_load_location: "//build/bazel/rules:proto.bzl",
+ }
+ ctx.CreateBazelTargetModule(myProtoLibraryProps, android.CommonAttributes{Name: baseName + "_my_proto_library_deps"}, attrs)
}
// Helper method for tests to easily access the targets in a dir.
@@ -353,7 +352,6 @@
func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
ctx.RegisterForBazelConversion()
}
@@ -364,3 +362,16 @@
bazel_module: { bp2build_available: false },
}`, typ, name)
}
+
+type attrNameToString map[string]string
+
+func makeBazelTarget(typ, name string, attrs attrNameToString) string {
+ attrStrings := make([]string, 0, len(attrs)+1)
+ attrStrings = append(attrStrings, fmt.Sprintf(` name = "%s",`, name))
+ for _, k := range android.SortedStringKeys(attrs) {
+ attrStrings = append(attrStrings, fmt.Sprintf(" %s = %s,", k, attrs[k]))
+ }
+ return fmt.Sprintf(`%s(
+%s
+)`, typ, strings.Join(attrStrings, "\n"))
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index fa1a84d..14b2d84 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -20,9 +20,9 @@
"strings"
"android/soong/android"
- _ "android/soong/cc/config"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
func init() {
@@ -41,6 +41,14 @@
CommandDeps: []string{"$ccCmd"},
},
"ccCmd", "cFlags")
+
+ stripRule = pctx.AndroidStaticRule("stripRule",
+ blueprint.RuleParams{
+ Command: `$stripCmd --strip-unneeded --remove-section=.rel.BTF ` +
+ `--remove-section=.rel.BTF.ext --remove-section=.BTF.ext $in -o $out`,
+ CommandDeps: []string{"$stripCmd"},
+ },
+ "stripCmd")
)
func registerBpfBuildComponents(ctx android.RegistrationContext) {
@@ -54,12 +62,21 @@
android.Module
OutputFiles(tag string) (android.Paths, error)
+
+ // Returns the sub install directory if the bpf module is included by apex.
+ SubDir() string
}
type BpfProperties struct {
Srcs []string `android:"path"`
Cflags []string
Include_dirs []string
+ Sub_dir string
+ // If set to true, generate BTF debug info for maps & programs
+ Btf *bool
+ Vendor *bool
+
+ VendorInternal bool `blueprint:"mutated"`
}
type bpf struct {
@@ -70,6 +87,41 @@
objs android.Paths
}
+var _ android.ImageInterface = (*bpf)(nil)
+
+func (bpf *bpf) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (bpf *bpf) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+ return !proptools.Bool(bpf.properties.Vendor)
+}
+
+func (bpf *bpf) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+ if proptools.Bool(bpf.properties.Vendor) {
+ return []string{"vendor"}
+ }
+ return nil
+}
+
+func (bpf *bpf) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+ bpf.properties.VendorInternal = variation == "vendor"
+}
+
func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) {
cflags := []string{
"-nostdlibinc",
@@ -83,9 +135,9 @@
// The architecture doesn't matter here, but asm/types.h is included by linux/types.h.
"-isystem bionic/libc/kernel/uapi/asm-arm64",
"-isystem bionic/libc/kernel/android/uapi",
+ "-I frameworks/libs/net/common/native/bpf_headers/include/bpf",
// TODO(b/149785767): only give access to specific file with AID_* constants
"-I system/core/libcutils/include",
- "-I system/bpf/progs/include",
"-I " + ctx.ModuleDir(),
}
@@ -95,10 +147,14 @@
cflags = append(cflags, bpf.properties.Cflags...)
+ if proptools.Bool(bpf.properties.Btf) {
+ cflags = append(cflags, "-g")
+ }
+
srcs := android.PathsForModuleSrc(ctx, bpf.properties.Srcs)
for _, src := range srcs {
- obj := android.ObjPathWithExt(ctx, "", src, "o")
+ obj := android.ObjPathWithExt(ctx, "unstripped", src, "o")
ctx.Build(pctx, android.BuildParams{
Rule: ccRule,
@@ -110,7 +166,21 @@
},
})
- bpf.objs = append(bpf.objs, obj.WithoutRel())
+ if proptools.Bool(bpf.properties.Btf) {
+ objStripped := android.ObjPathWithExt(ctx, "", src, "o")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: stripRule,
+ Input: obj,
+ Output: objStripped,
+ Args: map[string]string{
+ "stripCmd": "${config.ClangBin}/llvm-strip",
+ },
+ })
+ bpf.objs = append(bpf.objs, objStripped.WithoutRel())
+ } else {
+ bpf.objs = append(bpf.objs, obj.WithoutRel())
+ }
+
}
}
@@ -121,6 +191,15 @@
fmt.Fprintln(w)
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w)
+ var localModulePath string
+ if bpf.properties.VendorInternal {
+ localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/bpf"
+ } else {
+ localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf"
+ }
+ if len(bpf.properties.Sub_dir) > 0 {
+ localModulePath += "/" + bpf.properties.Sub_dir
+ }
for _, obj := range bpf.objs {
objName := name + "_" + obj.Base()
names = append(names, objName)
@@ -130,7 +209,7 @@
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String())
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base())
fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
- fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf")
+ fmt.Fprintln(w, localModulePath)
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
fmt.Fprintln(w)
}
@@ -154,6 +233,10 @@
}
}
+func (bpf *bpf) SubDir() string {
+ return bpf.properties.Sub_dir
+}
+
var _ android.OutputFileProducer = (*bpf)(nil)
func BpfFactory() android.Module {
diff --git a/bpfix/Android.bp b/bpfix/Android.bp
index 345dbd0..a72d9b4 100644
--- a/bpfix/Android.bp
+++ b/bpfix/Android.bp
@@ -52,5 +52,6 @@
],
deps: [
"blueprint-parser",
+ "blueprint-pathtools",
],
}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index a608630..4f7d88c 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -19,12 +19,18 @@
import (
"bytes"
"errors"
+ "flag"
"fmt"
"io"
+ "io/ioutil"
+ "os"
"path/filepath"
+ "reflect"
+ "sort"
"strings"
"github.com/google/blueprint/parser"
+ "github.com/google/blueprint/pathtools"
)
// Reformat takes a blueprint file as a string and returns a formatted version
@@ -137,9 +143,37 @@
Fix: runPatchListMod(removeObsoleteProperty("sanitize.scudo")),
},
{
+ Name: "removeAndroidLicenseKinds",
+ Fix: runPatchListMod(removeIncorrectProperties("android_license_kinds")),
+ },
+ {
+ Name: "removeAndroidLicenseConditions",
+ Fix: runPatchListMod(removeIncorrectProperties("android_license_conditions")),
+ },
+ {
+ Name: "removeAndroidLicenseFiles",
+ Fix: runPatchListMod(removeIncorrectProperties("android_license_files")),
+ },
+ {
Name: "formatFlagProperties",
Fix: runPatchListMod(formatFlagProperties),
},
+ {
+ Name: "removeResourcesAndAssetsIfDefault",
+ Fix: removeResourceAndAssetsIfDefault,
+ },
+}
+
+// for fix that only need to run once
+var fixStepsOnce = []FixStep{
+ {
+ Name: "haveSameLicense",
+ Fix: haveSameLicense,
+ },
+ {
+ Name: "rewriteLicenseProperties",
+ Fix: runPatchListMod(rewriteLicenseProperty(nil, "")),
+ },
}
func NewFixRequest() FixRequest {
@@ -196,6 +230,16 @@
return nil, err
}
+ // run fix that is expected to run once first
+ configOnce := NewFixRequest()
+ configOnce.steps = append(configOnce.steps, fixStepsOnce...)
+ if len(configOnce.steps) > 0 {
+ err = f.fixTreeOnce(configOnce)
+ if err != nil {
+ return nil, err
+ }
+ }
+
maxNumIterations := 20
i := 0
for {
@@ -570,7 +614,11 @@
}
var localModuleUpdate = map[string][]etcPrebuiltModuleUpdate{
- "HOST_OUT": {{prefix: "/etc", modType: "prebuilt_etc_host"}, {prefix: "/usr/share", modType: "prebuilt_usr_share_host"}},
+ "HOST_OUT": {
+ {prefix: "/etc", modType: "prebuilt_etc_host"},
+ {prefix: "/usr/share", modType: "prebuilt_usr_share_host"},
+ {prefix: "", modType: "prebuilt_root_host"},
+ },
"PRODUCT_OUT": {{prefix: "/system/etc"}, {prefix: "/vendor/etc", flags: []string{"proprietary"}}},
"TARGET_OUT": {{prefix: "/usr/share", modType: "prebuilt_usr_share"}, {prefix: "/fonts", modType: "prebuilt_font"},
{prefix: "/etc/firmware", modType: "prebuilt_firmware"}, {prefix: "/vendor/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}},
@@ -601,7 +649,7 @@
// 'srcs' --> 'src' conversion
convertToSingleSource(mod, "src")
- renameProperty(mod, "sub_dir", "relative_install_dir")
+ renameProperty(mod, "sub_dir", "relative_install_path")
// The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
// 'local_module_path'. Analyze its contents and create the correct sub_dir:,
@@ -842,6 +890,24 @@
return nil
}
+func removeResourceAndAssetsIfDefault(f *Fixer) error {
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+ resourceDirList, resourceDirFound := getLiteralListPropertyValue(mod, "resource_dirs")
+ if resourceDirFound && len(resourceDirList) == 1 && resourceDirList[0] == "res" {
+ removeProperty(mod, "resource_dirs")
+ }
+ assetDirList, assetDirFound := getLiteralListPropertyValue(mod, "asset_dirs")
+ if assetDirFound && len(assetDirList) == 1 && assetDirList[0] == "assets" {
+ removeProperty(mod, "asset_dirs")
+ }
+ }
+ return nil
+}
+
// Converts the default source list property, 'srcs', to a single source property with a given name.
// "LOCAL_MODULE" reference is also resolved during the conversion process.
func convertToSingleSource(mod *parser.Module, srcPropertyName string) {
@@ -1413,3 +1479,342 @@
}
return nil
}
+
+func rewriteLicenseProperty(fs pathtools.FileSystem, relativePath string) patchListModFunction {
+ return func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+ return rewriteLicenseProperties(mod, patchList, fs, relativePath)
+ }
+}
+
+// rewrite the "android_license_kinds" and "android_license_files" properties to a package module
+// (and a license module when needed).
+func rewriteLicenseProperties(mod *parser.Module, patchList *parser.PatchList, fs pathtools.FileSystem,
+ relativePath string) error {
+ // if a package module has been added, no more action is needed.
+ for _, patch := range *patchList {
+ if strings.Contains(patch.Replacement, "package {") {
+ return nil
+ }
+ }
+
+ // initial the fs
+ if fs == nil {
+ fs = pathtools.NewOsFs(os.Getenv("ANDROID_BUILD_TOP"))
+ }
+
+ // initial the relativePath
+ if len(relativePath) == 0 {
+ relativePath = getModuleRelativePath()
+ }
+ // validate the relativePath
+ ok := hasFile(relativePath+"/Android.mk", fs)
+ // some modules in the existing test cases in the androidmk_test.go do not have a valid path
+ if !ok && len(relativePath) > 0 {
+ return fmt.Errorf("Cannot find an Android.mk file at path %q", relativePath)
+ }
+
+ licenseKindsPropertyName := "android_license_kinds"
+ licenseFilesPropertyName := "android_license_files"
+
+ androidBpFileErr := "// Error: No Android.bp file is found at path\n" +
+ "// %s\n" +
+ "// Please add one there with the needed license module first.\n" +
+ "// Then reset the default_applicable_licenses property below with the license module name.\n"
+ licenseModuleErr := "// Error: Cannot get the name of the license module in the\n" +
+ "// %s file.\n" +
+ "// If no such license module exists, please add one there first.\n" +
+ "// Then reset the default_applicable_licenses property below with the license module name.\n"
+
+ defaultApplicableLicense := "Android-Apache-2.0"
+ var licenseModuleName, licensePatch string
+ var hasFileInParentDir bool
+
+ // when LOCAL_NOTICE_FILE is not empty
+ if hasNonEmptyLiteralListProperty(mod, licenseFilesPropertyName) {
+ hasFileInParentDir = hasValueStartWithTwoDotsLiteralList(mod, licenseFilesPropertyName)
+ // if have LOCAL_NOTICE_FILE outside the current directory, need to find and refer to the license
+ // module in the LOCAL_NOTICE_FILE location directly and no new license module needs to be created
+ if hasFileInParentDir {
+ bpPath, ok := getPathFromProperty(mod, licenseFilesPropertyName, fs, relativePath)
+ if !ok {
+ bpDir, err := getDirFromProperty(mod, licenseFilesPropertyName, fs, relativePath)
+ if err != nil {
+ return err
+ }
+ licensePatch += fmt.Sprintf(androidBpFileErr, bpDir)
+ defaultApplicableLicense = ""
+ } else {
+ licenseModuleName, _ = getModuleName(bpPath, "license", fs)
+ if len(licenseModuleName) == 0 {
+ licensePatch += fmt.Sprintf(licenseModuleErr, bpPath)
+ }
+ defaultApplicableLicense = licenseModuleName
+ }
+ } else {
+ // if have LOCAL_NOTICE_FILE in the current directory, need to create a new license module
+ if len(relativePath) == 0 {
+ return fmt.Errorf("Cannot obtain the relative path of the Android.mk file")
+ }
+ licenseModuleName = strings.Replace(relativePath, "/", "_", -1) + "_license"
+ defaultApplicableLicense = licenseModuleName
+ }
+ }
+
+ //add the package module
+ if hasNonEmptyLiteralListProperty(mod, licenseKindsPropertyName) {
+ licensePatch += "package {\n" +
+ " // See: http://go/android-license-faq\n" +
+ " default_applicable_licenses: [\n" +
+ " \"" + defaultApplicableLicense + "\",\n" +
+ " ],\n" +
+ "}\n" +
+ "\n"
+ }
+
+ // append the license module when necessary
+ // when LOCAL_NOTICE_FILE is not empty and in the current directory, create a new license module
+ // otherwise, use the above default license directly
+ if hasNonEmptyLiteralListProperty(mod, licenseFilesPropertyName) && !hasFileInParentDir {
+ licenseKinds, err := mergeLiteralListPropertyValue(mod, licenseKindsPropertyName)
+ if err != nil {
+ return err
+ }
+ licenseFiles, err := mergeLiteralListPropertyValue(mod, licenseFilesPropertyName)
+ if err != nil {
+ return err
+ }
+ licensePatch += "license {\n" +
+ " name: \"" + licenseModuleName + "\",\n" +
+ " visibility: [\":__subpackages__\"],\n" +
+ " license_kinds: [\n" +
+ licenseKinds +
+ " ],\n" +
+ " license_text: [\n" +
+ licenseFiles +
+ " ],\n" +
+ "}\n" +
+ "\n"
+ }
+
+ // add to the patchList
+ pos := mod.Pos().Offset
+ err := patchList.Add(pos, pos, licensePatch)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// merge the string vaules in a list property of a module into one string with expected format
+func mergeLiteralListPropertyValue(mod *parser.Module, property string) (s string, err error) {
+ listValue, ok := getLiteralListPropertyValue(mod, property)
+ if !ok {
+ // if do not find
+ return "", fmt.Errorf("Cannot retrieve the %s.%s field", mod.Type, property)
+ }
+ for i := 0; i < len(listValue); i++ {
+ s += " \"" + listValue[i] + "\",\n"
+ }
+ return s, nil
+}
+
+// check whether a string list property has any value starting with `../`
+func hasValueStartWithTwoDotsLiteralList(mod *parser.Module, property string) bool {
+ listValue, ok := getLiteralListPropertyValue(mod, property)
+ if ok {
+ for i := 0; i < len(listValue); i++ {
+ if strings.HasPrefix(listValue[i], "../") {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// get the relative path from ANDROID_BUILD_TOP to the Android.mk file to be converted
+func getModuleRelativePath() string {
+ // get the absolute path of the top of the tree
+ rootPath := os.Getenv("ANDROID_BUILD_TOP")
+ // get the absolute path of the `Android.mk` file to be converted
+ absPath := getModuleAbsolutePath()
+ // get the relative path of the `Android.mk` file to top of the tree
+ relModulePath, err := filepath.Rel(rootPath, absPath)
+ if err != nil {
+ return ""
+ }
+ return relModulePath
+}
+
+// get the absolute path of the Android.mk file to be converted
+func getModuleAbsolutePath() string {
+ // get the absolute path at where the `androidmk` commend is executed
+ curAbsPath, err := filepath.Abs(".")
+ if err != nil {
+ return ""
+ }
+ // the argument for the `androidmk` command could be
+ // 1. "./a/b/c/Android.mk"; 2. "a/b/c/Android.mk"; 3. "Android.mk"
+ argPath := flag.Arg(0)
+ if strings.HasPrefix(argPath, "./") {
+ argPath = strings.TrimPrefix(argPath, ".")
+ }
+ argPath = strings.TrimSuffix(argPath, "Android.mk")
+ if strings.HasSuffix(argPath, "/") {
+ argPath = strings.TrimSuffix(argPath, "/")
+ }
+ if len(argPath) > 0 && !strings.HasPrefix(argPath, "/") {
+ argPath = "/" + argPath
+ }
+ // get the absolute path of the `Android.mk` file to be converted
+ absPath := curAbsPath + argPath
+ return absPath
+}
+
+// check whether a file exists in a filesystem
+func hasFile(path string, fs pathtools.FileSystem) bool {
+ ok, _, _ := fs.Exists(path)
+ return ok
+}
+
+// get the directory where an `Android.bp` file and the property files are expected to locate
+func getDirFromProperty(mod *parser.Module, property string, fs pathtools.FileSystem, relativePath string) (string, error) {
+ listValue, ok := getLiteralListPropertyValue(mod, property)
+ if !ok {
+ // if do not find
+ return "", fmt.Errorf("Cannot retrieve the %s.%s property", mod.Type, property)
+ }
+ if len(listValue) == 0 {
+ // if empty
+ return "", fmt.Errorf("Cannot find the value of the %s.%s property", mod.Type, property)
+ }
+ if relativePath == "" {
+ relativePath = "."
+ }
+ _, isDir, _ := fs.Exists(relativePath)
+ if !isDir {
+ return "", fmt.Errorf("Cannot find the path %q", relativePath)
+ }
+ path := relativePath
+ for {
+ if !strings.HasPrefix(listValue[0], "../") {
+ break
+ }
+ path = filepath.Dir(path)
+ listValue[0] = strings.TrimPrefix(listValue[0], "../")
+ }
+ _, isDir, _ = fs.Exists(path)
+ if !isDir {
+ return "", fmt.Errorf("Cannot find the path %q", path)
+ }
+ return path, nil
+}
+
+// get the path of the `Android.bp` file at the expected location where the property files locate
+func getPathFromProperty(mod *parser.Module, property string, fs pathtools.FileSystem, relativePath string) (string, bool) {
+ dir, err := getDirFromProperty(mod, property, fs, relativePath)
+ if err != nil {
+ return "", false
+ }
+ ok := hasFile(dir+"/Android.bp", fs)
+ if !ok {
+ return "", false
+ }
+ return dir + "/Android.bp", true
+}
+
+// parse an Android.bp file to get the name of the first module with type of moduleType
+func getModuleName(path string, moduleType string, fs pathtools.FileSystem) (string, error) {
+ tree, err := parserPath(path, fs)
+ if err != nil {
+ return "", err
+ }
+ for _, def := range tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok || mod.Type != moduleType {
+ continue
+ }
+ prop, ok := mod.GetProperty("name")
+ if !ok {
+ return "", fmt.Errorf("Cannot get the %s."+"name property", mod.Type)
+ }
+ propVal, ok := prop.Value.(*parser.String)
+ if ok {
+ return propVal.Value, nil
+ }
+ }
+ return "", fmt.Errorf("Cannot find the value of the %s."+"name property", moduleType)
+}
+
+// parse an Android.bp file with the specific path
+func parserPath(path string, fs pathtools.FileSystem) (tree *parser.File, err error) {
+ f, err := fs.Open(path)
+ if err != nil {
+ return tree, err
+ }
+ defer f.Close()
+ fileContent, _ := ioutil.ReadAll(f)
+ tree, err = parse(path, bytes.NewBufferString(string(fileContent)))
+ if err != nil {
+ return tree, err
+ }
+ return tree, nil
+}
+
+// remove the incorrect property that Soong does not support
+func removeIncorrectProperties(propName string) patchListModFunction {
+ return removeObsoleteProperty(propName)
+}
+
+// the modules on the same Android.mk file are expected to have the same license
+func haveSameLicense(f *Fixer) error {
+ androidLicenseProperties := []string{
+ "android_license_kinds",
+ "android_license_conditions",
+ "android_license_files",
+ }
+
+ var prevModuleName string
+ var prevLicenseKindsVals, prevLicenseConditionsVals, prevLicenseFilesVals []string
+ prevLicenseVals := [][]string{
+ prevLicenseKindsVals,
+ prevLicenseConditionsVals,
+ prevLicenseFilesVals,
+ }
+
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+ for idx, property := range androidLicenseProperties {
+ curModuleName, ok := getLiteralStringPropertyValue(mod, "name")
+ // some modules in the existing test cases in the androidmk_test.go do not have name property
+ hasNameProperty := hasProperty(mod, "name")
+ if hasNameProperty && (!ok || len(curModuleName) == 0) {
+ return fmt.Errorf("Cannot retrieve the name property of a module of %s type.", mod.Type)
+ }
+ curVals, ok := getLiteralListPropertyValue(mod, property)
+ // some modules in the existing test cases in the androidmk_test.go do not have license-related property
+ hasLicenseProperty := hasProperty(mod, property)
+ if hasLicenseProperty && (!ok || len(curVals) == 0) {
+ // if do not find the property, or no value is found for the property
+ return fmt.Errorf("Cannot retrieve the %s.%s property", mod.Type, property)
+ }
+ if len(prevLicenseVals[idx]) > 0 {
+ if !reflect.DeepEqual(prevLicenseVals[idx], curVals) {
+ return fmt.Errorf("Modules %s and %s are expected to have the same %s property.",
+ prevModuleName, curModuleName, property)
+ }
+ }
+ sort.Strings(curVals)
+ prevLicenseVals[idx] = curVals
+ prevModuleName = curModuleName
+ }
+ }
+ return nil
+}
+
+func hasProperty(mod *parser.Module, propName string) bool {
+ _, ok := mod.GetProperty(propName)
+ return ok
+}
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index d8772c1..17b3c24 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -19,12 +19,12 @@
import (
"bytes"
"fmt"
+ "reflect"
"strings"
"testing"
- "reflect"
-
"github.com/google/blueprint/parser"
+ "github.com/google/blueprint/pathtools"
)
// TODO(jeffrygaston) remove this when position is removed from ParseNode (in b/38325146) and we can directly do reflect.DeepEqual
@@ -125,34 +125,103 @@
implFilterListTest(t, []string{}, []string{}, []string{})
}
-func runPass(t *testing.T, in, out string, innerTest func(*Fixer) error) {
- expected, err := Reformat(out)
+func checkError(t *testing.T, in, expectedErr string, innerTest func(*Fixer) error) {
+ expected := preProcessOutErr(expectedErr)
+ runTestOnce(t, in, expected, innerTest)
+}
+
+func runTestOnce(t *testing.T, in, expected string, innerTest func(*Fixer) error) {
+ fixer, err := preProcessIn(in)
if err != nil {
t.Fatal(err)
}
+ out, err := runFixerOnce(fixer, innerTest)
+ if err != nil {
+ out = err.Error()
+ }
+
+ compareResult := compareOutExpected(in, out, expected)
+ if len(compareResult) > 0 {
+ t.Errorf(compareResult)
+ }
+}
+
+func preProcessOutErr(expectedErr string) string {
+ expected := strings.TrimSpace(expectedErr)
+ return expected
+}
+
+func preProcessOut(out string) (expected string, err error) {
+ expected, err = Reformat(out)
+ if err != nil {
+ return expected, err
+ }
+ return expected, nil
+}
+
+func preProcessIn(in string) (fixer *Fixer, err error) {
in, err = Reformat(in)
if err != nil {
- t.Fatal(err)
+ return fixer, err
}
tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
if errs != nil {
- t.Fatal(errs)
+ return fixer, err
}
- fixer := NewFixer(tree)
+ fixer = NewFixer(tree)
+
+ return fixer, nil
+}
+
+func runFixerOnce(fixer *Fixer, innerTest func(*Fixer) error) (string, error) {
+ err := innerTest(fixer)
+ if err != nil {
+ return "", err
+ }
+
+ out, err := parser.Print(fixer.tree)
+ if err != nil {
+ return "", err
+ }
+ return string(out), nil
+}
+
+func compareOutExpected(in, out, expected string) string {
+ if out != expected {
+ return fmt.Sprintf("output didn't match:\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
+ in, expected, out)
+ }
+ return ""
+}
+
+func runPassOnce(t *testing.T, in, out string, innerTest func(*Fixer) error) {
+ expected, err := preProcessOut(out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ runTestOnce(t, in, expected, innerTest)
+}
+
+func runPass(t *testing.T, in, out string, innerTest func(*Fixer) error) {
+ expected, err := preProcessOut(out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ fixer, err := preProcessIn(in)
+ if err != nil {
+ t.Fatal(err)
+ }
got := ""
prev := "foo"
passes := 0
for got != prev && passes < 10 {
- err := innerTest(fixer)
- if err != nil {
- t.Fatal(err)
- }
-
- out, err := parser.Print(fixer.tree)
+ out, err = runFixerOnce(fixer, innerTest)
if err != nil {
t.Fatal(err)
}
@@ -162,9 +231,9 @@
passes++
}
- if got != expected {
- t.Errorf("output didn't match:\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
- in, expected, got)
+ compareResult := compareOutExpected(in, out, expected)
+ if len(compareResult) > 0 {
+ t.Errorf(compareResult)
}
}
@@ -754,7 +823,47 @@
out: `prebuilt_etc {
name: "foo",
src: "bar",
- relative_install_dir: "baz",
+ relative_install_path: "baz",
+ }
+ `,
+ },
+ {
+ name: "prebuilt_etc sub_dir",
+ in: `
+ prebuilt_etc_host {
+ name: "foo",
+ src: "bar",
+ local_module_path: {
+ var: "HOST_OUT",
+ fixed: "/etc/baz",
+ },
+ }
+ `,
+ out: `prebuilt_etc_host {
+ name: "foo",
+ src: "bar",
+ relative_install_path: "baz",
+
+ }
+ `,
+ },
+ {
+ name: "prebuilt_etc sub_dir",
+ in: `
+ prebuilt_etc_host {
+ name: "foo",
+ src: "bar",
+ local_module_path: {
+ var: "HOST_OUT",
+ fixed: "/baz/sub",
+ },
+ }
+ `,
+ out: `prebuilt_root_host {
+ name: "foo",
+ src: "bar",
+ relative_install_path: "baz/sub",
+
}
`,
},
@@ -1608,3 +1717,469 @@
})
}
}
+
+func TestRewriteLicenseProperty(t *testing.T) {
+ mockFs := pathtools.MockFs(map[string][]byte{
+ "a/b/c/d/Android.mk": []byte("this is not important."),
+ "a/b/LicenseFile1": []byte("LicenseFile1"),
+ "a/b/LicenseFile2": []byte("LicenseFile2"),
+ "a/b/Android.bp": []byte("license {\n\tname: \"reuse_a_b_license\",\n}\n"),
+ })
+ relativePath := "a/b/c/d"
+ relativePathErr := "a/b/c"
+ tests := []struct {
+ name string
+ in string
+ fs pathtools.FileSystem
+ path string
+ out string
+ }{
+ {
+ name: "license rewriting with one module",
+ in: `
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+ `,
+ out: `
+ package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+ }
+
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+ `,
+ },
+ {
+ name: "license rewriting with two modules",
+ in: `
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind1"],
+ android_license_conditions: ["license_notice1"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind2"],
+ android_license_conditions: ["license_notice2"],
+ }
+ `,
+ out: `
+ package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+ }
+
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind1"],
+ android_license_conditions: ["license_notice1"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind2"],
+ android_license_conditions: ["license_notice2"],
+ }
+ `,
+ },
+ {
+ name: "license rewriting with license files in the current directory",
+ in: `
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ android_license_files: ["LicenseFile1", "LicenseFile2",],
+ }
+ `,
+ fs: mockFs,
+ path: relativePath,
+ out: `
+ package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "a_b_c_d_license",
+ ],
+ }
+
+ license {
+ name: "a_b_c_d_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "license_kind",
+ ],
+ license_text: [
+ "LicenseFile1",
+ "LicenseFile2",
+ ],
+ }
+
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ android_license_files: [
+ "LicenseFile1",
+ "LicenseFile2",
+ ],
+ }
+ `,
+ },
+ {
+ name: "license rewriting with license files outside the current directory",
+ in: `
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ android_license_files: ["../../LicenseFile1", "../../LicenseFile2",],
+ }
+ `,
+ fs: mockFs,
+ path: relativePath,
+ out: `
+ package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "reuse_a_b_license",
+ ],
+ }
+
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ android_license_files: [
+ "../../LicenseFile1",
+ "../../LicenseFile2",
+ ],
+ }
+ `,
+ },
+ {
+ name: "license rewriting with no Android.bp file in the expected location",
+ in: `
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ android_license_files: ["../../LicenseFile1", "../../LicenseFile2",],
+ }
+ `,
+ fs: pathtools.MockFs(map[string][]byte{
+ "a/b/c/d/Android.mk": []byte("this is not important."),
+ "a/b/LicenseFile1": []byte("LicenseFile1"),
+ "a/b/LicenseFile2": []byte("LicenseFile2"),
+ "a/Android.bp": []byte("license {\n\tname: \"reuse_a_b_license\",\n}\n"),
+ }),
+ path: relativePath,
+ out: `
+ // Error: No Android.bp file is found at path
+ // a/b
+ // Please add one there with the needed license module first.
+ // Then reset the default_applicable_licenses property below with the license module name.
+ package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "",
+ ],
+ }
+
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ android_license_files: [
+ "../../LicenseFile1",
+ "../../LicenseFile2",
+ ],
+ }
+ `,
+ },
+ {
+ name: "license rewriting with an Android.bp file without a license module",
+ in: `
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ android_license_files: ["../../LicenseFile1", "../../LicenseFile2",],
+ }
+ `,
+ fs: pathtools.MockFs(map[string][]byte{
+ "a/b/c/d/Android.mk": []byte("this is not important."),
+ "a/b/LicenseFile1": []byte("LicenseFile1"),
+ "a/b/LicenseFile2": []byte("LicenseFile2"),
+ "a/b/Android.bp": []byte("non_license {\n\tname: \"reuse_a_b_license\",\n}\n"),
+ }),
+ path: relativePath,
+ out: `
+ // Error: Cannot get the name of the license module in the
+ // a/b/Android.bp file.
+ // If no such license module exists, please add one there first.
+ // Then reset the default_applicable_licenses property below with the license module name.
+ package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "",
+ ],
+ }
+
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ android_license_files: [
+ "../../LicenseFile1",
+ "../../LicenseFile2",
+ ],
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPassOnce(t, test.in, test.out, runPatchListMod(rewriteLicenseProperty(test.fs, test.path)))
+ })
+ }
+
+ testErrs := []struct {
+ name string
+ in string
+ fs pathtools.FileSystem
+ path string
+ expectedErr string
+ }{
+ {
+ name: "license rewriting with a wrong path",
+ in: `
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ android_license_files: ["../../LicenseFile1", "../../LicenseFile2",],
+ }
+ `,
+ fs: mockFs,
+ path: relativePathErr,
+ expectedErr: `
+ Cannot find an Android.mk file at path "a/b/c"
+ `,
+ },
+ }
+ for _, test := range testErrs {
+ t.Run(test.name, func(t *testing.T) {
+ checkError(t, test.in, test.expectedErr, runPatchListMod(rewriteLicenseProperty(test.fs, test.path)))
+ })
+ }
+}
+
+func TestHaveSameLicense(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "two modules with the same license",
+ in: `
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+ `,
+ out: `
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPassOnce(t, test.in, test.out, func(fixer *Fixer) error {
+ return haveSameLicense(fixer)
+ })
+ })
+ }
+ testErrs := []struct {
+ name string
+ in string
+ expectedErr string
+ }{
+ {
+ name: "two modules will different licenses",
+ in: `
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind1"],
+ android_license_conditions: ["license_notice1"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind2"],
+ android_license_conditions: ["license_notice2"],
+ }
+ `,
+ expectedErr: `
+ Modules foo1 and foo2 are expected to have the same android_license_kinds property.
+ `,
+ },
+ }
+ for _, test := range testErrs {
+ t.Run(test.name, func(t *testing.T) {
+ checkError(t, test.in, test.expectedErr, func(fixer *Fixer) error {
+ return haveSameLicense(fixer)
+ })
+ })
+ }
+}
+
+func TestRemoveResourceAndAssetsIfDefault(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "resource_dirs default",
+ in: `
+ android_app {
+ name: "foo",
+ resource_dirs: ["res"],
+ }
+ `,
+ out: `
+ android_app {
+ name: "foo",
+
+ }
+ `,
+ },
+ {
+ name: "resource_dirs not default",
+ in: `
+ android_app {
+ name: "foo",
+ resource_dirs: ["reso"],
+ }
+ `,
+ out: `
+ android_app {
+ name: "foo",
+ resource_dirs: ["reso"],
+ }
+ `,
+ },
+ {
+ name: "resource_dirs includes not default",
+ in: `
+ android_app {
+ name: "foo",
+ resource_dirs: ["res", "reso"],
+ }
+ `,
+ out: `
+ android_app {
+ name: "foo",
+ resource_dirs: ["res", "reso"],
+ }
+ `,
+ }, {
+ name: "asset_dirs default",
+ in: `
+ android_app {
+ name: "foo",
+ asset_dirs: ["assets"],
+ }
+ `,
+ out: `
+ android_app {
+ name: "foo",
+
+ }
+ `,
+ },
+ {
+ name: "asset_dirs not default",
+ in: `
+ android_app {
+ name: "foo",
+ asset_dirs: ["assety"],
+ }
+ `,
+ out: `
+ android_app {
+ name: "foo",
+ asset_dirs: ["assety"],
+ }
+ `,
+ },
+ {
+ name: "asset_dirs includes not default",
+ in: `
+ android_app {
+ name: "foo",
+ asset_dirs: ["assets", "assety"],
+ }
+ `,
+ out: `
+ android_app {
+ name: "foo",
+ asset_dirs: ["assets", "assety"],
+ }
+ `,
+ },
+ {
+ name: "resource_dirs and asset_dirs both default",
+ in: `
+ android_app {
+ name: "foo",
+ asset_dirs: ["assets"],
+ resource_dirs: ["res"],
+ }
+ `,
+ out: `
+ android_app {
+ name: "foo",
+
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPassOnce(t, test.in, test.out, func(fixer *Fixer) error {
+ return removeResourceAndAssetsIfDefault(fixer)
+ })
+ })
+ }
+}
diff --git a/build_kzip.bash b/build_kzip.bash
index 5655067..aff2d6d 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -61,5 +61,5 @@
# Pack
# TODO(asmundak): this should be done by soong.
declare -r allkzip="$KZIP_NAME.kzip"
-"$out/soong/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find "$out" -name '*.kzip')
+"$out/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find "$out" -name '*.kzip')
diff --git a/build_test.bash b/build_test.bash
index b039285..b6d00e2 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -50,7 +50,9 @@
echo
echo "Free disk space:"
-df -h
+# Ignore df errors because it errors out on gvfsd file systems
+# but still displays most of the useful info we need
+df -h || true
echo
echo "Running Bazel smoke test..."
diff --git a/cc/Android.bp b/cc/Android.bp
index 07aa7cb..9103a48 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -19,10 +19,11 @@
"soong-tradefed",
],
srcs: [
+ "afdo.go",
"androidmk.go",
"api_level.go",
- "builder.go",
"bp2build.go",
+ "builder.go",
"cc.go",
"ccdeps.go",
"check.go",
@@ -67,7 +68,6 @@
"native_bridge_sdk_trait.go",
"object.go",
"test.go",
- "toolchain_library.go",
"ndk_abi.go",
"ndk_headers.go",
@@ -88,6 +88,7 @@
"stub_library.go",
],
testSrcs: [
+ "afdo_test.go",
"cc_test.go",
"compiler_test.go",
"gen_test.go",
diff --git a/cc/OWNERS b/cc/OWNERS
index 6d7c30a..a438b15 100644
--- a/cc/OWNERS
+++ b/cc/OWNERS
@@ -1,4 +1,4 @@
per-file ndk_*.go = danalbert@google.com
per-file tidy.go = srhines@google.com, chh@google.com
-per-file lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@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
new file mode 100644
index 0000000..d7cce77
--- /dev/null
+++ b/cc/afdo.go
@@ -0,0 +1,200 @@
+// 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"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+var (
+ globalAfdoProfileProjects = []string{
+ "vendor/google_data/pgo_profile/sampling/",
+ "toolchain/pgo-profiles/sampling/",
+ }
+)
+
+var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
+
+const afdoCFlagsFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s"
+
+func getAfdoProfileProjects(config android.DeviceConfig) []string {
+ return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string {
+ return append(globalAfdoProfileProjects, config.AfdoAdditionalProfileDirs()...)
+ })
+}
+
+func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) {
+ getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
+}
+
+type AfdoProperties struct {
+ Afdo bool
+
+ AfdoTarget *string `blueprint:"mutated"`
+ AfdoDeps []string `blueprint:"mutated"`
+}
+
+type afdo struct {
+ Properties AfdoProperties
+}
+
+func (afdo *afdo) props() []interface{} {
+ 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)
+ }
+ }
+}
+
+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()
+
+ 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)
+ }
+ }
+
+ return flags
+}
+
+// Propagate afdo requirements down from binaries
+func afdoDepsMutator(mctx android.TopDownMutatorContext) {
+ if m, ok := mctx.Module().(*Module); ok && m.afdo.AfdoEnabled() {
+ afdoTarget := *m.afdo.Properties.AfdoTarget
+ mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
+ tag := mctx.OtherModuleDependencyTag(dep)
+ libTag, isLibTag := tag.(libraryDependencyTag)
+
+ // Do not recurse down non-static dependencies
+ if isLibTag {
+ if !libTag.static() {
+ return false
+ }
+ } else {
+ if tag != objDepTag && tag != reuseObjTag {
+ return false
+ }
+ }
+
+ if dep, ok := dep.(*Module); ok {
+ dep.afdo.Properties.AfdoDeps = append(dep.afdo.Properties.AfdoDeps, afdoTarget)
+ }
+
+ return true
+ })
+ }
+}
+
+// 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))
+ }
+
+ variationNames := []string{""}
+ afdoDeps := android.FirstUniqueStrings(m.afdo.Properties.AfdoDeps)
+ for _, dep := range afdoDeps {
+ variationNames = append(variationNames, encodeTarget(dep))
+ }
+ if len(variationNames) > 1 {
+ modules := mctx.CreateVariations(variationNames...)
+ for i, name := range variationNames {
+ if name == "" {
+ continue
+ }
+ variation := modules[i].(*Module)
+ variation.Properties.PreventInstall = true
+ variation.Properties.HideFromMake = true
+ variation.afdo.Properties.AfdoTarget = proptools.StringPtr(decodeTarget(name))
+ }
+ }
+ }
+}
+
+// Encode target name to variation name.
+func encodeTarget(target string) string {
+ if target == "" {
+ return ""
+ }
+ return "afdo-" + target
+}
+
+// Decode target name from variation name.
+func decodeTarget(variation string) string {
+ if variation == "" {
+ return ""
+ }
+ return strings.TrimPrefix(variation, "afdo-")
+}
diff --git a/cc/afdo_test.go b/cc/afdo_test.go
new file mode 100644
index 0000000..5515464
--- /dev/null
+++ b/cc/afdo_test.go
@@ -0,0 +1,70 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+func TestAfdoDeps(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libTest",
+ srcs: ["foo.c"],
+ static_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,
+ ).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()
+
+ 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
+ }
+
+ if !hasDep(libTest, libFoo) {
+ t.Errorf("libTest missing dependency on afdo variant of libFoo")
+ }
+
+ if !hasDep(libFoo, libBar) {
+ t.Errorf("libTest missing dependency on afdo variant of libBar")
+ }
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 93283d0..9290272 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -15,6 +15,8 @@
package cc
import (
+ "github.com/google/blueprint/proptools"
+
"fmt"
"io"
"path/filepath"
@@ -80,7 +82,7 @@
// to be installed. And this is breaking some older devices (like marlin)
// where system.img is small.
Required: c.Properties.AndroidMkRuntimeLibs,
- Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
+ Include: "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
@@ -106,6 +108,9 @@
if len(c.Properties.AndroidMkHeaderLibs) > 0 {
entries.AddStrings("LOCAL_HEADER_LIBRARIES", c.Properties.AndroidMkHeaderLibs...)
}
+ if len(c.Properties.AndroidMkRuntimeLibs) > 0 {
+ entries.AddStrings("LOCAL_RUNTIME_LIBRARIES", c.Properties.AndroidMkRuntimeLibs...)
+ }
entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
if c.UseVndk() {
entries.SetBool("LOCAL_USE_VNDK", true)
@@ -241,7 +246,7 @@
entries.Class = "SHARED_LIBRARIES"
entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_SOONG_TOC", library.toc().String())
- if !library.buildStubs() {
+ if !library.buildStubs() && library.unstrippedOutputFile != nil {
entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", library.unstrippedOutputFile.String())
}
if len(library.Properties.Overrides) > 0 {
@@ -263,9 +268,10 @@
library.androidMkWriteExportedFlags(entries)
library.androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries)
- _, _, ext := android.SplitFileExt(entries.OutputFile.Path().Base())
-
- entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
+ if entries.OutputFile.Valid() {
+ _, _, ext := android.SplitFileExt(entries.OutputFile.Path().Base())
+ entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
+ }
if library.coverageOutputFile.Valid() {
entries.SetString("LOCAL_PREBUILT_COVERAGE_ARCHIVE", library.coverageOutputFile.String())
@@ -393,6 +399,9 @@
}
entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(test.Properties.Per_testcase_directory))
+ if len(test.Properties.Data_bins) > 0 {
+ entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+ }
})
AndroidMkWriteTestData(test.data, entries)
@@ -438,26 +447,13 @@
ctx.subAndroidMk(entries, test.libraryDecorator)
}
-func (library *toolchainLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
- entries.Class = "STATIC_LIBRARIES"
- entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- _, suffix, _ := android.SplitFileExt(entries.OutputFile.Path().Base())
- entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
- })
-}
-
func (installer *baseInstaller) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
if installer.path == (android.InstallPath{}) {
return
}
- // Soong installation is only supported for host modules. Have Make
- // installation trigger Soong installation.
- if ctx.Target().Os.Class == android.Host {
- entries.OutputFile = android.OptionalPathForPath(installer.path)
- }
entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- path, file := filepath.Split(installer.path.ToMakePath().String())
+ path, file := filepath.Split(installer.path.String())
stem, suffix, _ := android.SplitFileExt(file)
entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
entries.SetString("LOCAL_MODULE_PATH", path)
@@ -536,7 +532,13 @@
c.libraryDecorator.androidMkWriteExportedFlags(entries)
if c.shared() || c.static() {
- path, file := filepath.Split(c.path.ToMakePath().String())
+ src := c.path.String()
+ // For static libraries which aren't installed, directly use Src to extract filename.
+ // This is safe: generated snapshot modules have a real path as Src, not a module
+ if c.static() {
+ src = proptools.String(c.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)
@@ -587,8 +589,8 @@
if p.properties.Check_elf_files != nil {
entries.SetBool("LOCAL_CHECK_ELF_FILES", *p.properties.Check_elf_files)
} else {
- // soong_cc_prebuilt.mk does not include check_elf_file.mk by default
- // because cc_library_shared and cc_binary use soong_cc_prebuilt.mk as well.
+ // soong_cc_rust_prebuilt.mk does not include check_elf_file.mk by default
+ // because cc_library_shared and cc_binary use soong_cc_rust_prebuilt.mk as well.
// In order to turn on prebuilt ABI checker, set `LOCAL_CHECK_ELF_FILES` to
// true if `p.properties.Check_elf_files` is not specified.
entries.SetBool("LOCAL_CHECK_ELF_FILES", true)
diff --git a/cc/binary.go b/cc/binary.go
index b423c50..6c7d581 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -18,8 +18,10 @@
"path/filepath"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
)
type BinaryLinkerProperties struct {
@@ -62,18 +64,19 @@
func RegisterBinaryBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("cc_binary", BinaryFactory)
- ctx.RegisterModuleType("cc_binary_host", binaryHostFactory)
+ ctx.RegisterModuleType("cc_binary_host", BinaryHostFactory)
}
// cc_binary produces a binary that is runnable on a device.
func BinaryFactory() android.Module {
- module, _ := NewBinary(android.HostAndDeviceSupported)
+ module, _ := newBinary(android.HostAndDeviceSupported, true)
+ module.bazelHandler = &ccBinaryBazelHandler{module: module}
return module.Init()
}
// cc_binary_host produces a binary that is runnable on a host.
-func binaryHostFactory() android.Module {
- module, _ := NewBinary(android.HostSupported)
+func BinaryHostFactory() android.Module {
+ module, _ := newBinary(android.HostSupported, true)
return module.Init()
}
@@ -191,6 +194,10 @@
// Individual module implementations which comprise a C++ binary should call this function,
// set some fields on the result, and then call the Init function.
func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
+ return newBinary(hod, true)
+}
+
+func newBinary(hod android.HostOrDeviceSupported, bazelable bool) (*Module, *binaryDecorator) {
module := newModule(hod, android.MultilibFirst)
binary := &binaryDecorator{
baseLinker: NewBaseLinker(module.sanitize),
@@ -199,6 +206,7 @@
module.compiler = NewBaseCompiler()
module.linker = binary
module.installer = binary
+ module.bazelable = bazelable
// Allow module to be added as member of an sdk/module_exports.
module.sdkMemberTypes = []android.SdkMemberType{
@@ -343,6 +351,12 @@
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-dynamic-linker")
}
+ if ctx.Darwin() && deps.DarwinSecondArchOutput.Valid() {
+ fatOutputFile := outputFile
+ outputFile = android.PathForModuleOut(ctx, "pre-fat", fileName)
+ transformDarwinUniversalBinary(ctx, fatOutputFile, outputFile, deps.DarwinSecondArchOutput.Path())
+ }
+
builderFlags := flagsToBuilderFlags(flags)
stripFlags := flagsToStripFlags(flags)
if binary.stripper.NeedsStrip(ctx) {
@@ -387,7 +401,7 @@
}
}
- var validations android.WritablePaths
+ var validations android.Paths
// Handle host bionic linker symbols.
if ctx.Os() == android.LinuxBionic && !binary.static() {
@@ -410,9 +424,10 @@
linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...)
linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
+ linkerDeps = append(linkerDeps, ndkSharedLibDeps(ctx)...)
}
- linkerDeps = append(linkerDeps, objs.tidyFiles...)
+ validations = append(validations, objs.tidyDepFiles...)
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
// Register link action.
@@ -541,3 +556,117 @@
},
})
}
+
+type ccBinaryBazelHandler struct {
+ android.BazelHandler
+
+ module *Module
+}
+
+func (handler *ccBinaryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+ 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
+}
+
+func binaryBp2build(ctx android.TopDownMutatorContext, m *Module, typ string) {
+ baseAttrs := bp2BuildParseBaseProps(ctx, m)
+ binaryLinkerAttrs := bp2buildBinaryLinkerProps(ctx, m)
+
+ if proptools.BoolDefault(binaryLinkerAttrs.Linkshared, true) {
+ baseAttrs.implementationDynamicDeps.Add(baseAttrs.protoDependency)
+ } else {
+ baseAttrs.implementationDeps.Add(baseAttrs.protoDependency)
+ }
+
+ attrs := &binaryAttributes{
+ binaryLinkerAttrs: binaryLinkerAttrs,
+
+ Srcs: baseAttrs.srcs,
+ Srcs_c: baseAttrs.cSrcs,
+ Srcs_as: baseAttrs.asSrcs,
+
+ Copts: baseAttrs.copts,
+ Cppflags: baseAttrs.cppFlags,
+ Conlyflags: baseAttrs.conlyFlags,
+ Asflags: baseAttrs.asFlags,
+
+ Deps: baseAttrs.implementationDeps,
+ Dynamic_deps: baseAttrs.implementationDynamicDeps,
+ Whole_archive_deps: baseAttrs.wholeArchiveDeps,
+ System_deps: baseAttrs.systemDynamicDeps,
+
+ Local_includes: baseAttrs.localIncludes,
+ Absolute_includes: baseAttrs.absoluteIncludes,
+ Linkopts: baseAttrs.linkopts,
+ Link_crt: baseAttrs.linkCrt,
+ Use_libcrt: baseAttrs.useLibcrt,
+ Rtti: baseAttrs.rtti,
+ Stl: baseAttrs.stl,
+ Cpp_std: baseAttrs.cppStd,
+
+ Additional_linker_inputs: baseAttrs.additionalLinkerInputs,
+
+ Strip: stripAttributes{
+ Keep_symbols: baseAttrs.stripKeepSymbols,
+ Keep_symbols_and_debug_frame: baseAttrs.stripKeepSymbolsAndDebugFrame,
+ Keep_symbols_list: baseAttrs.stripKeepSymbolsList,
+ All: baseAttrs.stripAll,
+ None: baseAttrs.stripNone,
+ },
+
+ Features: baseAttrs.features,
+ }
+
+ ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_binary",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
+ },
+ android.CommonAttributes{Name: m.Name()},
+ attrs)
+}
+
+// binaryAttributes contains Bazel attributes corresponding to a cc binary
+type binaryAttributes struct {
+ binaryLinkerAttrs
+ Srcs bazel.LabelListAttribute
+ Srcs_c bazel.LabelListAttribute
+ Srcs_as bazel.LabelListAttribute
+
+ Copts bazel.StringListAttribute
+ Cppflags bazel.StringListAttribute
+ Conlyflags bazel.StringListAttribute
+ Asflags bazel.StringListAttribute
+
+ Deps bazel.LabelListAttribute
+ Dynamic_deps bazel.LabelListAttribute
+ Whole_archive_deps bazel.LabelListAttribute
+ System_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
+
+ Rtti bazel.BoolAttribute
+ Stl *string
+ Cpp_std *string
+
+ Strip stripAttributes
+
+ Features bazel.StringListAttribute
+}
diff --git a/cc/binary_test.go b/cc/binary_test.go
new file mode 100644
index 0000000..8ec3871
--- /dev/null
+++ b/cc/binary_test.go
@@ -0,0 +1,51 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestCcBinaryWithBazel(t *testing.T) {
+ bp := `
+cc_binary {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToOutputFiles: map[string][]string{
+ "//foo/bar:bar": []string{"foo"},
+ },
+ }
+ ctx := testCcWithConfig(t, config)
+
+ binMod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module()
+ producer := binMod.(android.OutputFileProducer)
+ outputFiles, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_binary outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/foo"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
+ expectedUnStrippedFile := "outputbase/execroot/__main__/foo"
+ android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 22bd90b..42fc0e4 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -14,7 +14,9 @@
package cc
import (
+ "fmt"
"path/filepath"
+ "regexp"
"strings"
"android/soong/android"
@@ -25,70 +27,84 @@
"github.com/google/blueprint/proptools"
)
+const (
+ cSrcPartition = "c"
+ asSrcPartition = "as"
+ cppSrcPartition = "cpp"
+ protoSrcPartition = "proto"
+)
+
+var (
+ // ignoring case, checks for proto or protos as an independent word in the name, whether at the
+ // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
+ filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
+)
+
// staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
// properties which apply to either the shared or static version of a cc_library module.
type staticOrSharedAttributes struct {
Srcs bazel.LabelListAttribute
Srcs_c bazel.LabelListAttribute
Srcs_as bazel.LabelListAttribute
+ Hdrs bazel.LabelListAttribute
Copts bazel.StringListAttribute
- Deps bazel.LabelListAttribute
- Implementation_deps bazel.LabelListAttribute
- Dynamic_deps bazel.LabelListAttribute
- Implementation_dynamic_deps bazel.LabelListAttribute
- Whole_archive_deps bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Implementation_deps bazel.LabelListAttribute
+ Dynamic_deps bazel.LabelListAttribute
+ Implementation_dynamic_deps bazel.LabelListAttribute
+ Whole_archive_deps bazel.LabelListAttribute
+ Implementation_whole_archive_deps bazel.LabelListAttribute
System_dynamic_deps bazel.LabelListAttribute
+
+ Enabled bazel.BoolAttribute
}
-func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) {
- // Check that a module is a filegroup type named <label>.
- isFilegroupNamed := func(m android.Module, fullLabel string) bool {
- if ctx.OtherModuleType(m) != "filegroup" {
- return false
- }
- labelParts := strings.Split(fullLabel, ":")
- if len(labelParts) > 2 {
- // There should not be more than one colon in a label.
- ctx.ModuleErrorf("%s is not a valid Bazel label for a filegroup", fullLabel)
- }
- return m.Name() == labelParts[len(labelParts)-1]
+func groupSrcsByExtension(ctx android.BazelConversionPathContext, srcs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
+ // Check that a module is a filegroup type
+ isFilegroup := func(m blueprint.Module) bool {
+ return ctx.OtherModuleType(m) == "filegroup"
}
// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
// macro.
addSuffixForFilegroup := func(suffix string) bazel.LabelMapper {
- return func(ctx bazel.OtherModuleContext, label string) (string, bool) {
- m, exists := ctx.ModuleFromName(label)
- if !exists {
- return label, false
+ return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+ m, exists := ctx.ModuleFromName(label.OriginalModuleName)
+ labelStr := label.Label
+ if !exists || !isFilegroup(m) {
+ return labelStr, false
}
- aModule, _ := m.(android.Module)
- if !isFilegroupNamed(aModule, label) {
- return label, false
- }
- return label + suffix, true
+ return labelStr + suffix, true
}
}
+ isProtoFilegroup := func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+ m, exists := ctx.ModuleFromName(label.OriginalModuleName)
+ labelStr := label.Label
+ if !exists || !isFilegroup(m) {
+ return labelStr, false
+ }
+ likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName)
+ return labelStr, likelyProtos
+ }
+
// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
partitioned := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
- "c": bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
- "as": bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
+ protoSrcPartition: bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup},
+ cSrcPartition: bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
+ asSrcPartition: bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
// 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.
- "cpp": 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},
})
- cSrcs = partitioned["c"]
- asSrcs = partitioned["as"]
- cppSrcs = partitioned["cpp"]
- return
+ return partitioned
}
// bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
-func bp2BuildParseLibProps(ctx android.TopDownMutatorContext, module *Module, isStatic bool) staticOrSharedAttributes {
+func bp2BuildParseLibProps(ctx android.BazelConversionPathContext, module *Module, isStatic bool) staticOrSharedAttributes {
lib, ok := module.compiler.(*libraryDecorator)
if !ok {
return staticOrSharedAttributes{}
@@ -97,12 +113,12 @@
}
// bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
-func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
+func bp2BuildParseSharedProps(ctx android.BazelConversionPathContext, module *Module) staticOrSharedAttributes {
return bp2BuildParseLibProps(ctx, module, false)
}
// bp2buildParseStaticProps returns the attributes for the static variant of a cc_library.
-func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
+func bp2BuildParseStaticProps(ctx android.BazelConversionPathContext, module *Module) staticOrSharedAttributes {
return bp2BuildParseLibProps(ctx, module, true)
}
@@ -111,9 +127,15 @@
implementation bazel.LabelList
}
-type bazelLabelForDepsFn func(android.TopDownMutatorContext, []string) bazel.LabelList
+type bazelLabelForDepsFn func(android.BazelConversionPathContext, []string) bazel.LabelList
-func partitionExportedAndImplementationsDeps(ctx android.TopDownMutatorContext, allDeps, exportedDeps []string, fn bazelLabelForDepsFn) depsPartition {
+func maybePartitionExportedAndImplementationsDeps(ctx android.BazelConversionPathContext, exportsDeps bool, allDeps, exportedDeps []string, fn bazelLabelForDepsFn) depsPartition {
+ if !exportsDeps {
+ return depsPartition{
+ implementation: fn(ctx, allDeps),
+ }
+ }
+
implementation, export := android.FilterList(allDeps, exportedDeps)
return depsPartition{
@@ -122,9 +144,14 @@
}
}
-type bazelLabelForDepsExcludesFn func(android.TopDownMutatorContext, []string, []string) bazel.LabelList
+type bazelLabelForDepsExcludesFn func(android.BazelConversionPathContext, []string, []string) bazel.LabelList
-func partitionExportedAndImplementationsDepsExcludes(ctx android.TopDownMutatorContext, allDeps, excludes, exportedDeps []string, fn bazelLabelForDepsExcludesFn) depsPartition {
+func maybePartitionExportedAndImplementationsDepsExcludes(ctx android.BazelConversionPathContext, exportsDeps bool, allDeps, excludes, exportedDeps []string, fn bazelLabelForDepsExcludesFn) depsPartition {
+ if !exportsDeps {
+ return depsPartition{
+ implementation: fn(ctx, allDeps, excludes),
+ }
+ }
implementation, export := android.FilterList(allDeps, exportedDeps)
return depsPartition{
@@ -133,23 +160,24 @@
}
}
-func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
+func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
attrs := staticOrSharedAttributes{}
setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
- attrs.Copts.SetSelectValue(axis, config, props.Cflags)
+ attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
- staticDeps := partitionExportedAndImplementationsDeps(ctx, props.Static_libs, props.Export_static_lib_headers, bazelLabelForStaticDeps)
+ staticDeps := maybePartitionExportedAndImplementationsDeps(ctx, true, props.Static_libs, props.Export_static_lib_headers, bazelLabelForStaticDeps)
attrs.Deps.SetSelectValue(axis, config, staticDeps.export)
attrs.Implementation_deps.SetSelectValue(axis, config, staticDeps.implementation)
- sharedDeps := partitionExportedAndImplementationsDeps(ctx, props.Shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDeps)
+ sharedDeps := maybePartitionExportedAndImplementationsDeps(ctx, true, props.Shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDeps)
attrs.Dynamic_deps.SetSelectValue(axis, config, sharedDeps.export)
attrs.Implementation_dynamic_deps.SetSelectValue(axis, config, sharedDeps.implementation)
attrs.Whole_archive_deps.SetSelectValue(axis, config, bazelLabelForWholeDeps(ctx, props.Whole_static_libs))
+ attrs.Enabled.SetSelectValue(axis, config, props.Enabled)
}
// system_dynamic_deps distinguishes between nil/empty list behavior:
// nil -> use default values
@@ -174,10 +202,15 @@
}
}
- cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.Srcs)
- attrs.Srcs = cppSrcs
- attrs.Srcs_c = cSrcs
- attrs.Srcs_as = asSrcs
+ partitionedSrcs := groupSrcsByExtension(ctx, attrs.Srcs)
+ attrs.Srcs = partitionedSrcs[cppSrcPartition]
+ attrs.Srcs_c = partitionedSrcs[cSrcPartition]
+ attrs.Srcs_as = partitionedSrcs[asSrcPartition]
+
+ 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")
+ }
return attrs
}
@@ -188,7 +221,7 @@
}
// NOTE: Used outside of Soong repo project, in the clangprebuilts.go bootstrap_go_package
-func Bp2BuildParsePrebuiltLibraryProps(ctx android.TopDownMutatorContext, module *Module) prebuiltAttributes {
+func Bp2BuildParsePrebuiltLibraryProps(ctx android.BazelConversionPathContext, module *Module) prebuiltAttributes {
var srcLabelAttribute bazel.LabelAttribute
for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltLinkerProperties{}) {
@@ -211,6 +244,13 @@
}
}
+type baseAttributes struct {
+ compilerAttributes
+ linkerAttributes
+
+ protoDependency *bazel.LabelAttribute
+}
+
// Convenience struct to hold all attributes parsed from compiler properties.
type compilerAttributes struct {
// Options for all languages
@@ -225,241 +265,432 @@
cppFlags bazel.StringListAttribute
srcs bazel.LabelListAttribute
+ hdrs bazel.LabelListAttribute
+
rtti bazel.BoolAttribute
- stl *string
+
+ // Not affected by arch variants
+ stl *string
+ cStd *string
+ cppStd *string
localIncludes bazel.StringListAttribute
absoluteIncludes bazel.StringListAttribute
+
+ includes BazelIncludes
+
+ protoSrcs bazel.LabelListAttribute
+
+ stubsSymbolFile *string
+ stubsVersions bazel.StringListAttribute
}
-// bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
-func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Module) compilerAttributes {
- var srcs bazel.LabelListAttribute
- var copts bazel.StringListAttribute
- var asFlags bazel.StringListAttribute
- var conlyFlags bazel.StringListAttribute
- var cppFlags bazel.StringListAttribute
- var rtti bazel.BoolAttribute
- var localIncludes bazel.StringListAttribute
- var absoluteIncludes bazel.StringListAttribute
+type filterOutFn func(string) bool
- parseCommandLineFlags := func(soongFlags []string) []string {
- var result []string
- for _, flag := range soongFlags {
- // Soong's cflags can contain spaces, like `-include header.h`. For
- // Bazel's copts, split them up to be compatible with the
- // no_copts_tokenization feature.
- result = append(result, strings.Split(flag, " ")...)
+func filterOutStdFlag(flag string) bool {
+ return strings.HasPrefix(flag, "-std=")
+}
+
+func parseCommandLineFlags(soongFlags []string, filterOut filterOutFn) []string {
+ var result []string
+ for _, flag := range soongFlags {
+ if filterOut != nil && filterOut(flag) {
+ continue
}
- return result
+ // Soong's cflags can contain spaces, like `-include header.h`. For
+ // Bazel's copts, split them up to be compatible with the
+ // no_copts_tokenization feature.
+ result = append(result, strings.Split(flag, " ")...)
+ }
+ return result
+}
+
+func (ca *compilerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis, config string, props *BaseCompilerProperties) {
+ // If there's arch specific srcs or exclude_srcs, generate a select entry for it.
+ // TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
+ if srcsList, ok := parseSrcs(ctx, props); ok {
+ ca.srcs.SetSelectValue(axis, config, srcsList)
}
- // Parse srcs from an arch or OS's props value.
- parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList {
- // Add srcs-like dependencies such as generated files.
- // First create a LabelList containing these dependencies, then merge the values with srcs.
- generatedHdrsAndSrcs := baseCompilerProps.Generated_headers
- generatedHdrsAndSrcs = append(generatedHdrsAndSrcs, baseCompilerProps.Generated_sources...)
- generatedHdrsAndSrcsLabelList := android.BazelLabelForModuleDeps(ctx, generatedHdrsAndSrcs)
-
- allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, baseCompilerProps.Srcs, baseCompilerProps.Exclude_srcs)
- return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedHdrsAndSrcsLabelList)
- }
-
- archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
- for axis, configToProps := range archVariantCompilerProps {
- for config, props := range configToProps {
- if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
- // If there's arch specific srcs or exclude_srcs, generate a select entry for it.
- // TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
- if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
- srcsList := parseSrcs(baseCompilerProps)
- srcs.SetSelectValue(axis, config, srcsList)
- }
-
- archVariantCopts := parseCommandLineFlags(baseCompilerProps.Cflags)
- archVariantAsflags := parseCommandLineFlags(baseCompilerProps.Asflags)
-
- localIncludeDirs := baseCompilerProps.Local_include_dirs
- if axis == bazel.NoConfigAxis && includeBuildDirectory(baseCompilerProps.Include_build_directory) {
- localIncludeDirs = append(localIncludeDirs, ".")
- }
-
- absoluteIncludes.SetSelectValue(axis, config, baseCompilerProps.Include_dirs)
- localIncludes.SetSelectValue(axis, config, localIncludeDirs)
-
- copts.SetSelectValue(axis, config, archVariantCopts)
- asFlags.SetSelectValue(axis, config, archVariantAsflags)
- conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Conlyflags))
- cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Cppflags))
- rtti.SetSelectValue(axis, config, baseCompilerProps.Rtti)
- }
+ localIncludeDirs := props.Local_include_dirs
+ if axis == bazel.NoConfigAxis {
+ ca.cStd, ca.cppStd = bp2buildResolveCppStdValue(props.C_std, props.Cpp_std, props.Gnu_extensions)
+ if includeBuildDirectory(props.Include_build_directory) {
+ localIncludeDirs = append(localIncludeDirs, ".")
}
}
- srcs.ResolveExcludes()
- absoluteIncludes.DeduplicateAxesFromBase()
- localIncludes.DeduplicateAxesFromBase()
+ ca.absoluteIncludes.SetSelectValue(axis, config, props.Include_dirs)
+ ca.localIncludes.SetSelectValue(axis, config, localIncludeDirs)
- productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
- "Cflags": &copts,
- "Asflags": &asFlags,
- "CppFlags": &cppFlags,
- }
- productVariableProps := android.ProductVariableProperties(ctx)
- for propName, attr := range productVarPropNameToAttribute {
- if props, exists := productVariableProps[propName]; exists {
- for _, prop := range props {
- flags, ok := prop.Property.([]string)
- if !ok {
- ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
- }
- newFlags, _ := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable)
- attr.SetSelectValue(bazel.ProductVariableConfigurationAxis(prop.FullConfig), prop.FullConfig, newFlags)
- }
- }
- }
+ // 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.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.rtti.SetSelectValue(axis, config, props.Rtti)
+}
- srcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, srcs)
-
- var stl *string = nil
+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 {
- if stl == nil {
- stl = stlProps.Stl
- } else {
- if stl != stlProps.Stl {
- ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *stl, stlProps.Stl)
- }
- }
+ 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)
+ }
+ }
+ }
+ }
+}
+
+func (ca *compilerAttributes) convertProductVariables(ctx android.BazelConversionPathContext, productVariableProps android.ProductConfigProperties) {
+ productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
+ "Cflags": &ca.copts,
+ "Asflags": &ca.asFlags,
+ "CppFlags": &ca.cppFlags,
+ }
+ for propName, attr := range productVarPropNameToAttribute {
+ if productConfigProps, exists := productVariableProps[propName]; exists {
+ for productConfigProp, prop := range productConfigProps {
+ flags, ok := prop.([]string)
+ if !ok {
+ ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
+ }
+ newFlags, _ := bazel.TryVariableSubstitutions(flags, productConfigProp.Name)
+ attr.SetSelectValue(productConfigProp.ConfigurationAxis(), productConfigProp.SelectKey(), newFlags)
+ }
+ }
+ }
+}
+
+func (ca *compilerAttributes) finalize(ctx android.BazelConversionPathContext, implementationHdrs bazel.LabelListAttribute) {
+ ca.srcs.ResolveExcludes()
+ partitionedSrcs := groupSrcsByExtension(ctx, ca.srcs)
+
+ ca.protoSrcs = partitionedSrcs[protoSrcPartition]
+
+ for p, lla := range partitionedSrcs {
+ // if there are no sources, there is no need for headers
+ if lla.IsEmpty() {
+ continue
+ }
+ lla.Append(implementationHdrs)
+ partitionedSrcs[p] = lla
+ }
+
+ ca.srcs = partitionedSrcs[cppSrcPartition]
+ ca.cSrcs = partitionedSrcs[cSrcPartition]
+ ca.asSrcs = partitionedSrcs[asSrcPartition]
+
+ ca.absoluteIncludes.DeduplicateAxesFromBase()
+ ca.localIncludes.DeduplicateAxesFromBase()
+}
+
+// Parse srcs from an arch or OS's props value.
+func parseSrcs(ctx android.BazelConversionPathContext, props *BaseCompilerProperties) (bazel.LabelList, bool) {
+ anySrcs := false
+ // Add srcs-like dependencies such as generated files.
+ // First create a LabelList containing these dependencies, then merge the values with srcs.
+ generatedSrcsLabelList := android.BazelLabelForModuleDepsExcludes(ctx, props.Generated_sources, props.Exclude_generated_sources)
+ if len(props.Generated_sources) > 0 || len(props.Exclude_generated_sources) > 0 {
+ anySrcs = true
+ }
+
+ 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
+ // 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"
+ }
+
+ cStdVal, cppStdVal = maybeReplaceGnuToC(gnu_extensions, cStdVal, cppStdVal)
+ var c_std_prop, cpp_std_prop *string
+ if cStdVal != "" {
+ c_std_prop = &cStdVal
+ }
+ if cppStdVal != "" {
+ cpp_std_prop = &cppStdVal
+ }
+
+ return c_std_prop, cpp_std_prop
+}
+
+// packageFromLabel extracts package from a fully-qualified or relative Label and whether the label
+// is fully-qualified.
+// e.g. fully-qualified "//a/b:foo" -> "a/b", true, relative: ":bar" -> ".", false
+func packageFromLabel(label string) (string, bool) {
+ split := strings.Split(label, ":")
+ if len(split) != 2 {
+ return "", false
+ }
+ if split[0] == "" {
+ return ".", false
+ }
+ // remove leading "//"
+ return split[0][2:], true
+}
+
+// includesFromLabelList extracts relative/absolute includes from a bazel.LabelList>
+func includesFromLabelList(labelList bazel.LabelList) (relative, absolute []string) {
+ for _, hdr := range labelList.Includes {
+ if pkg, hasPkg := packageFromLabel(hdr.Label); hasPkg {
+ absolute = append(absolute, pkg)
+ } else if pkg != "" {
+ relative = append(relative, pkg)
+ }
+ }
+ return relative, absolute
+}
+
+// bp2BuildParseBaseProps returns all compiler, linker, library attributes of a cc module..
+func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module) baseAttributes {
+ archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
+ archVariantLinkerProps := module.GetArchVariantProperties(ctx, &BaseLinkerProperties{})
+ archVariantLibraryProperties := module.GetArchVariantProperties(ctx, &LibraryProperties{})
+
+ var implementationHdrs bazel.LabelListAttribute
+
+ axisToConfigs := map[bazel.ConfigurationAxis]map[string]bool{}
+ allAxesAndConfigs := func(cp android.ConfigurationAxisToArchVariantProperties) {
+ for axis, configMap := range cp {
+ if _, ok := axisToConfigs[axis]; !ok {
+ axisToConfigs[axis] = map[string]bool{}
+ }
+ for config, _ := range configMap {
+ axisToConfigs[axis][config] = true
+ }
+ }
+ }
+ allAxesAndConfigs(archVariantCompilerProps)
+ allAxesAndConfigs(archVariantLinkerProps)
+ allAxesAndConfigs(archVariantLibraryProperties)
+
+ compilerAttrs := compilerAttributes{}
+ linkerAttrs := linkerAttributes{}
+
+ for axis, configs := range axisToConfigs {
+ for config, _ := range configs {
+ var allHdrs []string
+ if baseCompilerProps, ok := archVariantCompilerProps[axis][config].(*BaseCompilerProperties); ok {
+ allHdrs = baseCompilerProps.Generated_headers
+
+ (&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, config, baseCompilerProps)
+ }
+
+ var exportHdrs []string
+
+ if baseLinkerProps, ok := archVariantLinkerProps[axis][config].(*BaseLinkerProperties); ok {
+ exportHdrs = baseLinkerProps.Export_generated_headers
+
+ (&linkerAttrs).bp2buildForAxisAndConfig(ctx, module.Binary(), axis, config, baseLinkerProps)
+ }
+ headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
+ implementationHdrs.SetSelectValue(axis, config, headers.implementation)
+ compilerAttrs.hdrs.SetSelectValue(axis, config, headers.export)
+
+ exportIncludes, exportAbsoluteIncludes := includesFromLabelList(headers.export)
+ compilerAttrs.includes.Includes.SetSelectValue(axis, config, exportIncludes)
+ compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, config, exportAbsoluteIncludes)
+
+ includes, absoluteIncludes := includesFromLabelList(headers.implementation)
+ currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, config)
+ 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 {
+ if axis == bazel.NoConfigAxis {
+ compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
+ compilerAttrs.stubsVersions.SetSelectValue(axis, config, libraryProps.Stubs.Versions)
}
}
}
}
- return compilerAttributes{
- copts: copts,
- srcs: srcs,
- asFlags: asFlags,
- asSrcs: asSrcs,
- cSrcs: cSrcs,
- conlyFlags: conlyFlags,
- cppFlags: cppFlags,
- rtti: rtti,
- stl: stl,
- localIncludes: localIncludes,
- absoluteIncludes: absoluteIncludes,
+ compilerAttrs.convertStlProps(ctx, module)
+ (&linkerAttrs).convertStripProps(ctx, module)
+
+ productVariableProps := android.ProductVariableProperties(ctx)
+
+ (&compilerAttrs).convertProductVariables(ctx, productVariableProps)
+ (&linkerAttrs).convertProductVariables(ctx, productVariableProps)
+
+ (&compilerAttrs).finalize(ctx, implementationHdrs)
+ (&linkerAttrs).finalize(ctx)
+
+ protoDep := bp2buildProto(ctx, module, compilerAttrs.protoSrcs)
+
+ // bp2buildProto will only set wholeStaticLib or implementationWholeStaticLib, but we don't know
+ // which. This will add the newly generated proto library to the appropriate attribute and nothing
+ // to the other
+ (&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
+ (&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
+
+ return baseAttributes{
+ compilerAttrs,
+ linkerAttrs,
+ protoDep.protoDep,
}
}
// Convenience struct to hold all attributes parsed from linker properties.
type linkerAttributes struct {
- deps bazel.LabelListAttribute
- implementationDeps bazel.LabelListAttribute
- dynamicDeps bazel.LabelListAttribute
- implementationDynamicDeps bazel.LabelListAttribute
- wholeArchiveDeps bazel.LabelListAttribute
- systemDynamicDeps bazel.LabelListAttribute
+ deps bazel.LabelListAttribute
+ implementationDeps bazel.LabelListAttribute
+ dynamicDeps bazel.LabelListAttribute
+ implementationDynamicDeps 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
- versionScript bazel.LabelAttribute
+ additionalLinkerInputs bazel.LabelListAttribute
stripKeepSymbols bazel.BoolAttribute
stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
stripKeepSymbolsList bazel.StringListAttribute
stripAll bazel.BoolAttribute
stripNone bazel.BoolAttribute
+ features bazel.StringListAttribute
}
-// FIXME(b/187655838): Use the existing linkerFlags() function instead of duplicating logic here
-func getBp2BuildLinkerFlags(linkerProperties *BaseLinkerProperties) []string {
- flags := linkerProperties.Ldflags
- if !BoolDefault(linkerProperties.Pack_relocations, true) {
- flags = append(flags, "-Wl,--pack-dyn-relocs=none")
+var (
+ soongSystemSharedLibs = []string{"libc", "libm", "libdl"}
+)
+
+func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, isBinary bool, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
+ // 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
+
+ // Excludes to parallel Soong:
+ // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
+ staticLibs := android.FirstUniqueStrings(props.Static_libs)
+ 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)
+
+ (&hDeps.export).Append(staticDeps.export)
+ la.deps.SetSelectValue(axis, config, hDeps.export)
+
+ (&hDeps.implementation).Append(staticDeps.implementation)
+ la.implementationDeps.SetSelectValue(axis, config, hDeps.implementation)
+
+ wholeStaticLibs := android.FirstUniqueStrings(props.Whole_static_libs)
+ la.wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, props.Exclude_static_libs))
+
+ systemSharedLibs := props.System_shared_libs
+ // systemSharedLibs distinguishes between nil/empty list behavior:
+ // nil -> use default values
+ // empty list -> no values specified
+ if len(systemSharedLibs) > 0 {
+ systemSharedLibs = android.FirstUniqueStrings(systemSharedLibs)
}
- return flags
+ la.systemDynamicDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs))
+
+ sharedLibs := android.FirstUniqueStrings(props.Shared_libs)
+ excludeSharedLibs := props.Exclude_shared_libs
+ usedSystem := android.FilterListPred(sharedLibs, func(s string) bool {
+ return android.InList(s, soongSystemSharedLibs) && !android.InList(s, excludeSharedLibs)
+ })
+ for _, el := range usedSystem {
+ if la.usedSystemDynamicDepAsDynamicDep == nil {
+ la.usedSystemDynamicDepAsDynamicDep = map[string]bool{}
+ }
+ la.usedSystemDynamicDepAsDynamicDep[el] = true
+ }
+
+ 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)
+
+ if !BoolDefault(props.Pack_relocations, packRelocationsDefault) {
+ axisFeatures = append(axisFeatures, "disable_pack_relocations")
+ }
+
+ if Bool(props.Allow_undefined_symbols) {
+ axisFeatures = append(axisFeatures, "-no_undefined_symbols")
+ }
+
+ var linkerFlags []string
+ if len(props.Ldflags) > 0 {
+ linkerFlags = append(linkerFlags, proptools.NinjaEscapeList(props.Ldflags)...)
+ // binaries remove static flag if -shared is in the linker flags
+ if isBinary && android.InList("-shared", linkerFlags) {
+ axisFeatures = append(axisFeatures, "-static_flag")
+ }
+ }
+ if props.Version_script != nil {
+ label := android.BazelLabelForModuleSrcSingle(ctx, *props.Version_script)
+ la.additionalLinkerInputs.SetSelectValue(axis, config, bazel.LabelList{Includes: []bazel.Label{label}})
+ linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--version-script,$(location %s)", label.Label))
+ }
+ 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)
+ }
}
-// bp2BuildParseLinkerProps parses the linker properties of a module, including
-// configurable attribute values.
-func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
-
- var headerDeps bazel.LabelListAttribute
- var implementationHeaderDeps bazel.LabelListAttribute
- var deps bazel.LabelListAttribute
- var implementationDeps bazel.LabelListAttribute
- var dynamicDeps bazel.LabelListAttribute
- var implementationDynamicDeps bazel.LabelListAttribute
- var wholeArchiveDeps bazel.LabelListAttribute
- systemSharedDeps := bazel.LabelListAttribute{ForceSpecifyEmptyList: true}
-
- var linkopts bazel.StringListAttribute
- var versionScript bazel.LabelAttribute
- var useLibcrt bazel.BoolAttribute
-
- var stripKeepSymbols bazel.BoolAttribute
- var stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
- var stripKeepSymbolsList bazel.StringListAttribute
- var stripAll bazel.BoolAttribute
- var stripNone bazel.BoolAttribute
-
+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 {
- stripKeepSymbols.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols)
- stripKeepSymbolsList.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_list)
- stripKeepSymbolsAndDebugFrame.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_and_debug_frame)
- stripAll.SetSelectValue(axis, config, stripProperties.Strip.All)
- stripNone.SetSelectValue(axis, config, stripProperties.Strip.None)
+ 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)
}
}
}
+}
- for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
- for config, props := range configToProps {
- if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
-
- // 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(baseLinkerProps.Static_libs)
- staticDeps := partitionExportedAndImplementationsDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs, baseLinkerProps.Export_static_lib_headers, bazelLabelForStaticDepsExcludes)
- deps.SetSelectValue(axis, config, staticDeps.export)
- implementationDeps.SetSelectValue(axis, config, staticDeps.implementation)
-
- wholeStaticLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
- wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, baseLinkerProps.Exclude_static_libs))
-
- systemSharedLibs := baseLinkerProps.System_shared_libs
- // systemSharedLibs distinguishes between nil/empty list behavior:
- // nil -> use default values
- // empty list -> no values specified
- if len(systemSharedLibs) > 0 {
- systemSharedLibs = android.FirstUniqueStrings(systemSharedLibs)
- }
- systemSharedDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs))
-
- sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
- sharedDeps := partitionExportedAndImplementationsDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs, baseLinkerProps.Export_shared_lib_headers, bazelLabelForSharedDepsExcludes)
- dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
- implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
-
- headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
- hDeps := partitionExportedAndImplementationsDeps(ctx, headerLibs, baseLinkerProps.Export_header_lib_headers, bazelLabelForHeaderDeps)
-
- headerDeps.SetSelectValue(axis, config, hDeps.export)
- implementationHeaderDeps.SetSelectValue(axis, config, hDeps.implementation)
-
- linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps))
- if baseLinkerProps.Version_script != nil {
- versionScript.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
- }
- useLibcrt.SetSelectValue(axis, config, baseLinkerProps.libCrt())
- }
- }
- }
+func (la *linkerAttributes) convertProductVariables(ctx android.BazelConversionPathContext, productVariableProps android.ProductConfigProperties) {
type productVarDep struct {
// the name of the corresponding excludes field, if one exists
@@ -467,17 +698,16 @@
// reference to the bazel attribute that should be set for the given product variable config
attribute *bazel.LabelListAttribute
- depResolutionFunc func(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList
+ depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
}
productVarToDepFields := map[string]productVarDep{
// product variables do not support exclude_shared_libs
- "Shared_libs": productVarDep{attribute: &implementationDynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
- "Static_libs": productVarDep{"Exclude_static_libs", &implementationDeps, bazelLabelForStaticDepsExcludes},
- "Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
+ "Shared_libs": {attribute: &la.implementationDynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
+ "Static_libs": {"Exclude_static_libs", &la.implementationDeps, bazelLabelForStaticDepsExcludes},
+ "Whole_static_libs": {"Exclude_static_libs", &la.wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
}
- productVariableProps := android.ProductVariableProperties(ctx)
for name, dep := range productVarToDepFields {
props, exists := productVariableProps[name]
excludeProps, excludesExists := productVariableProps[dep.excludesField]
@@ -485,62 +715,59 @@
if !exists && !excludesExists {
continue
}
- // collect all the configurations that an include or exclude property exists for.
- // we want to iterate all configurations rather than either the include or exclude because for a
- // particular configuration we may have only and include or only an exclude to handle
- configs := make(map[string]bool, len(props)+len(excludeProps))
- for config := range props {
- configs[config] = true
+ // Collect all the configurations that an include or exclude property exists for.
+ // We want to iterate all configurations rather than either the include or exclude because, for a
+ // particular configuration, we may have either only an include or an exclude to handle.
+ productConfigProps := make(map[android.ProductConfigProperty]bool, len(props)+len(excludeProps))
+ for p := range props {
+ productConfigProps[p] = true
}
- for config := range excludeProps {
- configs[config] = true
+ for p := range excludeProps {
+ productConfigProps[p] = true
}
- for config := range configs {
- prop, includesExists := props[config]
- excludesProp, excludesExists := excludeProps[config]
+ for productConfigProp := range productConfigProps {
+ prop, includesExists := props[productConfigProp]
+ excludesProp, excludesExists := excludeProps[productConfigProp]
var includes, excludes []string
var ok bool
// if there was no includes/excludes property, casting fails and that's expected
- if includes, ok = prop.Property.([]string); includesExists && !ok {
+ if includes, ok = prop.([]string); includesExists && !ok {
ctx.ModuleErrorf("Could not convert product variable %s property", name)
}
- if excludes, ok = excludesProp.Property.([]string); excludesExists && !ok {
+ if excludes, ok = excludesProp.([]string); excludesExists && !ok {
ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField)
}
- dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes))
+ dep.attribute.EmitEmptyList = productConfigProp.AlwaysEmit()
+ dep.attribute.SetSelectValue(
+ productConfigProp.ConfigurationAxis(),
+ productConfigProp.SelectKey(),
+ dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes),
+ )
}
}
+}
- headerDeps.Append(deps)
- implementationHeaderDeps.Append(implementationDeps)
-
- headerDeps.ResolveExcludes()
- implementationHeaderDeps.ResolveExcludes()
- dynamicDeps.ResolveExcludes()
- implementationDynamicDeps.ResolveExcludes()
- wholeArchiveDeps.ResolveExcludes()
-
- return linkerAttributes{
- deps: headerDeps,
- implementationDeps: implementationHeaderDeps,
- dynamicDeps: dynamicDeps,
- implementationDynamicDeps: implementationDynamicDeps,
- wholeArchiveDeps: wholeArchiveDeps,
- systemDynamicDeps: systemSharedDeps,
-
- linkopts: linkopts,
- useLibcrt: useLibcrt,
- versionScript: versionScript,
-
- // Strip properties
- stripKeepSymbols: stripKeepSymbols,
- stripKeepSymbolsAndDebugFrame: stripKeepSymbolsAndDebugFrame,
- stripKeepSymbolsList: stripKeepSymbolsList,
- stripAll: stripAll,
- stripNone: stripNone,
+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.
+ if la.systemDynamicDeps.IsNil() && len(la.usedSystemDynamicDepAsDynamicDep) > 0 {
+ toRemove := bazelLabelForSharedDeps(ctx, android.SortedStringKeys(la.usedSystemDynamicDepAsDynamicDep))
+ la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
+ la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+ la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
+ la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
}
+
+ la.deps.ResolveExcludes()
+ la.implementationDeps.ResolveExcludes()
+ la.dynamicDeps.ResolveExcludes()
+ la.implementationDynamicDeps.ResolveExcludes()
+ la.wholeArchiveDeps.ResolveExcludes()
+ la.systemDynamicDeps.ForceSpecifyEmptyList = true
+
}
// Relativize a list of root-relative paths with respect to the module's
@@ -569,62 +796,67 @@
// BazelIncludes contains information about -I and -isystem paths from a module converted to Bazel
// attributes.
type BazelIncludes struct {
- Includes bazel.StringListAttribute
- SystemIncludes bazel.StringListAttribute
+ AbsoluteIncludes bazel.StringListAttribute
+ Includes bazel.StringListAttribute
+ SystemIncludes bazel.StringListAttribute
}
-func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) BazelIncludes {
+func bp2BuildParseExportedIncludes(ctx android.BazelConversionPathContext, module *Module, existingIncludes BazelIncludes) BazelIncludes {
libraryDecorator := module.linker.(*libraryDecorator)
- return bp2BuildParseExportedIncludesHelper(ctx, module, 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.TopDownMutatorContext, module *Module) BazelIncludes {
+func Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx android.BazelConversionPathContext, module *Module) BazelIncludes {
prebuiltLibraryLinker := module.linker.(*prebuiltLibraryLinker)
libraryDecorator := prebuiltLibraryLinker.libraryDecorator
- return bp2BuildParseExportedIncludesHelper(ctx, module, 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.TopDownMutatorContext, module *Module, libraryDecorator *libraryDecorator) BazelIncludes {
- exported := BazelIncludes{}
+func bp2BuildParseExportedIncludesHelper(ctx android.BazelConversionPathContext, module *Module, libraryDecorator *libraryDecorator, 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, flagExporterProperties.Export_include_dirs)
+ 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, flagExporterProperties.Export_system_include_dirs)
+ 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()
return exported
}
-func bazelLabelForStaticModule(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
label := android.BazelModuleLabel(ctx, m)
- if aModule, ok := m.(android.Module); ok {
- if ctx.OtherModuleType(aModule) == "cc_library" && !android.GenerateCcLibraryStaticOnly(m.Name()) {
- label += "_bp2build_cc_library_static"
- }
+ if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GenerateCcLibraryStaticOnly(m.Name()) {
+ label += "_bp2build_cc_library_static"
}
return label
}
-func bazelLabelForSharedModule(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+func bazelLabelForSharedModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
// cc_library, at it's root name, propagates the shared library, which depends on the static
// library.
return android.BazelModuleLabel(ctx, m)
}
-func bazelLabelForStaticWholeModuleDeps(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+func bazelLabelForStaticWholeModuleDeps(ctx android.BazelConversionPathContext, m blueprint.Module) string {
label := bazelLabelForStaticModule(ctx, m)
if aModule, ok := m.(android.Module); ok {
if android.IsModulePrebuilt(aModule) {
@@ -634,32 +866,58 @@
return label
}
-func bazelLabelForWholeDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+func bazelLabelForWholeDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticWholeModuleDeps)
}
-func bazelLabelForWholeDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+func bazelLabelForWholeDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticWholeModuleDeps)
}
-func bazelLabelForStaticDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+func bazelLabelForStaticDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticModule)
}
-func bazelLabelForStaticDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+func bazelLabelForStaticDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticModule)
}
-func bazelLabelForSharedDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+func bazelLabelForSharedDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForSharedModule)
}
-func bazelLabelForHeaderDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+func bazelLabelForHeaderDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
// This is not elegant, but bp2build's shared library targets only propagate
// their header information as part of the normal C++ provider.
return bazelLabelForSharedDeps(ctx, modules)
}
-func bazelLabelForSharedDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+func bazelLabelForSharedDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
}
+
+type binaryLinkerAttrs struct {
+ Linkshared *bool
+}
+
+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")
+ }
+ }
+ }
+
+ return attrs
+}
diff --git a/cc/builder.go b/cc/builder.go
index b494f7b..525b1a1 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -165,6 +165,12 @@
}
}()
+ darwinLipo = pctx.AndroidStaticRule("darwinLipo",
+ blueprint.RuleParams{
+ Command: "${config.MacLipoPath} -create -output $out $in",
+ CommandDeps: []string{"${config.MacLipoPath}"},
+ })
+
_ = pctx.SourcePathVariable("archiveRepackPath", "build/soong/scripts/archive_repack.sh")
// Rule to repack an archive (.a) file with a subset of object files.
@@ -197,25 +203,34 @@
"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{})
+
clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- // Pick bash because some machines with old /bin/sh cannot handle arrays.
- // All $cFlags and $tidyFlags should have single quotes escaped.
- // Assume no single quotes in other parameters like $in, $out, $ccCmd.
- Command: "/bin/bash -c 'SRCF=$in; TIDYF=$out; CLANGFLAGS=($cFlags); " +
- "rm -f $$TIDYF $${TIDYF}.d && " +
- "${config.CcWrapper}$ccCmd \"$${CLANGFLAGS[@]}\" -E -o /dev/null $$SRCF " +
- "-MQ $$TIDYF -MD -MF $${TIDYF}.d && " +
- "$tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $$SRCF " +
- "-- \"$${CLANGFLAGS[@]}\" && touch $$TIDYF'",
- CommandDeps: []string{"${config.ClangBin}/clang-tidy", "$ccCmd"},
+ Command: "cp ${out}.dep ${out}.d && " +
+ "$tidyVars$reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && " +
+ "touch $out",
+ CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
},
&remoteexec.REParams{
Labels: map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
ExecStrategy: "${config.REClangTidyExecStrategy}",
- Inputs: []string{"$in"},
+ Inputs: []string{"$in", "${out}.dep"},
EnvironmentVariables: []string{"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.
@@ -224,7 +239,7 @@
// (1) New timestamps trigger clang and clang-tidy compilations again.
// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
- }, []string{"ccCmd", "cFlags", "tidyFlags", "tidyVars"}, []string{})
+ }, []string{"cFlags", "tidyFlags", "tidyVars"}, []string{})
_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
@@ -238,14 +253,6 @@
},
"asFlags")
- // Rule to invoke windres, for interaction with Windows resources.
- windres = pctx.AndroidStaticRule("windres",
- blueprint.RuleParams{
- Command: "$windresCmd $flags -I$$(dirname $in) -i $in -o $out --preprocessor \"${config.ClangBin}/clang -E -xc-header -DRC_INVOKED\"",
- CommandDeps: []string{"$windresCmd"},
- },
- "windresCmd", "flags")
-
_ = pctx.SourcePathVariable("sAbiDumper", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-dumper")
// -w has been added since header-abi-dumper does not need to produce any sort of diagnostic information.
@@ -389,10 +396,11 @@
toolchain config.Toolchain
// True if these extra features are enabled.
- tidy bool
- gcovCoverage bool
- sAbiDump bool
- emitXrefs bool
+ tidy bool
+ needTidyFiles bool
+ gcovCoverage bool
+ sAbiDump bool
+ emitXrefs bool
assemblerWithCpp bool // True if .s files should be processed with the c preprocessor.
@@ -422,6 +430,7 @@
type Objects struct {
objFiles android.Paths
tidyFiles android.Paths
+ tidyDepFiles android.Paths // link dependent .tidy files
coverageFiles android.Paths
sAbiDumpFiles android.Paths
kytheFiles android.Paths
@@ -431,6 +440,7 @@
return Objects{
objFiles: append(android.Paths{}, a.objFiles...),
tidyFiles: append(android.Paths{}, a.tidyFiles...),
+ tidyDepFiles: append(android.Paths{}, a.tidyDepFiles...),
coverageFiles: append(android.Paths{}, a.coverageFiles...),
sAbiDumpFiles: append(android.Paths{}, a.sAbiDumpFiles...),
kytheFiles: append(android.Paths{}, a.kytheFiles...),
@@ -441,35 +451,33 @@
return Objects{
objFiles: append(a.objFiles, b.objFiles...),
tidyFiles: append(a.tidyFiles, b.tidyFiles...),
+ tidyDepFiles: append(a.tidyDepFiles, b.tidyDepFiles...),
coverageFiles: append(a.coverageFiles, b.coverageFiles...),
sAbiDumpFiles: append(a.sAbiDumpFiles, b.sAbiDumpFiles...),
kytheFiles: append(a.kytheFiles, b.kytheFiles...),
}
}
-func escapeSingleQuotes(s string) string {
- // Replace single quotes to work when embedded in a single quoted string for bash.
- // Relying on string concatenation of bash to get A'B from quoted 'A'\''B'.
- return strings.Replace(s, `'`, `'\''`, -1)
-}
-
// Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
-func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles, noTidySrcs android.Paths,
+func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs, timeoutTidySrcs android.Paths,
flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
-
// Source files are one-to-one with tidy, coverage, or kythe files, if enabled.
objFiles := make(android.Paths, len(srcFiles))
var tidyFiles android.Paths
- noTidySrcsMap := make(map[android.Path]bool)
+ noTidySrcsMap := make(map[string]bool)
var tidyVars string
if flags.tidy {
tidyFiles = make(android.Paths, 0, len(srcFiles))
for _, path := range noTidySrcs {
- noTidySrcsMap[path] = true
+ noTidySrcsMap[path.String()] = true
}
tidyTimeout := ctx.Config().Getenv("TIDY_TIMEOUT")
if len(tidyTimeout) > 0 {
- tidyVars += "TIDY_TIMEOUT=" + tidyTimeout
+ tidyVars += "TIDY_TIMEOUT=" + tidyTimeout + " "
+ // add timeoutTidySrcs into noTidySrcsMap if TIDY_TIMEOUT is set
+ for _, path := range timeoutTidySrcs {
+ noTidySrcsMap[path.String()] = true
+ }
}
}
var coverageFiles android.Paths
@@ -531,11 +539,18 @@
cppflags += " ${config.NoOverrideGlobalCflags}"
toolingCppflags += " ${config.NoOverrideGlobalCflags}"
+ modulePath := android.PathForModuleSrc(ctx).String()
+ if android.IsThirdPartyPath(modulePath) {
+ cflags += " ${config.NoOverrideExternalGlobalCflags}"
+ toolingCflags += " ${config.NoOverrideExternalGlobalCflags}"
+ cppflags += " ${config.NoOverrideExternalGlobalCflags}"
+ toolingCppflags += " ${config.NoOverrideExternalGlobalCflags}"
+ }
+
// Multiple source files have build rules usually share the same cFlags or tidyFlags.
// Define only one version in this module and share it in multiple build rules.
// To simplify the code, the shared variables are all named as $flags<nnn>.
- numSharedFlags := 0
- flagsMap := make(map[string]string)
+ shared := ctx.getSharedFlags()
// Share flags only when there are multiple files or tidy rules.
var hasMultipleRules = len(srcFiles) > 1 || flags.tidy
@@ -547,11 +562,11 @@
return flags
}
mapKey := kind + flags
- n, ok := flagsMap[mapKey]
+ n, ok := shared.flagsMap[mapKey]
if !ok {
- numSharedFlags += 1
- n = strconv.Itoa(numSharedFlags)
- flagsMap[mapKey] = n
+ shared.numSharedFlags += 1
+ n = strconv.Itoa(shared.numSharedFlags)
+ shared.flagsMap[mapKey] = n
ctx.Variable(pctx, kind+n, flags)
}
return "$" + kind + n
@@ -577,20 +592,6 @@
},
})
continue
- case ".rc":
- ctx.Build(pctx, android.BuildParams{
- Rule: windres,
- Description: "windres " + srcFile.Rel(),
- Output: objFile,
- Input: srcFile,
- Implicits: cFlagsDeps,
- OrderOnly: pathDeps,
- Args: map[string]string{
- "windresCmd": mingwCmd(flags.toolchain, "windres"),
- "flags": shareFlags("flags", flags.toolchain.WindresFlags()),
- },
- })
- continue
case ".o":
objFiles[i] = srcFile
continue
@@ -678,27 +679,46 @@
}
// Even with tidy, some src file could be skipped by noTidySrcsMap.
- if tidy && !noTidySrcsMap[srcFile] {
+ if tidy && !noTidySrcsMap[srcFile.String()] {
tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
+ tidyDepFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy.dep")
tidyFiles = append(tidyFiles, tidyFile)
+ ruleDep := clangTidyDep
rule := clangTidy
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
+ ruleDep = clangTidyDepRE
rule = clangTidyRE
}
- ctx.TidyFile(tidyFile)
+ sharedCFlags := shareFlags("cFlags", moduleFlags)
+ srcRelPath := srcFile.Rel()
+
+ // Add the .tidy.d rule
ctx.Build(pctx, android.BuildParams{
- Rule: rule,
- Description: "clang-tidy " + srcFile.Rel(),
- Output: tidyFile,
+ Rule: ruleDep,
+ Description: "clang-tidy-dep " + srcRelPath,
+ Output: tidyDepFile,
Input: srcFile,
Implicits: cFlagsDeps,
OrderOnly: pathDeps,
Args: map[string]string{
- "ccCmd": ccCmd,
- "cFlags": shareFlags("cFlags", escapeSingleQuotes(moduleToolingFlags)),
- "tidyFlags": shareFlags("tidyFlags", escapeSingleQuotes(config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags))),
+ "ccCmd": ccCmd,
+ "cFlags": sharedCFlags,
+ "tidyFile": tidyFile.String(),
+ },
+ })
+ // Add the .tidy rule with order only dependency on the .tidy.d file
+ ctx.Build(pctx, android.BuildParams{
+ Rule: rule,
+ Description: "clang-tidy " + srcRelPath,
+ Output: tidyFile,
+ Input: srcFile,
+ Implicits: cFlagsDeps,
+ OrderOnly: append(android.Paths{}, tidyDepFile),
+ Args: map[string]string{
+ "cFlags": sharedCFlags,
+ "tidyFlags": shareFlags("tidyFlags", config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags)),
"tidyVars": tidyVars, // short and not shared
},
})
@@ -729,9 +749,14 @@
}
+ var tidyDepFiles android.Paths
+ if flags.needTidyFiles {
+ tidyDepFiles = tidyFiles
+ }
return Objects{
objFiles: objFiles,
tidyFiles: tidyFiles,
+ tidyDepFiles: tidyDepFiles,
coverageFiles: coverageFiles,
sAbiDumpFiles: sAbiDumpFiles,
kytheFiles: kytheFiles,
@@ -741,12 +766,12 @@
// Generate a rule for compiling multiple .o files to a static library (.a)
func transformObjToStaticLib(ctx android.ModuleContext,
objFiles android.Paths, wholeStaticLibs android.Paths,
- flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
+ flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths, validations android.Paths) {
arCmd := "${config.ClangBin}/llvm-ar"
arFlags := ""
if !ctx.Darwin() {
- arFlags += " -format=gnu"
+ arFlags += " --format=gnu"
}
if len(wholeStaticLibs) == 0 {
@@ -756,6 +781,7 @@
Output: outputFile,
Inputs: objFiles,
Implicits: deps,
+ Validations: validations,
Args: map[string]string{
"arFlags": "crsPD" + arFlags,
"arCmd": arCmd,
@@ -785,7 +811,7 @@
func transformObjToDynamicBinary(ctx android.ModuleContext,
objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps, crtBegin, crtEnd android.Paths,
groupLate bool, flags builderFlags, outputFile android.WritablePath,
- implicitOutputs android.WritablePaths, validations android.WritablePaths) {
+ implicitOutputs android.WritablePaths, validations android.Paths) {
ldCmd := "${config.ClangBin}/clang++"
@@ -852,7 +878,7 @@
Inputs: objFiles,
Implicits: deps,
OrderOnly: sharedLibs,
- Validations: validations.Paths(),
+ Validations: validations,
Args: args,
})
}
@@ -970,8 +996,7 @@
}
// Generate a rule for extracting a table of contents from a shared library (.so)
-func transformSharedObjectToToc(ctx android.ModuleContext, inputFile android.Path,
- outputFile android.WritablePath, flags builderFlags) {
+func TransformSharedObjectToToc(ctx android.ModuleContext, inputFile android.Path, outputFile android.WritablePath) {
var format string
if ctx.Darwin() {
@@ -1082,6 +1107,15 @@
})
}
+func transformDarwinUniversalBinary(ctx android.ModuleContext, outputFile android.WritablePath, inputFiles ...android.Path) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: darwinLipo,
+ Description: "lipo " + outputFile.Base(),
+ Output: outputFile,
+ Inputs: inputFiles,
+ })
+}
+
// Registers build statement to zip one or more coverage files.
func transformCoverageFilesToZip(ctx android.ModuleContext,
inputs Objects, baseName string) android.OptionalPath {
diff --git a/cc/cc.go b/cc/cc.go
index b410834..a8adb0c 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -64,6 +64,9 @@
ctx.BottomUp("coverage", coverageMutator).Parallel()
+ ctx.TopDown("afdo_deps", afdoDepsMutator)
+ ctx.BottomUp("afdo", afdoMutator).Parallel()
+
ctx.TopDown("lto_deps", ltoDepsMutator)
ctx.BottomUp("lto", ltoMutator).Parallel()
@@ -167,6 +170,10 @@
// Path to the dynamic linker binary
DynamicLinker android.OptionalPath
+
+ // For Darwin builds, the path to the second architecture's output that should
+ // be combined with this architectures's output into a FAT MachO file.
+ DarwinSecondArchOutput android.OptionalPath
}
// LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -203,11 +210,12 @@
// These must be after any module include flags, which will be in CommonFlags.
SystemIncludeFlags []string
- Toolchain config.Toolchain
- Tidy bool // True if clang-tidy is enabled.
- GcovCoverage bool // True if coverage files should be generated.
- SAbiDump bool // True if header abi dumps should be generated.
- EmitXrefs bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
+ Toolchain config.Toolchain
+ Tidy bool // True if ninja .tidy rules should be generated.
+ NeedTidyFiles bool // True if module link should depend on .tidy files
+ GcovCoverage bool // True if coverage files should be generated.
+ SAbiDump bool // True if header abi dumps should be generated.
+ EmitXrefs bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
// The instruction set required for clang ("arm" or "thumb").
RequiredInstructionSet string
@@ -325,7 +333,7 @@
SnapshotStaticLibs []string `blueprint:"mutated"`
SnapshotRuntimeLibs []string `blueprint:"mutated"`
- Installable *bool
+ Installable *bool `android:"arch_variant"`
// Set by factories of module types that can only be referenced from variants compiled against
// the SDK.
@@ -497,6 +505,7 @@
selectedStl() string
baseModuleName() string
getVndkExtendsModuleName() string
+ isAfdoCompile() bool
isPgoCompile() bool
isNDKStubLibrary() bool
useClangLld(actx ModuleContext) bool
@@ -509,6 +518,12 @@
directlyInAnyApex() bool
isPreventInstall() bool
isCfiAssemblySupportEnabled() bool
+ getSharedFlags() *SharedFlags
+}
+
+type SharedFlags struct {
+ numSharedFlags int
+ flagsMap map[string]string
}
type ModuleContext interface {
@@ -534,8 +549,7 @@
}
// compiler is the interface for a compiler helper object. Different module decorators may implement
-// this helper differently. For example, compiling a `cc_library` may use a different build
-// statement than building a `toolchain_library`.
+// this helper differently.
type compiler interface {
compilerInit(ctx BaseModuleContext)
compilerDeps(ctx DepsContext, deps Deps) Deps
@@ -682,6 +696,15 @@
return d.Kind == staticLibraryDependency
}
+func (d libraryDependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
+ if d.shared() {
+ return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
+ }
+ return nil
+}
+
+var _ android.LicenseAnnotationsDependencyTag = libraryDependencyTag{}
+
// InstallDepNeeded returns true for shared libraries so that shared library dependencies of
// binaries or other shared libraries are installed as dependencies.
func (d libraryDependencyTag) InstallDepNeeded() bool {
@@ -773,8 +796,9 @@
Properties BaseProperties
// initialize before calling Init
- hod android.HostOrDeviceSupported
- multilib android.Multilib
+ hod android.HostOrDeviceSupported
+ multilib android.Multilib
+ bazelable bool
// Allowable SdkMemberTypes of this module type.
sdkMemberTypes []android.SdkMemberType
@@ -796,6 +820,7 @@
sabi *sabi
vndkdep *vndkdep
lto *lto
+ afdo *afdo
pgo *pgo
library libraryInterface
@@ -809,12 +834,19 @@
// Flags used to compile this module
flags Flags
+ // Shared flags among build rules of this module
+ sharedFlags SharedFlags
+
// only non-nil when this is a shared library that reuses the objects of a static library
staticAnalogue *StaticLibraryInfo
makeLinkType string
// Kythe (source file indexer) paths for this compilation module
kytheFiles android.Paths
+ // Object .o file output paths for this compilation module
+ objFiles android.Paths
+ // Tidy .tidy file output paths for this compilation module
+ tidyFiles android.Paths
// For apex variants, this is set as apex.min_sdk_version
apexSdkVersion android.ApiLevel
@@ -947,13 +979,6 @@
return ""
}
-func (c *Module) ToolchainLibrary() bool {
- if _, ok := c.linker.(*toolchainLibraryDecorator); ok {
- return true
- }
- return false
-}
-
func (c *Module) NdkPrebuiltStl() bool {
if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok {
return true
@@ -1125,6 +1150,9 @@
if c.lto != nil {
c.AddProperties(c.lto.props()...)
}
+ if c.afdo != nil {
+ c.AddProperties(c.afdo.props()...)
+ }
if c.pgo != nil {
c.AddProperties(c.pgo.props()...)
}
@@ -1133,7 +1161,9 @@
}
android.InitAndroidArchModule(c, c.hod, c.multilib)
- android.InitBazelModule(c)
+ if c.bazelable {
+ android.InitBazelModule(c)
+ }
android.InitApexModule(c)
android.InitSdkAwareModule(c)
android.InitDefaultableModule(c)
@@ -1230,6 +1260,13 @@
return false
}
+func (c *Module) isAfdoCompile() bool {
+ if afdo := c.afdo; afdo != nil {
+ return afdo.Properties.AfdoTarget != nil
+ }
+ return false
+}
+
func (c *Module) isPgoCompile() bool {
if pgo := c.pgo; pgo != nil {
return pgo.Properties.PgoCompile
@@ -1507,6 +1544,10 @@
return ctx.mod.IsVndk()
}
+func (ctx *moduleContextImpl) isAfdoCompile() bool {
+ return ctx.mod.isAfdoCompile()
+}
+
func (ctx *moduleContextImpl) isPgoCompile() bool {
return ctx.mod.isPgoCompile()
}
@@ -1578,6 +1619,15 @@
return ctx.mod.Properties.PreventInstall
}
+func (ctx *moduleContextImpl) getSharedFlags() *SharedFlags {
+ shared := &ctx.mod.sharedFlags
+ if shared.flagsMap == nil {
+ shared.numSharedFlags = 0
+ shared.flagsMap = make(map[string]string)
+ }
+ return shared
+}
+
func (ctx *moduleContextImpl) isCfiAssemblySupportEnabled() bool {
return ctx.mod.isCfiAssemblySupportEnabled()
}
@@ -1600,6 +1650,7 @@
module.sabi = &sabi{}
module.vndkdep = &vndkdep{}
module.lto = <o{}
+ module.afdo = &afdo{}
module.pgo = &pgo{}
return module
}
@@ -1711,11 +1762,20 @@
// Returns true if Bazel was successfully used for the analysis of this module.
func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool {
- bazelModuleLabel := c.GetBazelLabel(actx, c)
+ 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)
+ } else {
+ bazelModuleLabel = c.GetBazelLabel(actx, c)
+ }
+
bazelActionsUsed := false
// Mixed builds mode is disabled for modules outside of device OS.
// TODO(b/200841190): Support non-device OS in mixed builds.
- if c.MixedBuildsEnabled(actx) && c.bazelHandler != nil && actx.Os().Class == android.Device {
+ if c.MixedBuildsEnabled(actx) && c.bazelHandler != nil {
bazelActionsUsed = c.bazelHandler.GenerateBazelBuildActions(actx, bazelModuleLabel)
}
return bazelActionsUsed
@@ -1787,6 +1847,9 @@
if c.lto != nil {
flags = c.lto.flags(ctx, flags)
}
+ if c.afdo != nil {
+ flags = c.afdo.flags(ctx, flags)
+ }
if c.pgo != nil {
flags = c.pgo.flags(ctx, flags)
}
@@ -1825,6 +1888,8 @@
return
}
c.kytheFiles = objs.kytheFiles
+ c.objFiles = objs.objFiles
+ c.tidyFiles = objs.tidyFiles
}
if c.linker != nil {
@@ -1865,7 +1930,7 @@
}
func (c *Module) maybeInstall(ctx ModuleContext, apexInfo android.ApexInfo) {
- if !proptools.BoolDefault(c.Properties.Installable, true) {
+ if !proptools.BoolDefault(c.Installable(), true) {
// If the module has been specifically configure to not be installed then
// hide from make as otherwise it will break when running inside make
// as the output path to install will not be specified. Not all uninstallable
@@ -1912,6 +1977,9 @@
if c.lto != nil {
c.lto.begin(ctx)
}
+ if c.afdo != nil {
+ c.afdo.begin(ctx)
+ }
if c.pgo != nil {
c.pgo.begin(ctx)
}
@@ -2387,10 +2455,6 @@
return
}
if c, ok := to.(*Module); ok {
- if c.ToolchainLibrary() {
- // These are always allowed
- return
- }
if c.NdkPrebuiltStl() {
// These are allowed, but they don't set sdk_version
return
@@ -2568,6 +2632,11 @@
depName := ctx.OtherModuleName(dep)
depTag := ctx.OtherModuleDependencyTag(dep)
+ if depTag == android.DarwinUniversalVariantTag {
+ depPaths.DarwinSecondArchOutput = dep.(*Module).OutputFile()
+ return
+ }
+
ccDep, ok := dep.(LinkableInterface)
if !ok {
@@ -2738,6 +2807,8 @@
// dependency.
depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
}
+ depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts,
+ staticLibraryInfo.WholeStaticLibsFromPrebuilts...)
} else {
switch libDepTag.Order {
case earlyLibraryDependency:
@@ -3151,6 +3222,24 @@
return false
}
+func (c *Module) benchmarkBinary() bool {
+ if b, ok := c.linker.(interface {
+ benchmarkBinary() bool
+ }); ok {
+ return b.benchmarkBinary()
+ }
+ return false
+}
+
+func (c *Module) fuzzBinary() bool {
+ if f, ok := c.linker.(interface {
+ fuzzBinary() bool
+ }); ok {
+ return f.fuzzBinary()
+ }
+ return false
+}
+
// Header returns true if the module is a header-only variant. (See cc/library.go header()).
func (c *Module) Header() bool {
if h, ok := c.linker.(interface {
@@ -3249,16 +3338,6 @@
return c.Properties.Test_for
}
-func (c *Module) UniqueApexVariations() bool {
- if u, ok := c.compiler.(interface {
- uniqueApexVariations() bool
- }); ok {
- return u.uniqueApexVariations()
- } else {
- return false
- }
-}
-
func (c *Module) EverInstallable() bool {
return c.installer != nil &&
// Check to see whether the module is actually ever installable.
@@ -3270,6 +3349,11 @@
}
func (c *Module) Installable() *bool {
+ if c.library != nil {
+ if i := c.library.installable(); i != nil {
+ return i
+ }
+ }
return c.Properties.Installable
}
@@ -3358,10 +3442,6 @@
if strings.HasPrefix(ctx.OtherModuleName(c), "libclang_rt") {
return nil
}
- // b/154569636: set min_sdk_version correctly for toolchain_libraries
- if c.ToolchainLibrary() {
- return nil
- }
// We don't check for prebuilt modules
if _, ok := c.linker.(prebuiltLinkerInterface); ok {
return nil
@@ -3399,18 +3479,95 @@
return c.IsStubs() || c.Target().NativeBridge == android.NativeBridgeEnabled
}
+// Overrides android.ApexModuleBase.UniqueApexVariations
+func (c *Module) UniqueApexVariations() bool {
+ // When a vendor APEX needs a VNDK lib in it (use_vndk_as_stable: false), it should be a unique
+ // APEX variation. Otherwise, another vendor APEX with use_vndk_as_stable:true may use a wrong
+ // variation of the VNDK lib because APEX variations are merged/grouped.
+ return c.UseVndk() && c.IsVndk()
+}
+
var _ snapshot.RelativeInstallPath = (*Module)(nil)
+type moduleType int
+
+const (
+ unknownType moduleType = iota
+ binary
+ object
+ fullLibrary
+ staticLibrary
+ sharedLibrary
+ headerLibrary
+)
+
+func (c *Module) typ() moduleType {
+ if c.Binary() {
+ return binary
+ } else if c.Object() {
+ return object
+ } else if c.CcLibrary() {
+ static := false
+ shared := false
+ if library, ok := c.linker.(*libraryDecorator); ok {
+ static = library.MutatedProperties.BuildStatic
+ shared = library.MutatedProperties.BuildShared
+ } else if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+ static = library.MutatedProperties.BuildStatic
+ shared = library.MutatedProperties.BuildShared
+ }
+ if static && shared {
+ return fullLibrary
+ } else if !static && !shared {
+ return headerLibrary
+ } else if static {
+ return staticLibrary
+ }
+ return sharedLibrary
+ }
+ return unknownType
+}
+
+// ConvertWithBp2build converts Module to Bazel for bp2build.
+func (c *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ prebuilt := c.IsPrebuilt()
+ switch c.typ() {
+ case binary:
+ if !prebuilt {
+ binaryBp2build(ctx, c, ctx.ModuleType())
+ }
+ case object:
+ if !prebuilt {
+ objectBp2Build(ctx, c)
+ }
+ case fullLibrary:
+ if !prebuilt {
+ libraryBp2Build(ctx, c)
+ }
+ case headerLibrary:
+ libraryHeadersBp2Build(ctx, c)
+ case staticLibrary:
+
+ if prebuilt {
+ prebuiltLibraryStaticBp2Build(ctx, c)
+ } else {
+ sharedOrStaticLibraryBp2Build(ctx, c, true)
+ }
+ case sharedLibrary:
+ if prebuilt {
+ prebuiltLibrarySharedBp2Build(ctx, c)
+ } else {
+ sharedOrStaticLibraryBp2Build(ctx, c, false)
+ }
+ }
+}
+
//
// Defaults
//
type Defaults struct {
android.ModuleBase
android.DefaultsModuleBase
- // 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().
- android.BazelModuleBase
android.ApexModuleBase
}
@@ -3451,14 +3608,14 @@
&SAbiProperties{},
&VndkProperties{},
<OProperties{},
+ &AfdoProperties{},
&PgoProperties{},
&android.ProtoProperties{},
// RustBindgenProperties is included here so that cc_defaults can be used for rust_bindgen modules.
&RustBindgenClangProperties{},
+ &prebuiltLinkerProperties{},
)
- // Bazel module must be initialized _before_ Defaults to be included in cc_defaults module.
- android.InitBazelModule(module)
android.InitDefaultsModule(module)
return module
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 4c9f579..51a6a27 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -346,7 +346,7 @@
func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
t.Helper()
- got := ctx.ModuleForTests(module, "").Module().(*vndkLibrariesTxt).fileNames
+ got := ctx.ModuleForTests(module, "android_common").Module().(*vndkLibrariesTxt).fileNames
assertArrayString(t, got, expected)
}
@@ -532,11 +532,11 @@
CheckSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLib2ndPath, variant2nd)
snapshotConfigsPath := filepath.Join(snapshotVariantPath, "configs")
- CheckSnapshot(t, ctx, snapshotSingleton, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "")
- CheckSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
- CheckSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
- CheckSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
- CheckSnapshot(t, ctx, snapshotSingleton, "vndkproduct.libraries.txt", "vndkproduct.libraries.txt", snapshotConfigsPath, "")
+ CheckSnapshot(t, ctx, snapshotSingleton, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "android_common")
+ CheckSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "android_common")
+ CheckSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "android_common")
+ CheckSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "android_common")
+ CheckSnapshot(t, ctx, snapshotSingleton, "vndkproduct.libraries.txt", "vndkproduct.libraries.txt", snapshotConfigsPath, "android_common")
checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
"LLNDK: libc.so",
@@ -614,7 +614,7 @@
config.TestProductVariables.Platform_vndk_version = StringPtr("29")
ctx := testCcWithConfig(t, config)
- module := ctx.ModuleForTests("llndk.libraries.txt", "")
+ module := ctx.ModuleForTests("llndk.libraries.txt", "android_common")
entries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
assertArrayString(t, entries.EntryMap["LOCAL_MODULE_STEM"], []string{"llndk.libraries.29.txt"})
}
@@ -730,9 +730,16 @@
gtest: false,
}
+ cc_binary {
+ name: "test_bin",
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "both",
+ }
+
cc_test {
name: "main_test",
data_libs: ["test_lib"],
+ data_bins: ["test_bin"],
gtest: false,
}
`
@@ -750,10 +757,10 @@
t.Fatalf("Expected cc_test to produce output files, error: %s", err)
}
if len(outputFiles) != 1 {
- t.Errorf("expected exactly one output file. output files: [%s]", outputFiles)
+ t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
}
- if len(testBinary.dataPaths()) != 1 {
- t.Errorf("expected exactly one test data file. test data files: [%s]", testBinary.dataPaths())
+ if len(testBinary.dataPaths()) != 2 {
+ t.Fatalf("expected exactly one test data file. test data files: [%s]", testBinary.dataPaths())
}
outputPath := outputFiles[0].String()
@@ -766,6 +773,10 @@
t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
}
+ if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][1], ":test_bin:foo/bar/baz") {
+ t.Errorf("expected LOCAL_TEST_DATA to end with `:test_bin:foo/bar/baz`,"+
+ " but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][1])
+ }
}
func TestVndkWhenVndkVersionIsNotSet(t *testing.T) {
@@ -3585,6 +3596,58 @@
}
}
+func TestAidlFlagsWithMinSdkVersion(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ sdkVersion string
+ variant string
+ expected string
+ }{
+ {
+ name: "default is current",
+ sdkVersion: "",
+ variant: "android_arm64_armv8-a_static",
+ expected: "platform_apis",
+ },
+ {
+ name: "use sdk_version",
+ sdkVersion: `sdk_version: "29"`,
+ variant: "android_arm64_armv8-a_static",
+ expected: "platform_apis",
+ },
+ {
+ name: "use sdk_version(sdk variant)",
+ sdkVersion: `sdk_version: "29"`,
+ variant: "android_arm64_armv8-a_sdk_static",
+ expected: "29",
+ },
+ {
+ name: "use min_sdk_version",
+ sdkVersion: `min_sdk_version: "29"`,
+ variant: "android_arm64_armv8-a_static",
+ expected: "29",
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library {
+ name: "libfoo",
+ stl: "none",
+ srcs: ["a/Foo.aidl"],
+ `+tc.sdkVersion+`
+ }
+ `)
+ libfoo := ctx.ModuleForTests("libfoo", tc.variant)
+ manifest := android.RuleBuilderSboxProtoForTests(t, libfoo.Output("aidl.sbox.textproto"))
+ aidlCommand := manifest.Commands[0].GetCommand()
+ expectedAidlFlag := "--min_sdk_version=" + tc.expected
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+ })
+ }
+}
+
func TestMinSdkVersionInClangTriple(t *testing.T) {
ctx := testCc(t, `
cc_library_shared {
@@ -3880,7 +3943,6 @@
"${config.ArmGenericCflags}",
"-target",
"armv7a-linux-androideabi20",
- "-B${config.ArmGccRoot}/arm-linux-androideabi/bin",
}
expectedIncludes := []string{
@@ -3917,9 +3979,9 @@
conly := []string{"-fPIC", "${config.CommonGlobalConlyflags}"}
cppOnly := []string{"-fPIC", "${config.CommonGlobalCppflags}", "${config.DeviceGlobalCppflags}", "${config.ArmCppflags}"}
- cflags := []string{"-Wall", "-Werror"}
- cstd := []string{"-std=gnu99"}
- cppstd := []string{"-std=gnu++17", "-fno-rtti"}
+ cflags := []string{"-Wall", "-Werror", "-std=candcpp"}
+ cstd := []string{"-std=gnu99", "-std=conly"}
+ cppstd := []string{"-std=gnu++17", "-std=cpp", "-fno-rtti"}
lastIncludes := []string{
"out/soong/ndk/sysroot/usr/include",
@@ -3942,12 +4004,12 @@
{
name: "c",
src: "foo.c",
- expected: combineSlices(baseExpectedFlags, conly, expectedIncludes, cflags, cstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}"}),
+ expected: combineSlices(baseExpectedFlags, conly, expectedIncludes, cflags, cstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
},
{
name: "cc",
src: "foo.cc",
- expected: combineSlices(baseExpectedFlags, cppOnly, expectedIncludes, cflags, cppstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}"}),
+ expected: combineSlices(baseExpectedFlags, cppOnly, expectedIncludes, cflags, cppstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
},
{
name: "assemble",
@@ -3962,6 +4024,9 @@
cc_library {
name: "libfoo",
srcs: ["%s"],
+ cflags: ["-std=candcpp"],
+ conlyflags: ["-std=conly"],
+ cppflags: ["-std=cpp"],
local_include_dirs: ["local_include_dirs"],
export_include_dirs: ["export_include_dirs"],
export_system_include_dirs: ["export_system_include_dirs"],
diff --git a/cc/ccdeps.go b/cc/ccdeps.go
index b96d8b0..75e1faf 100644
--- a/cc/ccdeps.go
+++ b/cc/ccdeps.go
@@ -44,11 +44,9 @@
var _ android.SingletonMakeVarsProvider = (*ccdepsGeneratorSingleton)(nil)
const (
- // Environment variables used to control the behavior of this singleton.
- envVariableCollectCCDeps = "SOONG_COLLECT_CC_DEPS"
- ccdepsJsonFileName = "module_bp_cc_deps.json"
- cClang = "clang"
- cppClang = "clang++"
+ ccdepsJsonFileName = "module_bp_cc_deps.json"
+ cClang = "clang"
+ cppClang = "clang++"
)
type ccIdeInfo struct {
@@ -83,10 +81,7 @@
}
func (c *ccdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- if !ctx.Config().IsEnvTrue(envVariableCollectCCDeps) {
- return
- }
-
+ // (b/204397180) Generate module_bp_cc_deps.json by default.
moduleDeps := ccDeps{}
moduleInfos := map[string]ccIdeInfo{}
diff --git a/cc/compiler.go b/cc/compiler.go
index b535e7f..7645cbb 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -42,6 +42,9 @@
// list of source files that should not be compiled with clang-tidy.
Tidy_disabled_srcs []string `android:"path,arch_variant"`
+ // list of source files that should not be compiled by clang-tidy when TIDY_TIMEOUT is set.
+ Tidy_timeout_srcs []string `android:"path,arch_variant"`
+
// list of source files that should not be used to build the C/C++ module.
// This is most useful in the arch/multilib variants to remove non-common files
Exclude_srcs []string `android:"path,arch_variant"`
@@ -209,15 +212,6 @@
// Build and link with OpenMP
Openmp *bool `android:"arch_variant"`
-
- // Deprecated.
- // Adds __ANDROID_APEX_<APEX_MODULE_NAME>__ macro defined for apex variants in addition to __ANDROID_APEX__
- Use_apex_name_macro *bool
-
- // Adds two macros for apex variants in addition to __ANDROID_APEX__
- // * __ANDROID_APEX_COM_ANDROID_FOO__
- // * __ANDROID_APEX_NAME__="com.android.foo"
- UseApexNameMacro bool `blueprint:"mutated"`
}
func NewBaseCompiler() *baseCompiler {
@@ -291,10 +285,6 @@
return deps
}
-func (compiler *baseCompiler) useApexNameMacro() bool {
- return Bool(compiler.Properties.Use_apex_name_macro) || compiler.Properties.UseApexNameMacro
-}
-
// Return true if the module is in the WarningAllowedProjects.
func warningsAreAllowed(subdir string) bool {
subdir += "/"
@@ -305,6 +295,38 @@
getNamedMapForConfig(ctx.Config(), key).Store(module, true)
}
+func maybeReplaceGnuToC(gnuExtensions *bool, cStd string, cppStd string) (string, string) {
+ if gnuExtensions != nil && *gnuExtensions == false {
+ cStd = gnuToCReplacer.Replace(cStd)
+ cppStd = gnuToCReplacer.Replace(cppStd)
+ }
+ return cStd, cppStd
+}
+
+func parseCppStd(cppStdPtr *string) string {
+ cppStd := String(cppStdPtr)
+ switch cppStd {
+ case "":
+ return config.CppStdVersion
+ case "experimental":
+ return config.ExperimentalCppStdVersion
+ default:
+ return cppStd
+ }
+}
+
+func parseCStd(cStdPtr *string) string {
+ cStd := String(cStdPtr)
+ switch cStd {
+ case "":
+ return config.CStdVersion
+ case "experimental":
+ return config.ExperimentalCStdVersion
+ default:
+ return cStd
+ }
+}
+
// Create a Flags struct that collects the compile flags from global values,
// per-target values, module type values, and per-module Blueprints properties
func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
@@ -386,10 +408,6 @@
if ctx.apexVariationName() != "" {
flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
- if compiler.useApexNameMacro() {
- flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX_"+makeDefineString(ctx.apexVariationName())+"__")
- flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX_NAME__='\""+ctx.apexVariationName()+"\"'")
- }
if ctx.Device() {
flags.Global.CommonFlags = append(flags.Global.CommonFlags,
fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
@@ -435,11 +453,9 @@
}
}
- gccPrefix := "-B" + config.ToolPath(tc)
-
- flags.Global.CFlags = append(flags.Global.CFlags, target, gccPrefix)
- flags.Global.AsFlags = append(flags.Global.AsFlags, target, gccPrefix)
- flags.Global.LdFlags = append(flags.Global.LdFlags, target, gccPrefix)
+ flags.Global.CFlags = append(flags.Global.CFlags, target)
+ flags.Global.AsFlags = append(flags.Global.AsFlags, target)
+ flags.Global.LdFlags = append(flags.Global.LdFlags, target)
hod := "Host"
if ctx.Os().Class == android.Device {
@@ -477,25 +493,10 @@
flags.Global.CommonFlags = append(flags.Global.CommonFlags, tc.ToolchainCflags())
- cStd := config.CStdVersion
- if String(compiler.Properties.C_std) == "experimental" {
- cStd = config.ExperimentalCStdVersion
- } else if String(compiler.Properties.C_std) != "" {
- cStd = String(compiler.Properties.C_std)
- }
+ cStd := parseCStd(compiler.Properties.C_std)
+ cppStd := parseCppStd(compiler.Properties.Cpp_std)
- cppStd := String(compiler.Properties.Cpp_std)
- switch String(compiler.Properties.Cpp_std) {
- case "":
- cppStd = config.CppStdVersion
- case "experimental":
- cppStd = config.ExperimentalCppStdVersion
- }
-
- if compiler.Properties.Gnu_extensions != nil && *compiler.Properties.Gnu_extensions == false {
- cStd = gnuToCReplacer.Replace(cStd)
- cppStd = gnuToCReplacer.Replace(cppStd)
- }
+ cStd, cppStd = maybeReplaceGnuToC(compiler.Properties.Gnu_extensions, cStd, cppStd)
flags.Local.ConlyFlags = append([]string{"-std=" + cStd}, flags.Local.ConlyFlags...)
flags.Local.CppFlags = append([]string{"-std=" + cppStd}, flags.Local.CppFlags...)
@@ -544,11 +545,6 @@
"-I"+android.PathForModuleGen(ctx, "yacc", ctx.ModuleDir()).String())
}
- if compiler.hasSrcExt(".mc") {
- flags.Local.CommonFlags = append(flags.Local.CommonFlags,
- "-I"+android.PathForModuleGen(ctx, "windmc", ctx.ModuleDir()).String())
- }
-
if compiler.hasSrcExt(".aidl") {
flags.aidlFlags = append(flags.aidlFlags, compiler.Properties.Aidl.Flags...)
if len(compiler.Properties.Aidl.Local_include_dirs) > 0 {
@@ -564,6 +560,12 @@
flags.aidlFlags = append(flags.aidlFlags, "-t")
}
+ aidlMinSdkVersion := ctx.minSdkVersion()
+ if aidlMinSdkVersion == "" {
+ aidlMinSdkVersion = "platform_apis"
+ }
+ flags.aidlFlags = append(flags.aidlFlags, "--min_sdk_version="+aidlMinSdkVersion)
+
flags.Local.CommonFlags = append(flags.Local.CommonFlags,
"-I"+android.PathForModuleGen(ctx, "aidl").String())
}
@@ -624,10 +626,6 @@
return false
}
-func (compiler *baseCompiler) uniqueApexVariations() bool {
- return compiler.useApexNameMacro()
-}
-
var invalidDefineCharRegex = regexp.MustCompile("[^a-zA-Z0-9_]")
// makeDefineString transforms a name of an APEX module into a value to be used as value for C define
@@ -640,9 +638,9 @@
func ndkPathDeps(ctx ModuleContext) android.Paths {
if ctx.Module().(*Module).IsSdkVariant() {
- // The NDK sysroot timestamp file depends on all the NDK sysroot files
- // (headers and libraries).
- return android.Paths{getNdkBaseTimestampFile(ctx)}
+ // The NDK sysroot timestamp file depends on all the NDK sysroot header files
+ // for compiling src to obj files.
+ return android.Paths{getNdkHeadersTimestampFile(ctx)}
}
return nil
}
@@ -668,6 +666,7 @@
// Compile files listed in c.Properties.Srcs into objects
objs := compileObjs(ctx, buildFlags, "", srcs,
android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs),
+ android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_timeout_srcs),
pathDeps, compiler.cFlagsDeps)
if ctx.Failed() {
@@ -678,10 +677,10 @@
}
// Compile a list of source files into objects a specified subdirectory
-func compileObjs(ctx android.ModuleContext, flags builderFlags, subdir string,
- srcFiles, noTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
+func compileObjs(ctx ModuleContext, flags builderFlags, subdir string,
+ srcFiles, noTidySrcs, timeoutTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
- return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, flags, pathDeps, cFlagsDeps)
+ return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, timeoutTidySrcs, flags, pathDeps, cFlagsDeps)
}
// Properties for rust_bindgen related to generating rust bindings.
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 3e8ee48..e1b0605 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -8,6 +8,7 @@
deps: [
"soong-android",
"soong-remoteexec",
+ "soong-starlark-format",
],
srcs: [
"bp2build.go",
@@ -24,7 +25,7 @@
"x86_device.go",
"x86_64_device.go",
- "x86_darwin_host.go",
+ "darwin_host.go",
"x86_linux_host.go",
"x86_linux_bionic_host.go",
"x86_windows_host.go",
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 2d6bcb8..979c825 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -33,7 +33,9 @@
},
"armv8-a-branchprot": []string{
"-march=armv8-a",
- "-mbranch-protection=standard",
+ // Disable BTI until drm vendors stop using OS libraries as sources
+ // of gadgets (https://issuetracker.google.com/216395195).
+ "-mbranch-protection=pac-ret",
},
"armv8-2a": []string{
"-march=armv8.2-a",
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 853d818..5c7f926 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -41,7 +41,6 @@
"-Wl,-z,relro",
"-Wl,-z,now",
"-Wl,--build-id=md5",
- "-Wl,--warn-shared-textrel",
"-Wl,--fatal-warnings",
"-Wl,--hash-style=gnu",
"-Wl,--no-undefined-version",
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
index d19f5ac..eca5161 100644
--- a/cc/config/bp2build.go
+++ b/cc/config/bp2build.go
@@ -16,28 +16,39 @@
import (
"fmt"
+ "reflect"
"regexp"
"sort"
"strings"
-)
-const (
- bazelIndent = 4
+ "android/soong/android"
+ "android/soong/starlark_fmt"
+
+ "github.com/google/blueprint"
)
type bazelVarExporter interface {
- asBazel(exportedStringVariables, exportedStringListVariables) []bazelConstant
+ asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
}
// Helpers for exporting cc configuration information to Bazel.
var (
- // Map containing toolchain variables that are independent of the
+ // Maps containing toolchain variables that are independent of the
// environment variables of the build.
exportedStringListVars = exportedStringListVariables{}
exportedStringVars = exportedStringVariables{}
exportedStringListDictVars = exportedStringListDictVariables{}
+
+ /// Maps containing variables that are dependent on the build config.
+ exportedConfigDependingVars = exportedConfigDependingVariables{}
)
+type exportedConfigDependingVariables map[string]interface{}
+
+func (m exportedConfigDependingVariables) Set(k string, v interface{}) {
+ m[k] = v
+}
+
// Ensure that string s has no invalid characters to be generated into the bzl file.
func validateCharacters(s string) string {
for _, c := range []string{`\n`, `"`, `\`} {
@@ -59,25 +70,14 @@
m[k] = v
}
-func bazelIndention(level int) string {
- return strings.Repeat(" ", level*bazelIndent)
-}
-
-func printBazelList(items []string, indentLevel int) string {
- list := make([]string, 0, len(items)+2)
- list = append(list, "[")
- innerIndent := bazelIndention(indentLevel + 1)
- for _, item := range items {
- list = append(list, fmt.Sprintf(`%s"%s",`, innerIndent, item))
- }
- list = append(list, bazelIndention(indentLevel)+"]")
- return strings.Join(list, "\n")
-}
-
-func (m exportedStringVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+func (m exportedStringVariables) asBazel(config android.Config,
+ stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
ret := make([]bazelConstant, 0, len(m))
for k, variableValue := range m {
- expandedVar := expandVar(variableValue, exportedStringVars, exportedStringListVars)
+ expandedVar, err := expandVar(config, variableValue, stringVars, stringListVars, cfgDepVars)
+ if err != nil {
+ panic(fmt.Errorf("error expanding config variable %s: %s", k, err))
+ }
if len(expandedVar) > 1 {
panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
}
@@ -101,7 +101,9 @@
m[k] = v
}
-func (m exportedStringListVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+func (m exportedStringListVariables) asBazel(config android.Config,
+ stringScope exportedStringVariables, stringListScope exportedStringListVariables,
+ exportedVars exportedConfigDependingVariables) []bazelConstant {
ret := make([]bazelConstant, 0, len(m))
// For each exported variable, recursively expand elements in the variableValue
// list to ensure that interpolated variables are expanded according to their values
@@ -109,48 +111,58 @@
for k, variableValue := range m {
var expandedVars []string
for _, v := range variableValue {
- expandedVars = append(expandedVars, expandVar(v, stringScope, stringListScope)...)
+ expandedVar, err := expandVar(config, v, stringScope, stringListScope, exportedVars)
+ if err != nil {
+ panic(fmt.Errorf("Error expanding config variable %s=%s: %s", k, v, err))
+ }
+ expandedVars = append(expandedVars, expandedVar...)
}
// Assign the list as a bzl-private variable; this variable will be exported
// out through a constants struct later.
ret = append(ret, bazelConstant{
variableName: k,
- internalDefinition: printBazelList(expandedVars, 0),
+ internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
})
}
return ret
}
+// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
+func exportVariableConfigMethod(name string, method interface{}) blueprint.Variable {
+ exportedConfigDependingVars.Set(name, method)
+ return pctx.VariableConfigMethod(name, method)
+}
+
+// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
+func exportSourcePathVariable(name string, value string) {
+ pctx.SourcePathVariable(name, value)
+ exportedStringVars.Set(name, value)
+}
+
// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
func exportStringListStaticVariable(name string, value []string) {
pctx.StaticVariable(name, strings.Join(value, " "))
exportedStringListVars.Set(name, value)
}
+func ExportStringList(name string, value []string) {
+ exportedStringListVars.Set(name, value)
+}
+
type exportedStringListDictVariables map[string]map[string][]string
func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
m[k] = v
}
-func printBazelStringListDict(dict map[string][]string) string {
- bazelDict := make([]string, 0, len(dict)+2)
- bazelDict = append(bazelDict, "{")
- for k, v := range dict {
- bazelDict = append(bazelDict,
- fmt.Sprintf(`%s"%s": %s,`, bazelIndention(1), k, printBazelList(v, 1)))
- }
- bazelDict = append(bazelDict, "}")
- return strings.Join(bazelDict, "\n")
-}
-
// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
-func (m exportedStringListDictVariables) asBazel(_ exportedStringVariables, _ exportedStringListVariables) []bazelConstant {
+func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
+ _ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
ret := make([]bazelConstant, 0, len(m))
for k, dict := range m {
ret = append(ret, bazelConstant{
variableName: k,
- internalDefinition: printBazelStringListDict(dict),
+ internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
})
}
return ret
@@ -158,19 +170,20 @@
// BazelCcToolchainVars generates bzl file content containing variables for
// Bazel's cc_toolchain configuration.
-func BazelCcToolchainVars() string {
+func BazelCcToolchainVars(config android.Config) string {
return bazelToolchainVars(
+ config,
exportedStringListDictVars,
exportedStringListVars,
exportedStringVars)
}
-func bazelToolchainVars(vars ...bazelVarExporter) string {
+func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string {
ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
results := []bazelConstant{}
for _, v := range vars {
- results = append(results, v.asBazel(exportedStringVars, exportedStringListVars)...)
+ results = append(results, v.asBazel(config, exportedStringVars, exportedStringListVars, exportedConfigDependingVars)...)
}
sort.Slice(results, func(i, j int) bool { return results[i].variableName < results[j].variableName })
@@ -181,7 +194,7 @@
definitions = append(definitions,
fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
constants = append(constants,
- fmt.Sprintf("%[1]s%[2]s = _%[2]s,", bazelIndention(1), b.variableName))
+ fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
}
// Build the exported constants struct.
@@ -201,34 +214,35 @@
// string slice than to handle a pass-by-referenced map, which would make it
// quite complex to track depth-first interpolations. It's also unlikely the
// interpolation stacks are deep (n > 1).
-func expandVar(toExpand string, stringScope exportedStringVariables, stringListScope exportedStringListVariables) []string {
+func expandVar(config android.Config, toExpand string, stringScope exportedStringVariables,
+ stringListScope exportedStringListVariables, exportedVars exportedConfigDependingVariables) ([]string, error) {
// e.g. "${ExternalCflags}"
r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`)
// Internal recursive function.
- var expandVarInternal func(string, map[string]bool) []string
- expandVarInternal = func(toExpand string, seenVars map[string]bool) []string {
- var ret []string
- for _, v := range strings.Split(toExpand, " ") {
- matches := r.FindStringSubmatch(v)
+ var expandVarInternal func(string, map[string]bool) (string, error)
+ expandVarInternal = func(toExpand string, seenVars map[string]bool) (string, error) {
+ var ret string
+ remainingString := toExpand
+ for len(remainingString) > 0 {
+ matches := r.FindStringSubmatch(remainingString)
if len(matches) == 0 {
- return []string{v}
+ return ret + remainingString, nil
}
-
if len(matches) != 2 {
- panic(fmt.Errorf(
- "Expected to only match 1 subexpression in %s, got %d",
- v,
- len(matches)-1))
+ panic(fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", remainingString, len(matches)-1))
}
+ matchIndex := strings.Index(remainingString, matches[0])
+ ret += remainingString[:matchIndex]
+ remainingString = remainingString[matchIndex+len(matches[0]):]
// Index 1 of FindStringSubmatch contains the subexpression match
// (variable name) of the capture group.
variable := matches[1]
// toExpand contains a variable.
if _, ok := seenVars[variable]; ok {
- panic(fmt.Errorf(
- "Unbounded recursive interpolation of variable: %s", variable))
+ return ret, fmt.Errorf(
+ "Unbounded recursive interpolation of variable: %s", variable)
}
// A map is passed-by-reference. Create a new map for
// this scope to prevent variables seen in one depth-first expansion
@@ -239,15 +253,65 @@
}
newSeenVars[variable] = true
if unexpandedVars, ok := stringListScope[variable]; ok {
+ expandedVars := []string{}
for _, unexpandedVar := range unexpandedVars {
- ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...)
+ expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
+ if err != nil {
+ return ret, err
+ }
+ expandedVars = append(expandedVars, expandedVar)
}
+ ret += strings.Join(expandedVars, " ")
} else if unexpandedVar, ok := stringScope[variable]; ok {
- ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...)
+ expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
+ if err != nil {
+ return ret, err
+ }
+ ret += expandedVar
+ } else if unevaluatedVar, ok := exportedVars[variable]; ok {
+ evalFunc := reflect.ValueOf(unevaluatedVar)
+ validateVariableMethod(variable, evalFunc)
+ evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
+ evaluatedValue := evaluatedResult[0].Interface().(string)
+ expandedVar, err := expandVarInternal(evaluatedValue, newSeenVars)
+ if err != nil {
+ return ret, err
+ }
+ ret += expandedVar
+ } else {
+ return "", fmt.Errorf("Unbound config variable %s", variable)
}
}
- return ret
+ return ret, nil
+ }
+ var ret []string
+ for _, v := range strings.Split(toExpand, " ") {
+ val, err := expandVarInternal(v, map[string]bool{})
+ if err != nil {
+ return ret, err
+ }
+ ret = append(ret, val)
}
- return expandVarInternal(toExpand, map[string]bool{})
+ return ret, nil
+}
+
+func validateVariableMethod(name string, methodValue reflect.Value) {
+ methodType := methodValue.Type()
+ if methodType.Kind() != reflect.Func {
+ panic(fmt.Errorf("method given for variable %s is not a function",
+ name))
+ }
+ if n := methodType.NumIn(); n != 1 {
+ panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
+ name, n))
+ }
+ if n := methodType.NumOut(); n != 1 {
+ panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
+ name, n))
+ }
+ if kind := methodType.Out(0).Kind(); kind != reflect.String {
+ panic(fmt.Errorf("method for variable %s does not return a string",
+ name))
+ }
}
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
index 883597a..4cbf0c6 100644
--- a/cc/config/bp2build_test.go
+++ b/cc/config/bp2build_test.go
@@ -16,13 +16,21 @@
import (
"testing"
+
+ "android/soong/android"
)
func TestExpandVars(t *testing.T) {
+ android_arm64_config := android.TestConfig("out", nil, "", nil)
+ android_arm64_config.BuildOS = android.Android
+ android_arm64_config.BuildArch = android.Arm64
+
testCases := []struct {
description string
+ config android.Config
stringScope exportedStringVariables
stringListScope exportedStringListVariables
+ configVars exportedConfigDependingVariables
toExpand string
expectedValues []string
}{
@@ -57,7 +65,7 @@
"bar": []string{"baz", "${qux}"},
},
toExpand: "${foo}",
- expectedValues: []string{"baz", "hello"},
+ expectedValues: []string{"baz hello"},
},
{
description: "double level expansion",
@@ -75,7 +83,7 @@
"b": []string{"d"},
},
toExpand: "${a}",
- expectedValues: []string{"d", "c"},
+ expectedValues: []string{"d c"},
},
{
description: "double level expansion, with two variables in a string",
@@ -85,7 +93,7 @@
"c": []string{"e"},
},
toExpand: "${a}",
- expectedValues: []string{"d", "e"},
+ expectedValues: []string{"d e"},
},
{
description: "triple level expansion with two variables in a string",
@@ -96,13 +104,38 @@
"d": []string{"foo"},
},
toExpand: "${a}",
- expectedValues: []string{"foo", "foo", "foo"},
+ expectedValues: []string{"foo foo foo"},
+ },
+ {
+ description: "expansion with config depending vars",
+ configVars: exportedConfigDependingVariables{
+ "a": func(c android.Config) string { return c.BuildOS.String() },
+ "b": func(c android.Config) string { return c.BuildArch.String() },
+ },
+ config: android_arm64_config,
+ toExpand: "${a}-${b}",
+ expectedValues: []string{"android-arm64"},
+ },
+ {
+ description: "double level multi type expansion",
+ stringListScope: exportedStringListVariables{
+ "platform": []string{"${os}-${arch}"},
+ "const": []string{"const"},
+ },
+ configVars: exportedConfigDependingVariables{
+ "os": func(c android.Config) string { return c.BuildOS.String() },
+ "arch": func(c android.Config) string { return c.BuildArch.String() },
+ "foo": func(c android.Config) string { return "foo" },
+ },
+ config: android_arm64_config,
+ toExpand: "${const}/${platform}/${foo}",
+ expectedValues: []string{"const/android-arm64/foo"},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
- output := expandVar(testCase.toExpand, testCase.stringScope, testCase.stringListScope)
+ output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars)
if len(output) != len(testCase.expectedValues) {
t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
}
@@ -119,6 +152,7 @@
func TestBazelToolchainVars(t *testing.T) {
testCases := []struct {
name string
+ config android.Config
vars []bazelVarExporter
expectedOut string
}{
@@ -177,15 +211,11 @@
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
_a = {
- "b1": [
- "b2",
- ],
+ "b1": ["b2"],
}
_c = {
- "d1": [
- "d2",
- ],
+ "d1": ["d2"],
}
constants = struct(
@@ -212,27 +242,19 @@
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
_a = {
- "a1": [
- "a2",
- ],
+ "a1": ["a2"],
}
_b = "b-val"
-_c = [
- "c-val",
-]
+_c = ["c-val"]
_d = "d-val"
-_e = [
- "e-val",
-]
+_e = ["e-val"]
_f = {
- "f1": [
- "f2",
- ],
+ "f1": ["f2"],
}
constants = struct(
@@ -248,7 +270,7 @@
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- out := bazelToolchainVars(tc.vars...)
+ out := bazelToolchainVars(tc.config, tc.vars...)
if out != tc.expectedOut {
t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
}
diff --git a/cc/config/x86_darwin_host.go b/cc/config/darwin_host.go
similarity index 65%
rename from cc/config/x86_darwin_host.go
rename to cc/config/darwin_host.go
index 0bb1a81..5e3f7c7 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -53,12 +53,8 @@
}
darwinSupportedSdkVersions = []string{
- "10.13",
- "10.14",
- "10.15",
- "11.0",
- "11.1",
- "11.3",
+ "11",
+ "12",
}
darwinAvailableLibraries = append(
@@ -92,6 +88,10 @@
return getMacTools(ctx).arPath
})
+ pctx.VariableFunc("MacLipoPath", func(ctx android.PackageVarContext) string {
+ return getMacTools(ctx).lipoPath
+ })
+
pctx.VariableFunc("MacStripPath", func(ctx android.PackageVarContext) string {
return getMacTools(ctx).stripPath
})
@@ -113,19 +113,24 @@
pctx.StaticVariable("DarwinYasmFlags", "-f macho -m amd64")
}
+func MacStripPath(ctx android.PathContext) string {
+ return getMacTools(ctx).stripPath
+}
+
type macPlatformTools struct {
once sync.Once
err error
sdkRoot string
arPath string
+ lipoPath string
stripPath string
toolPath string
}
var macTools = &macPlatformTools{}
-func getMacTools(ctx android.PackageVarContext) *macPlatformTools {
+func getMacTools(ctx android.PathContext) *macPlatformTools {
macTools.once.Do(func() {
xcrunTool := "/usr/bin/xcrun"
@@ -134,7 +139,7 @@
return ""
}
- bytes, err := exec.Command(xcrunTool, args...).Output()
+ bytes, err := exec.Command(xcrunTool, append([]string{"--sdk", "macosx"}, args...)...).Output()
if err != nil {
macTools.err = fmt.Errorf("xcrun %q failed with: %q", args, err)
return ""
@@ -143,34 +148,27 @@
return strings.TrimSpace(string(bytes))
}
- xcrunSdk := func(arg string) string {
- if selected := ctx.Config().Getenv("MAC_SDK_VERSION"); selected != "" {
- if !inList(selected, darwinSupportedSdkVersions) {
- macTools.err = fmt.Errorf("MAC_SDK_VERSION %s isn't supported: %q", selected, darwinSupportedSdkVersions)
- return ""
- }
-
- return xcrun("--sdk", "macosx"+selected, arg)
+ sdkVersion := xcrun("--show-sdk-version")
+ sdkVersionSupported := false
+ for _, version := range darwinSupportedSdkVersions {
+ if version == sdkVersion || strings.HasPrefix(sdkVersion, version+".") {
+ sdkVersionSupported = true
}
-
- for _, sdk := range darwinSupportedSdkVersions {
- bytes, err := exec.Command(xcrunTool, "--sdk", "macosx"+sdk, arg).Output()
- if err == nil {
- return strings.TrimSpace(string(bytes))
- }
- }
- macTools.err = fmt.Errorf("Could not find a supported mac sdk: %q", darwinSupportedSdkVersions)
- return ""
+ }
+ if !sdkVersionSupported {
+ macTools.err = fmt.Errorf("Unsupported macOS SDK version %q not in %v", sdkVersion, darwinSupportedSdkVersions)
+ return
}
- macTools.sdkRoot = xcrunSdk("--show-sdk-path")
+ macTools.sdkRoot = xcrun("--show-sdk-path")
macTools.arPath = xcrun("--find", "ar")
+ macTools.lipoPath = xcrun("--find", "lipo")
macTools.stripPath = xcrun("--find", "strip")
macTools.toolPath = filepath.Dir(xcrun("--find", "ld"))
})
if macTools.err != nil {
- ctx.Errorf("%q", macTools.err)
+ android.ReportPathErrorf(ctx, "%q", macTools.err)
}
return macTools
}
@@ -180,19 +178,43 @@
toolchain64Bit
}
-func (t *toolchainDarwin) Name() string {
+type toolchainDarwinX86 struct {
+ toolchainDarwin
+}
+
+type toolchainDarwinArm struct {
+ toolchainDarwin
+}
+
+func (t *toolchainDarwinArm) Name() string {
+ return "arm64"
+}
+
+func (t *toolchainDarwinX86) Name() string {
return "x86_64"
}
-func (t *toolchainDarwin) GccRoot() string {
+func (t *toolchainDarwinArm) GccRoot() string {
+ panic("unimplemented")
+}
+
+func (t *toolchainDarwinArm) GccTriple() string {
+ panic("unimplemented")
+}
+
+func (t *toolchainDarwinArm) GccVersion() string {
+ panic("unimplemented")
+}
+
+func (t *toolchainDarwinX86) GccRoot() string {
return "${config.DarwinGccRoot}"
}
-func (t *toolchainDarwin) GccTriple() string {
+func (t *toolchainDarwinX86) GccTriple() string {
return "${config.DarwinGccTriple}"
}
-func (t *toolchainDarwin) GccVersion() string {
+func (t *toolchainDarwinX86) GccVersion() string {
return darwinGccVersion
}
@@ -200,7 +222,11 @@
return ""
}
-func (t *toolchainDarwin) ClangTriple() string {
+func (t *toolchainDarwinArm) ClangTriple() string {
+ return "aarch64-apple-darwin"
+}
+
+func (t *toolchainDarwinX86) ClangTriple() string {
return "x86_64-apple-darwin"
}
@@ -232,16 +258,26 @@
return darwinAvailableLibraries
}
-func (t *toolchainDarwin) ToolPath() string {
- return "${config.MacToolPath}"
+func (t *toolchainDarwin) ToolchainCflags() string {
+ return "-B${config.MacToolPath}"
}
-var toolchainDarwinSingleton Toolchain = &toolchainDarwin{}
+func (t *toolchainDarwin) ToolchainLdflags() string {
+ return "-B${config.MacToolPath}"
+}
-func darwinToolchainFactory(arch android.Arch) Toolchain {
- return toolchainDarwinSingleton
+var toolchainDarwinArmSingleton Toolchain = &toolchainDarwinArm{}
+var toolchainDarwinX86Singleton Toolchain = &toolchainDarwinX86{}
+
+func darwinArmToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainDarwinArmSingleton
+}
+
+func darwinX86ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainDarwinX86Singleton
}
func init() {
- registerToolchainFactory(android.Darwin, android.X86_64, darwinToolchainFactory)
+ registerToolchainFactory(android.Darwin, android.Arm64, darwinArmToolchainFactory)
+ registerToolchainFactory(android.Darwin, android.X86_64, darwinX86ToolchainFactory)
}
diff --git a/cc/config/global.go b/cc/config/global.go
index 5f41f9e..400be31 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -46,7 +46,7 @@
"-O2",
"-g",
- "-fdebug-info-for-profiling",
+ "-fdebug-default-version=5",
"-fno-strict-aliasing",
@@ -77,10 +77,6 @@
// TODO: can we remove this now?
"-Wno-reserved-id-macro",
- // Workaround for ccache with clang.
- // See http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html.
- "-Wno-unused-command-line-argument",
-
// Force clang to always output color diagnostics. Ninja will strip the ANSI
// color codes if it is not running in a terminal.
"-fcolor-diagnostics",
@@ -99,6 +95,9 @@
// Nested and array designated initialization is nice to have.
"-Wno-c99-designator",
+ // Many old files still have GNU designator syntax.
+ "-Wno-gnu-designator",
+
// Warnings from clang-12
"-Wno-gnu-folding-constant",
@@ -108,6 +107,9 @@
// This macro allows the bionic versioning.h to indirectly determine whether the
// option -Wunguarded-availability is on or not.
"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
+
+ // Turn off FMA which got enabled by default in clang-r445002 (http://b/218805949)
+ "-ffp-contract=off",
}
commonGlobalConlyflags = []string{}
@@ -129,6 +131,9 @@
"-Werror=sequence-point",
"-Werror=format-security",
"-nostdlibinc",
+
+ // Emit additional debug info for AutoFDO
+ "-fdebug-info-for-profiling",
}
deviceGlobalCppflags = []string{
@@ -140,7 +145,6 @@
"-Wl,-z,relro",
"-Wl,-z,now",
"-Wl,--build-id=md5",
- "-Wl,--warn-shared-textrel",
"-Wl,--fatal-warnings",
"-Wl,--no-undefined-version",
// TODO: Eventually we should link against a libunwind.a with hidden symbols, and then these
@@ -226,8 +230,6 @@
"-Wno-pessimizing-move", // http://b/154270751
// New warnings to be fixed after clang-r399163
"-Wno-non-c-typedef-for-linkage", // http://b/161304145
- // New warnings to be fixed after clang-r407598
- "-Wno-string-concatenation", // http://b/175068488
// New warnings to be fixed after clang-r428724
"-Wno-align-mismatch", // http://b/193679946
// New warnings to be fixed after clang-r433403
@@ -235,6 +237,14 @@
"-Wno-error=unused-but-set-parameter", // http://b/197240255
}
+ noOverrideExternalGlobalCflags = []string{
+ // http://b/197240255
+ "-Wno-unused-but-set-variable",
+ "-Wno-unused-but-set-parameter",
+ // http://b/215753485
+ "-Wno-bitwise-instead-of-logical",
+ }
+
// Extra cflags for external third-party projects to disable warnings that
// are infeasible to fix in all the external projects and their upstream repos.
extraExternalCflags = []string{
@@ -261,6 +271,9 @@
// http://b/199369603
"-Wno-null-pointer-subtraction",
+
+ // http://b/175068488
+ "-Wno-string-concatenation",
}
IllegalFlags = []string{
@@ -274,8 +287,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r433403"
- ClangDefaultShortVersion = "13.0.2"
+ ClangDefaultVersion = "clang-r445002"
+ ClangDefaultShortVersion = "14.0.2"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
@@ -329,6 +342,12 @@
// 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")
}
+
+ // Workaround for ccache with clang.
+ // See http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html.
+ if ctx.Config().IsEnvTrue("USE_CCACHE") {
+ flags = append(flags, "-Wno-unused-command-line-argument")
+ }
return strings.Join(flags, " ")
})
@@ -342,6 +361,7 @@
exportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
exportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
+ exportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
exportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
exportStringListStaticVariable("ExternalCflags", extraExternalCflags)
@@ -364,28 +384,12 @@
exportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
exportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
- pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
- pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string {
- if override := ctx.Config().Getenv("LLVM_PREBUILTS_BASE"); override != "" {
- return override
- }
- return "${ClangDefaultBase}"
- })
- pctx.VariableFunc("ClangVersion", func(ctx android.PackageVarContext) string {
- if override := ctx.Config().Getenv("LLVM_PREBUILTS_VERSION"); override != "" {
- return override
- }
- return ClangDefaultVersion
- })
+ pctx.StaticVariableWithEnvOverride("ClangBase", "LLVM_PREBUILTS_BASE", ClangDefaultBase)
+ pctx.StaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion)
pctx.StaticVariable("ClangPath", "${ClangBase}/${HostPrebuiltTag}/${ClangVersion}")
pctx.StaticVariable("ClangBin", "${ClangPath}/bin")
- pctx.VariableFunc("ClangShortVersion", func(ctx android.PackageVarContext) string {
- if override := ctx.Config().Getenv("LLVM_RELEASE_VERSION"); override != "" {
- return override
- }
- return ClangDefaultShortVersion
- })
+ pctx.StaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib64/clang/${ClangShortVersion}/lib/linux")
// These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts
@@ -418,4 +422,30 @@
pctx.StaticVariableWithEnvOverride("REAbiLinkerExecStrategy", "RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
}
-var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+var HostPrebuiltTag = exportVariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+
+func ClangPath(ctx android.PathContext, file string) android.SourcePath {
+ type clangToolKey string
+
+ key := android.NewCustomOnceKey(clangToolKey(file))
+
+ return ctx.Config().OnceSourcePath(key, func() android.SourcePath {
+ return clangPath(ctx).Join(ctx, file)
+ })
+}
+
+var clangPathKey = android.NewOnceKey("clangPath")
+
+func clangPath(ctx android.PathContext) android.SourcePath {
+ return ctx.Config().OnceSourcePath(clangPathKey, func() android.SourcePath {
+ clangBase := ClangDefaultBase
+ if override := ctx.Config().Getenv("LLVM_PREBUILTS_BASE"); override != "" {
+ clangBase = override
+ }
+ clangVersion := ClangDefaultVersion
+ if override := ctx.Config().Getenv("LLVM_PREBUILTS_VERSION"); override != "" {
+ clangVersion = override
+ }
+ return android.PathForSource(ctx, clangBase, ctx.Config().PrebuiltOS(), clangVersion)
+ })
+}
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index fdc246c..ba1043b 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -62,8 +62,9 @@
}, ",")
// clang-analyzer-* checks are too slow to be in the default for WITH_TIDY=1.
// nightly builds add CLANG_ANALYZER_CHECKS=1 to run those checks.
+ // The insecureAPI.DeprecatedOrUnsafeBufferHandling warning does not apply to Android.
if ctx.Config().IsEnvTrue("CLANG_ANALYZER_CHECKS") {
- checks += ",clang-analyzer-*"
+ checks += ",clang-analyzer-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling"
}
return checks
})
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 20384a8..6cede11 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "path/filepath"
"android/soong/android"
)
@@ -77,7 +76,6 @@
GccTriple() string
// GccVersion should return a real value, not a ninja reference
GccVersion() string
- ToolPath() string
IncludeFlags() string
@@ -95,8 +93,6 @@
YasmFlags() string
- WindresFlags() string
-
Is64Bit() bool
ShlibSuffix() string
@@ -169,10 +165,6 @@
return ""
}
-func (toolchainBase) WindresFlags() string {
- return ""
-}
-
func (toolchainBase) LibclangRuntimeLibraryArch() string {
return ""
}
@@ -204,10 +196,6 @@
return false
}
-func (t toolchainBase) ToolPath() string {
- return ""
-}
-
type toolchain64Bit struct {
toolchainBase
}
@@ -289,11 +277,4 @@
return LibclangRuntimeLibrary(t, "fuzzer")
}
-func ToolPath(t Toolchain) string {
- if p := t.ToolPath(); p != "" {
- return p
- }
- return filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
-}
-
var inList = android.InList
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index e72efae..492cd98 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -25,12 +25,20 @@
"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.gnss-V1-ndk",
+ "android.hardware.gnss-V1-ndk_platform",
+ "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",
"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",
@@ -44,8 +52,9 @@
"android.hardware.oemlock-V1-ndk_platform",
"android.hardware.oemlock-ndk_platform",
"android.hardware.oemlock-unstable-ndk_platform",
- "android.hardware.power-V1-ndk",
"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",
@@ -82,20 +91,25 @@
"android.hardware.security.sharedsecret-V1-ndk_platform",
"android.hardware.security.sharedsecret-ndk_platform",
"android.hardware.security.sharedsecret-unstable-ndk_platform",
- "android.hardware.vibrator-V1-ndk",
"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",
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 00f07ff..d789cde 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -15,6 +15,7 @@
package config
import (
+ "fmt"
"strings"
"android/soong/android"
@@ -190,6 +191,11 @@
}
func x86_64ToolchainFactory(arch android.Arch) Toolchain {
+ // Error now rather than having a confusing Ninja error
+ if _, ok := x86_64ArchVariantCflags[arch.ArchVariant]; !ok {
+ panic(fmt.Sprintf("Unknown x86_64 architecture version: %q", arch.ArchVariant))
+ }
+
toolchainCflags := []string{
"${config.X86_64ToolchainCflags}",
"${config.X86_64" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 29f0593..e32e1bd 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -15,6 +15,7 @@
package config
import (
+ "fmt"
"strings"
"android/soong/android"
@@ -186,6 +187,11 @@
}
func x86ToolchainFactory(arch android.Arch) Toolchain {
+ // Error now rather than having a confusing Ninja error
+ if _, ok := x86ArchVariantCflags[arch.ArchVariant]; !ok {
+ panic(fmt.Sprintf("Unknown x86 architecture version: %q", arch.ArchVariant))
+ }
+
toolchainCflags := []string{
"${config.X86ToolchainCflags}",
"${config.X86" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index 4b7ba6a..976cc25 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -47,7 +47,6 @@
"-Wl,-z,relro",
"-Wl,-z,now",
"-Wl,--build-id=md5",
- "-Wl,--warn-shared-textrel",
"-Wl,--fatal-warnings",
"-Wl,--hash-style=gnu",
"-Wl,--no-undefined-version",
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index ac5d5f7..ce6836b 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -47,6 +47,7 @@
"-D_LIBCPP_HAS_MUSL_LIBC",
"-DANDROID_HOST_MUSL",
"-nostdlibinc",
+ "--sysroot /dev/null",
}
linuxLdflags = []string{
@@ -65,6 +66,7 @@
linuxMuslLdflags = []string{
"-nostdlib",
"-lgcc", "-lgcc_eh",
+ "--sysroot /dev/null",
}
// Extended cflags
@@ -120,40 +122,40 @@
)
func init() {
- pctx.StaticVariable("LinuxGccVersion", linuxGccVersion)
- pctx.StaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
+ exportStringStaticVariable("LinuxGccVersion", linuxGccVersion)
+ exportStringStaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
+
// Most places use the full GCC version. A few only use up to the first two numbers.
if p := strings.Split(linuxGccVersion, "."); len(p) > 2 {
- pctx.StaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
+ exportStringStaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
} else {
- pctx.StaticVariable("ShortLinuxGccVersion", linuxGccVersion)
+ exportStringStaticVariable("ShortLinuxGccVersion", linuxGccVersion)
}
- pctx.SourcePathVariable("LinuxGccRoot",
- "prebuilts/gcc/${HostPrebuiltTag}/host/x86_64-linux-glibc${LinuxGlibcVersion}-${ShortLinuxGccVersion}")
+ exportSourcePathVariable("LinuxGccRoot",
+ "prebuilts/gcc/linux-x86/host/x86_64-linux-glibc${LinuxGlibcVersion}-${ShortLinuxGccVersion}")
- pctx.StaticVariable("LinuxGccTriple", "x86_64-linux")
+ exportStringListStaticVariable("LinuxGccTriple", []string{"x86_64-linux"})
- pctx.StaticVariable("LinuxCflags", strings.Join(linuxCflags, " "))
- pctx.StaticVariable("LinuxLdflags", strings.Join(linuxLdflags, " "))
- pctx.StaticVariable("LinuxLldflags", strings.Join(linuxLdflags, " "))
+ exportStringListStaticVariable("LinuxCflags", linuxCflags)
+ exportStringListStaticVariable("LinuxLdflags", linuxLdflags)
+ exportStringListStaticVariable("LinuxLldflags", linuxLdflags)
+ exportStringListStaticVariable("LinuxGlibcCflags", linuxGlibcCflags)
+ exportStringListStaticVariable("LinuxGlibcLdflags", linuxGlibcLdflags)
+ exportStringListStaticVariable("LinuxGlibcLldflags", linuxGlibcLdflags)
+ exportStringListStaticVariable("LinuxMuslCflags", linuxMuslCflags)
+ exportStringListStaticVariable("LinuxMuslLdflags", linuxMuslLdflags)
+ exportStringListStaticVariable("LinuxMuslLldflags", linuxMuslLdflags)
- pctx.StaticVariable("LinuxX86Cflags", strings.Join(linuxX86Cflags, " "))
- pctx.StaticVariable("LinuxX8664Cflags", strings.Join(linuxX8664Cflags, " "))
- pctx.StaticVariable("LinuxX86Ldflags", strings.Join(linuxX86Ldflags, " "))
- pctx.StaticVariable("LinuxX86Lldflags", strings.Join(linuxX86Ldflags, " "))
- pctx.StaticVariable("LinuxX8664Ldflags", strings.Join(linuxX8664Ldflags, " "))
- pctx.StaticVariable("LinuxX8664Lldflags", strings.Join(linuxX8664Ldflags, " "))
+ exportStringListStaticVariable("LinuxX86Cflags", linuxX86Cflags)
+ exportStringListStaticVariable("LinuxX8664Cflags", linuxX8664Cflags)
+ exportStringListStaticVariable("LinuxX86Ldflags", linuxX86Ldflags)
+ exportStringListStaticVariable("LinuxX86Lldflags", linuxX86Ldflags)
+ exportStringListStaticVariable("LinuxX8664Ldflags", linuxX8664Ldflags)
+ exportStringListStaticVariable("LinuxX8664Lldflags", linuxX8664Ldflags)
// Yasm flags
- pctx.StaticVariable("LinuxX86YasmFlags", "-f elf32 -m x86")
- pctx.StaticVariable("LinuxX8664YasmFlags", "-f elf64 -m amd64")
-
- pctx.StaticVariable("LinuxGlibcCflags", strings.Join(linuxGlibcCflags, " "))
- pctx.StaticVariable("LinuxGlibcLdflags", strings.Join(linuxGlibcLdflags, " "))
- pctx.StaticVariable("LinuxGlibcLldflags", strings.Join(linuxGlibcLdflags, " "))
- pctx.StaticVariable("LinuxMuslCflags", strings.Join(linuxMuslCflags, " "))
- pctx.StaticVariable("LinuxMuslLdflags", strings.Join(linuxMuslLdflags, " "))
- pctx.StaticVariable("LinuxMuslLldflags", strings.Join(linuxMuslLdflags, " "))
+ exportStringListStaticVariable("LinuxX86YasmFlags", []string{"-f elf32 -m x86"})
+ exportStringListStaticVariable("LinuxX8664YasmFlags", []string{"-f elf64 -m amd64"})
}
type toolchainLinux struct {
@@ -194,10 +196,6 @@
return ""
}
-func (t *toolchainLinuxX86) ClangTriple() string {
- return "i686-linux-gnu"
-}
-
func (t *toolchainLinuxX86) Cflags() string {
return "${config.LinuxCflags} ${config.LinuxX86Cflags}"
}
@@ -206,10 +204,6 @@
return ""
}
-func (t *toolchainLinuxX8664) ClangTriple() string {
- return "x86_64-linux-gnu"
-}
-
func (t *toolchainLinuxX8664) Cflags() string {
return "${config.LinuxCflags} ${config.LinuxX8664Cflags}"
}
@@ -283,6 +277,10 @@
toolchainGlibc
}
+func (t *toolchainLinuxGlibcX86) ClangTriple() string {
+ return "i686-linux-gnu"
+}
+
func (t *toolchainLinuxGlibcX86) Cflags() string {
return t.toolchainLinuxX86.Cflags() + " " + t.toolchainGlibc.Cflags()
}
@@ -295,6 +293,10 @@
return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainGlibc.Lldflags()
}
+func (t *toolchainLinuxGlibcX8664) ClangTriple() string {
+ return "x86_64-linux-gnu"
+}
+
func (t *toolchainLinuxGlibcX8664) Cflags() string {
return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainGlibc.Cflags()
}
@@ -356,6 +358,10 @@
toolchainMusl
}
+func (t *toolchainLinuxMuslX86) ClangTriple() string {
+ return "i686-linux-musl"
+}
+
func (t *toolchainLinuxMuslX86) Cflags() string {
return t.toolchainLinuxX86.Cflags() + " " + t.toolchainMusl.Cflags()
}
@@ -368,6 +374,10 @@
return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainMusl.Lldflags()
}
+func (t *toolchainLinuxMuslX8664) ClangTriple() string {
+ return "x86_64-linux-musl"
+}
+
func (t *toolchainLinuxMuslX8664) Cflags() string {
return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainMusl.Cflags()
}
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index d9a7537..2c83211 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -15,6 +15,7 @@
package config
import (
+ "path/filepath"
"strings"
"android/soong/android"
@@ -180,6 +181,14 @@
return "${config.WindowsGccTriple}"
}
+func (t *toolchainWindows) ToolchainCflags() string {
+ return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
+func (t *toolchainWindows) ToolchainLdflags() string {
+ return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
func (t *toolchainWindows) GccVersion() string {
return windowsGccVersion
}
@@ -188,14 +197,6 @@
return "${config.WindowsIncludeFlags}"
}
-func (t *toolchainWindowsX86) WindresFlags() string {
- return "-F pe-i386"
-}
-
-func (t *toolchainWindowsX8664) WindresFlags() string {
- return "-F pe-x86-64"
-}
-
func (t *toolchainWindowsX86) ClangTriple() string {
return "i686-windows-gnu"
}
diff --git a/cc/coverage.go b/cc/coverage.go
index 8dd2db1..f2b5425 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -98,6 +98,9 @@
} else if clangCoverage {
flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag,
"-fcoverage-mapping", "-Wno-pass-failed", "-D__ANDROID_CLANG_COVERAGE__")
+ // Override -Wframe-larger-than. We can expect frame size increase after
+ // coverage instrumentation.
+ flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=")
}
}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 83f0037..23d81d6 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -52,6 +52,10 @@
installedSharedDeps []string
}
+func (fuzz *fuzzBinary) fuzzBinary() bool {
+ return true
+}
+
func (fuzz *fuzzBinary) linkerProps() []interface{} {
props := fuzz.binaryDecorator.linkerProps()
props = append(props, &fuzz.fuzzPackagedModule.FuzzProperties)
@@ -79,54 +83,21 @@
return flags
}
-// This function 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) 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
-
- ccModule := module.(*Module)
- sharedLibraries = append(sharedLibraries, ccModule.UnstrippedOutputFile())
- ctx.VisitDirectDeps(module, func(dep android.Module) {
- if isValidSharedDependency(dep) && !seen[dep.Name()] {
- fringe = append(fringe, dep)
- }
- })
+func UnstrippedOutputFile(module android.Module) android.Path {
+ if mod, ok := module.(LinkableInterface); ok {
+ return mod.UnstrippedOutputFile()
}
-
- return sharedLibraries
+ panic("UnstrippedOutputFile called on non-LinkableInterface module: " + module.Name())
}
-// This function takes a module and determines if it is a unique shared library
+// IsValidSharedDependency takes a module and determines if it is a unique shared library
// that should be installed in the fuzz target output directories. This function
// returns true, unless:
// - The module is not an installable shared library, or
// - The module is a header or stub, or
// - The module is a prebuilt and its source is available, or
// - The module is a versioned member of an SDK snapshot.
-func isValidSharedDependency(dependency android.Module) bool {
+func IsValidSharedDependency(dependency android.Module) bool {
// TODO(b/144090547): We should be parsing these modules using
// ModuleDependencyTag instead of the current brute-force checking.
@@ -156,7 +127,7 @@
}
// Discard installable:false libraries because they are expected to be absent
// in runtime.
- if !proptools.BoolDefault(ccLibrary.Properties.Installable, true) {
+ if !proptools.BoolDefault(ccLibrary.Installable(), true) {
return false
}
}
@@ -246,7 +217,7 @@
}
seen[child.Name()] = true
- if isValidSharedDependency(child) {
+ if IsValidSharedDependency(child) {
sharedLibraries = append(sharedLibraries, child.(*Module).UnstrippedOutputFile())
return true
}
@@ -267,7 +238,7 @@
}
func NewFuzz(hod android.HostOrDeviceSupported) *Module {
- module, binary := NewBinary(hod)
+ module, binary := newBinary(hod, false)
binary.baseInstaller = NewFuzzInstaller()
module.sanitize.SetSanitizer(Fuzzer, true)
@@ -304,7 +275,6 @@
// their architecture & target/host specific zip file.
type ccFuzzPackager struct {
fuzz.FuzzPackager
- sharedLibInstallStrings []string
}
func fuzzPackagingFactory() android.Singleton {
@@ -317,14 +287,14 @@
// archive}).
archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
- // 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)
-
// List of individual fuzz targets, so that 'make fuzz' also installs the targets
// to the correct output directories as well.
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) {
ccModule, ok := module.(*Module)
if !ok || ccModule.Properties.PreventInstall {
@@ -351,7 +321,7 @@
archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
// Grab the list of required shared libraries.
- sharedLibraries := collectAllSharedDependencies(ctx, module)
+ sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, UnstrippedOutputFile, IsValidSharedDependency)
var files []fuzz.FileToZip
builder := android.NewRuleBuilder(pctx, ctx)
@@ -359,39 +329,8 @@
// Package the corpus, data, dict and config into a zipfile.
files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
- // Find and mark all the transiently-dependent shared libraries for
- // packaging.
- for _, library := range sharedLibraries {
- files = append(files, fuzz.FileToZip{library, "lib"})
-
- // 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, ccModule.Host(), archString)
- if sharedLibraryInstalled[installDestination] {
- continue
- }
- sharedLibraryInstalled[installDestination] = true
-
- // Escape all the variables, as the install destination here will be called
- // via. $(eval) in Make.
- installDestination = strings.ReplaceAll(
- installDestination, "$", "$$")
- s.sharedLibInstallStrings = append(s.sharedLibInstallStrings,
- library.String()+":"+installDestination)
-
- // Ensure that on device, the library is also reinstalled to the /symbols/
- // dir. Symbolized DSO's are always installed to the device when fuzzing, but
- // we want symbolization tools (like `stack`) to be able to find the symbols
- // in $ANDROID_PRODUCT_OUT/symbols automagically.
- if !ccModule.Host() {
- symbolsInstallDestination := sharedLibrarySymbolsInstallLocation(library, archString)
- symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
- s.sharedLibInstallStrings = append(s.sharedLibInstallStrings,
- library.String()+":"+symbolsInstallDestination)
- }
- }
+ // Package shared libraries
+ files = append(files, GetSharedLibsToZip(sharedLibraries, ccModule, &s.FuzzPackager, archString, &sharedLibraryInstalled)...)
// The executable.
files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
@@ -409,15 +348,54 @@
func (s *ccFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
packages := s.Packages.Strings()
sort.Strings(packages)
- sort.Strings(s.sharedLibInstallStrings)
+ sort.Strings(s.FuzzPackager.SharedLibInstallStrings)
// TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's
// 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",
- strings.Join(s.sharedLibInstallStrings, " "))
+ strings.Join(s.FuzzPackager.SharedLibInstallStrings, " "))
// Preallocate the slice of fuzz targets to minimise memory allocations.
s.PreallocateSlice(ctx, "ALL_FUZZ_TARGETS")
}
+
+// 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 {
+ var files []fuzz.FileToZip
+
+ for _, library := range sharedLibraries {
+ files = append(files, fuzz.FileToZip{library, "lib"})
+
+ // 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)
+ if (*sharedLibraryInstalled)[installDestination] {
+ continue
+ }
+ (*sharedLibraryInstalled)[installDestination] = true
+
+ // Escape all the variables, as the install destination here will be called
+ // via. $(eval) in Make.
+ installDestination = strings.ReplaceAll(
+ installDestination, "$", "$$")
+ s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
+ library.String()+":"+installDestination)
+
+ // Ensure that on device, the library is also reinstalled to the /symbols/
+ // dir. Symbolized DSO's are always installed to the device when fuzzing, but
+ // 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 = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
+ s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
+ library.String()+":"+symbolsInstallDestination)
+ }
+ }
+ return files
+}
diff --git a/cc/gen.go b/cc/gen.go
index 3a1a0e2..8f62363 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -45,13 +45,6 @@
CommandDeps: []string{"$syspropCmd"},
},
"headerOutDir", "publicOutDir", "srcOutDir", "includeName")
-
- windmc = pctx.AndroidStaticRule("windmc",
- blueprint.RuleParams{
- Command: "$windmcCmd -r$$(dirname $out) -h$$(dirname $out) $in",
- CommandDeps: []string{"$windmcCmd"},
- },
- "windmcCmd")
)
type YaccProperties struct {
@@ -200,26 +193,6 @@
return cppFile, headers.Paths()
}
-func genWinMsg(ctx android.ModuleContext, srcFile android.Path, flags builderFlags) (android.Path, android.Path) {
- headerFile := android.GenPathWithExt(ctx, "windmc", srcFile, "h")
- rcFile := android.GenPathWithExt(ctx, "windmc", srcFile, "rc")
-
- windmcCmd := mingwCmd(flags.toolchain, "windmc")
-
- ctx.Build(pctx, android.BuildParams{
- Rule: windmc,
- Description: "windmc " + srcFile.Rel(),
- Output: rcFile,
- ImplicitOutput: headerFile,
- Input: srcFile,
- Args: map[string]string{
- "windmcCmd": windmcCmd,
- },
- })
-
- return rcFile, headerFile
-}
-
// Used to communicate information from the genSources method back to the library code that uses
// it.
type generatedSourceInfo struct {
@@ -305,10 +278,6 @@
cppFile := rsGeneratedCppFile(ctx, srcFile)
rsFiles = append(rsFiles, srcFiles[i])
srcFiles[i] = cppFile
- case ".mc":
- rcFile, headerFile := genWinMsg(ctx, srcFile, buildFlags)
- srcFiles[i] = rcFile
- deps = append(deps, headerFile)
case ".sysprop":
cppFile, headerFiles := genSysprop(ctx, srcFile)
srcFiles[i] = cppFile
diff --git a/cc/installer.go b/cc/installer.go
index f95b493..2522610 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -29,6 +29,9 @@
// Install output directly in {partition}/, not in any subdir. This is only intended for use by
// init_first_stage.
Install_in_root *bool `android:"arch_variant"`
+
+ // Install output directly in {partition}/xbin
+ Install_in_xbin *bool `android:"arch_vvariant"`
}
type installLocation int
@@ -73,6 +76,8 @@
if installer.installInRoot() {
dir = ""
+ } else if installer.installInXbin() {
+ dir = "xbin"
}
if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -123,3 +128,7 @@
func (installer *baseInstaller) installInRoot() bool {
return Bool(installer.Properties.Install_in_root)
}
+
+func (installer *baseInstaller) installInXbin() bool {
+ return Bool(installer.Properties.Install_in_xbin)
+}
diff --git a/cc/libbuildversion/Android.bp b/cc/libbuildversion/Android.bp
index 4debb1c..b105a30 100644
--- a/cc/libbuildversion/Android.bp
+++ b/cc/libbuildversion/Android.bp
@@ -14,8 +14,10 @@
enabled: true,
},
},
+ min_sdk_version: "26",
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
],
+ vendor_available: true,
}
diff --git a/cc/libbuildversion/libbuildversion.cpp b/cc/libbuildversion/libbuildversion.cpp
index 5242025..1e01c11 100644
--- a/cc/libbuildversion/libbuildversion.cpp
+++ b/cc/libbuildversion/libbuildversion.cpp
@@ -36,7 +36,11 @@
return soong_build_number;
}
+#ifdef __ANDROID_VENDOR__
+ const prop_info* pi = __system_property_find("ro.vendor.build.version.incremental");
+#else
const prop_info* pi = __system_property_find("ro.build.version.incremental");
+#endif
if (pi == nullptr) return "";
std::string property_value;
diff --git a/cc/library.go b/cc/library.go
index 77eddbf..5fa3471 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -32,7 +32,7 @@
"github.com/google/blueprint/pathtools"
)
-// LibraryProperties is a collection of properties shared by cc library rules.
+// LibraryProperties is a collection of properties shared by cc library rules/cc.
type LibraryProperties struct {
// local file name to pass to the linker as -unexported_symbols_list
Unexported_symbols_list *string `android:"path,arch_variant"`
@@ -145,6 +145,8 @@
Tidy_disabled_srcs []string `android:"path,arch_variant"`
+ Tidy_timeout_srcs []string `android:"path,arch_variant"`
+
Sanitized Sanitized `android:"arch_variant"`
Cflags []string `android:"arch_variant"`
@@ -159,6 +161,8 @@
Export_static_lib_headers []string `android:"arch_variant"`
Apex_available []string `android:"arch_variant"`
+
+ Installable *bool `android:"arch_variant"`
}
type LibraryMutatedProperties struct {
@@ -205,10 +209,6 @@
func init() {
RegisterLibraryBuildComponents(android.InitRegistrationContext)
-
- android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
- android.RegisterBp2BuildMutator("cc_library_shared", CcLibrarySharedBp2Build)
- android.RegisterBp2BuildMutator("cc_library", CcLibraryBp2Build)
}
func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -234,12 +234,13 @@
Hdrs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- Implementation_deps bazel.LabelListAttribute
- Dynamic_deps bazel.LabelListAttribute
- Implementation_dynamic_deps bazel.LabelListAttribute
- Whole_archive_deps bazel.LabelListAttribute
- System_dynamic_deps bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Implementation_deps bazel.LabelListAttribute
+ Dynamic_deps bazel.LabelListAttribute
+ Implementation_dynamic_deps bazel.LabelListAttribute
+ Whole_archive_deps bazel.LabelListAttribute
+ Implementation_whole_archive_deps bazel.LabelListAttribute
+ System_dynamic_deps bazel.LabelListAttribute
Export_includes bazel.StringListAttribute
Export_system_includes bazel.StringListAttribute
@@ -248,16 +249,22 @@
Linkopts bazel.StringListAttribute
Use_libcrt bazel.BoolAttribute
Rtti bazel.BoolAttribute
- Stl *string
+
+ Stl *string
+ Cpp_std *string
+ C_std *string
// This is shared only.
- Version_script bazel.LabelAttribute
+ Link_crt bazel.BoolAttribute
+ Additional_linker_inputs bazel.LabelListAttribute
// Common properties shared between both shared and static variants.
Shared staticOrSharedAttributes
Static staticOrSharedAttributes
Strip stripAttributes
+
+ Features bazel.StringListAttribute
}
type stripAttributes struct {
@@ -268,64 +275,105 @@
None bazel.BoolAttribute
}
-func CcLibraryBp2Build(ctx android.TopDownMutatorContext) {
- m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build(ctx) {
- return
- }
-
- if ctx.ModuleType() != "cc_library" {
- return
- }
-
+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()) {
- ccSharedOrStaticBp2BuildMutatorInternal(ctx, m, "cc_library_static")
+ sharedOrStaticLibraryBp2Build(ctx, m, true)
return
}
sharedAttrs := bp2BuildParseSharedProps(ctx, m)
staticAttrs := bp2BuildParseStaticProps(ctx, m)
- compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
- linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
- exportedIncludes := bp2BuildParseExportedIncludes(ctx, m)
+ baseAttributes := bp2BuildParseBaseProps(ctx, m)
+ compilerAttrs := baseAttributes.compilerAttributes
+ linkerAttrs := baseAttributes.linkerAttributes
+ exportedIncludes := bp2BuildParseExportedIncludes(ctx, m, compilerAttrs.includes)
srcs := compilerAttrs.srcs
+ sharedAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
+ staticAttrs.Deps.Add(baseAttributes.protoDependency)
+
asFlags := compilerAttrs.asFlags
if compilerAttrs.asSrcs.IsEmpty() && sharedAttrs.Srcs_as.IsEmpty() && staticAttrs.Srcs_as.IsEmpty() {
// Skip asflags for BUILD file simplicity if there are no assembly sources.
asFlags = bazel.MakeStringListAttribute(nil)
}
- attrs := &bazelCcLibraryAttributes{
- Srcs: srcs,
- Srcs_c: compilerAttrs.cSrcs,
- Srcs_as: compilerAttrs.asSrcs,
+ staticCommonAttrs := staticOrSharedAttributes{
+ Srcs: *srcs.Clone().Append(staticAttrs.Srcs),
+ Srcs_c: *compilerAttrs.cSrcs.Clone().Append(staticAttrs.Srcs_c),
+ Srcs_as: *compilerAttrs.asSrcs.Clone().Append(staticAttrs.Srcs_as),
+ Copts: *compilerAttrs.copts.Clone().Append(staticAttrs.Copts),
+ Hdrs: *compilerAttrs.hdrs.Clone().Append(staticAttrs.Hdrs),
- Copts: compilerAttrs.copts,
+ Deps: *linkerAttrs.deps.Clone().Append(staticAttrs.Deps),
+ Implementation_deps: *linkerAttrs.implementationDeps.Clone().Append(staticAttrs.Implementation_deps),
+ Dynamic_deps: *linkerAttrs.dynamicDeps.Clone().Append(staticAttrs.Dynamic_deps),
+ Implementation_dynamic_deps: *linkerAttrs.implementationDynamicDeps.Clone().Append(staticAttrs.Implementation_dynamic_deps),
+ 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),
+ }
+
+ sharedCommonAttrs := staticOrSharedAttributes{
+ Srcs: *srcs.Clone().Append(sharedAttrs.Srcs),
+ Srcs_c: *compilerAttrs.cSrcs.Clone().Append(sharedAttrs.Srcs_c),
+ Srcs_as: *compilerAttrs.asSrcs.Clone().Append(sharedAttrs.Srcs_as),
+ 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),
+ }
+
+ staticTargetAttrs := &bazelCcLibraryStaticAttributes{
+ staticOrSharedAttributes: staticCommonAttrs,
+
Cppflags: compilerAttrs.cppFlags,
Conlyflags: compilerAttrs.conlyFlags,
Asflags: asFlags,
- Implementation_deps: linkerAttrs.implementationDeps,
- Deps: linkerAttrs.deps,
- Implementation_dynamic_deps: linkerAttrs.implementationDynamicDeps,
- Dynamic_deps: linkerAttrs.dynamicDeps,
- Whole_archive_deps: linkerAttrs.wholeArchiveDeps,
- System_dynamic_deps: linkerAttrs.systemDynamicDeps,
- Export_includes: exportedIncludes.Includes,
- Export_system_includes: exportedIncludes.SystemIncludes,
- Local_includes: compilerAttrs.localIncludes,
- Absolute_includes: compilerAttrs.absoluteIncludes,
- Linkopts: linkerAttrs.linkopts,
- Use_libcrt: linkerAttrs.useLibcrt,
- Rtti: compilerAttrs.rtti,
- Stl: compilerAttrs.stl,
+ Export_includes: exportedIncludes.Includes,
+ Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+ 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,
- Version_script: linkerAttrs.versionScript,
+ Features: linkerAttrs.features,
+ }
+
+ sharedTargetAttrs := &bazelCcLibrarySharedAttributes{
+ staticOrSharedAttributes: sharedCommonAttrs,
+ Cppflags: compilerAttrs.cppFlags,
+ Conlyflags: compilerAttrs.conlyFlags,
+ Asflags: asFlags,
+
+ Export_includes: exportedIncludes.Includes,
+ Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+ Export_system_includes: exportedIncludes.SystemIncludes,
+ 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,
+ C_std: compilerAttrs.cStd,
+
+ Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
Strip: stripAttributes{
Keep_symbols: linkerAttrs.stripKeepSymbols,
@@ -334,18 +382,42 @@
All: linkerAttrs.stripAll,
None: linkerAttrs.stripNone,
},
+ Features: linkerAttrs.features,
- Shared: sharedAttrs,
-
- Static: staticAttrs,
+ Stubs_symbol_file: compilerAttrs.stubsSymbolFile,
+ Stubs_versions: compilerAttrs.stubsVersions,
}
- props := bazel.BazelTargetModuleProperties{
- Rule_class: "cc_library",
- Bzl_load_location: "//build/bazel/rules:full_cc_library.bzl",
+ for axis, configToProps := range m.GetArchVariantProperties(ctx, &LibraryProperties{}) {
+ for config, props := range configToProps {
+ if props, ok := props.(*LibraryProperties); ok {
+ if props.Inject_bssl_hash != nil {
+ // This is an edge case applies only to libcrypto
+ if m.Name() == "libcrypto" {
+ sharedTargetAttrs.Inject_bssl_hash.SetSelectValue(axis, config, props.Inject_bssl_hash)
+ } else {
+ ctx.PropertyErrorf("inject_bssl_hash", "only applies to libcrypto")
+ }
+ }
+ }
+ }
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+ staticProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_library_static",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_library_static.bzl",
+ }
+ sharedProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_library_shared",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl",
+ }
+
+ ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
+ android.CommonAttributes{Name: m.Name() + "_bp2build_cc_library_static"},
+ staticTargetAttrs, staticAttrs.Enabled)
+ ctx.CreateBazelTargetModuleWithRestrictions(sharedProps,
+ android.CommonAttributes{Name: m.Name()},
+ sharedTargetAttrs, sharedAttrs.Enabled)
}
// cc_library creates both static and/or shared libraries for a device and/or
@@ -360,6 +432,7 @@
staticLibrarySdkMemberType,
staticAndSharedLibrarySdkMemberType,
}
+ module.bazelable = true
module.bazelHandler = &ccLibraryBazelHandler{module: module}
return module.Init()
}
@@ -369,6 +442,7 @@
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyStatic()
module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
+ module.bazelable = true
module.bazelHandler = &ccLibraryBazelHandler{module: module}
return module.Init()
}
@@ -378,6 +452,7 @@
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyShared()
module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
+ module.bazelable = true
module.bazelHandler = &ccLibraryBazelHandler{module: module}
return module.Init()
}
@@ -388,6 +463,8 @@
module, library := NewLibrary(android.HostSupported)
library.BuildOnlyStatic()
module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
+ module.bazelable = true
+ module.bazelHandler = &ccLibraryBazelHandler{module: module}
return module.Init()
}
@@ -396,6 +473,8 @@
module, library := NewLibrary(android.HostSupported)
library.BuildOnlyShared()
module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
+ module.bazelable = true
+ module.bazelHandler = &ccLibraryBazelHandler{module: module}
return module.Init()
}
@@ -509,7 +588,8 @@
stripper Stripper
// For whole_static_libs
- objects Objects
+ objects Objects
+ wholeStaticLibsFromPrebuilts android.Paths
// Uses the module's name if empty, but can be overridden. Does not include
// shlib suffix.
@@ -630,7 +710,7 @@
func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+ ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil {
ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
return false
@@ -695,9 +775,10 @@
if dir == "external/eigen" {
// Only these two directories contains exported headers.
for _, subdir := range []string{"Eigen", "unsupported/Eigen"} {
- glob, err := ctx.GlobWithDeps("external/eigen/"+subdir+"/**/*", nil)
+ globDir := "external/eigen/" + subdir + "/**/*"
+ glob, err := ctx.GlobWithDeps(globDir, nil)
if err != nil {
- ctx.ModuleErrorf("glob failed: %#v", err)
+ ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
return nil
}
for _, header := range glob {
@@ -713,9 +794,10 @@
}
continue
}
- glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+ globDir := dir + "/**/*"
+ glob, err := ctx.GlobWithDeps(globDir, nil)
if err != nil {
- ctx.ModuleErrorf("glob failed: %#v", err)
+ ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
return nil
}
isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
@@ -999,11 +1081,13 @@
srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs,
android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs),
+ android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_timeout_srcs),
library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
} else if library.shared() {
srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs)
objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, srcs,
android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs),
+ android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_timeout_srcs),
library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
}
@@ -1037,6 +1121,8 @@
availableFor(string) bool
getAPIListCoverageXMLPath() android.ModuleOutPath
+
+ installable() *bool
}
type versionedInterface interface {
@@ -1262,6 +1348,7 @@
library.objects = deps.WholeStaticLibObjs.Copy()
library.objects = library.objects.Append(objs)
+ library.wholeStaticLibsFromPrebuilts = android.CopyOfPaths(deps.WholeStaticLibsFromPrebuilts)
fileName := ctx.ModuleName() + staticLibraryExtension
outputFile := android.PathForModuleOut(ctx, fileName)
@@ -1279,7 +1366,7 @@
}
}
- transformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, objs.tidyFiles)
+ transformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, nil, objs.tidyDepFiles)
library.coverageOutputFile = transformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
@@ -1287,9 +1374,10 @@
if library.static() {
ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
- StaticLibrary: outputFile,
- ReuseObjects: library.reuseObjects,
- Objects: library.objects,
+ StaticLibrary: outputFile,
+ ReuseObjects: library.reuseObjects,
+ Objects: library.objects,
+ WholeStaticLibsFromPrebuilts: library.wholeStaticLibsFromPrebuilts,
TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
Direct(outputFile).
@@ -1305,11 +1393,21 @@
return outputFile
}
+func ndkSharedLibDeps(ctx ModuleContext) android.Paths {
+ if ctx.Module().(*Module).IsSdkVariant() {
+ // The NDK sysroot timestamp file depends on all the NDK
+ // sysroot header and shared library files.
+ return android.Paths{getNdkBaseTimestampFile(ctx)}
+ }
+ return nil
+}
+
func (library *libraryDecorator) linkShared(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
var linkerDeps android.Paths
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
+ linkerDeps = append(linkerDeps, ndkSharedLibDeps(ctx)...)
unexportedSymbols := ctx.ExpandOptionalSource(library.Properties.Unexported_symbols_list, "unexported_symbols_list")
forceNotWeakSymbols := ctx.ExpandOptionalSource(library.Properties.Force_symbols_not_weak_list, "force_symbols_not_weak_list")
@@ -1358,11 +1456,17 @@
builderFlags := flagsToBuilderFlags(flags)
+ if ctx.Darwin() && deps.DarwinSecondArchOutput.Valid() {
+ fatOutputFile := outputFile
+ outputFile = android.PathForModuleOut(ctx, "pre-fat", fileName)
+ transformDarwinUniversalBinary(ctx, fatOutputFile, outputFile, deps.DarwinSecondArchOutput.Path())
+ }
+
// Optimize out relinking against shared libraries whose interface hasn't changed by
// depending on a table of contents file instead of the library itself.
tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.ShlibSuffix()[1:]+".toc")
library.tocFile = android.OptionalPathForPath(tocFile)
- transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
+ TransformSharedObjectToToc(ctx, outputFile, tocFile)
stripFlags := flagsToStripFlags(flags)
needsStrip := library.stripper.NeedsStrip(ctx)
@@ -1408,10 +1512,9 @@
linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...)
linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
- linkerDeps = append(linkerDeps, objs.tidyFiles...)
transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
- linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, nil)
+ linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -1962,6 +2065,15 @@
return android.CheckAvailableForApex(what, list)
}
+func (library *libraryDecorator) installable() *bool {
+ if library.static() {
+ return library.StaticProperties.Static.Installable
+ } else if library.shared() {
+ return library.SharedProperties.Shared.Installable
+ }
+ return nil
+}
+
func (library *libraryDecorator) makeUninstallable(mod *Module) {
if library.static() && library.buildStatic() && !library.buildStubs() {
// If we're asked to make a static library uninstallable we don't do
@@ -2326,31 +2438,12 @@
return outputFile
}
-func ccSharedOrStaticBp2BuildMutator(ctx android.TopDownMutatorContext, modType string) {
- module, ok := ctx.Module().(*Module)
- if !ok {
- // Not a cc module
- return
- }
- if !module.ConvertWithBp2build(ctx) {
- return
- }
- if ctx.ModuleType() != modType {
- return
- }
+func sharedOrStaticLibraryBp2Build(ctx android.TopDownMutatorContext, module *Module, isStatic bool) {
+ baseAttributes := bp2BuildParseBaseProps(ctx, module)
+ compilerAttrs := baseAttributes.compilerAttributes
+ linkerAttrs := baseAttributes.linkerAttributes
- ccSharedOrStaticBp2BuildMutatorInternal(ctx, module, modType)
-}
-
-func ccSharedOrStaticBp2BuildMutatorInternal(ctx android.TopDownMutatorContext, module *Module, modType string) {
- if modType != "cc_library_static" && modType != "cc_library_shared" {
- panic("ccSharedOrStaticBp2BuildMutatorInternal only supports cc_library_{static,shared}")
- }
- isStatic := modType == "cc_library_static"
-
- compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
- linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
- exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
+ 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
@@ -2379,51 +2472,69 @@
Srcs_c: compilerAttrs.cSrcs,
Srcs_as: compilerAttrs.asSrcs,
Copts: compilerAttrs.copts,
+ Hdrs: compilerAttrs.hdrs,
- Deps: linkerAttrs.deps,
- Implementation_deps: linkerAttrs.implementationDeps,
- Dynamic_deps: linkerAttrs.dynamicDeps,
- Implementation_dynamic_deps: linkerAttrs.implementationDynamicDeps,
- Whole_archive_deps: linkerAttrs.wholeArchiveDeps,
- System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+ Deps: linkerAttrs.deps,
+ Implementation_deps: linkerAttrs.implementationDeps,
+ Dynamic_deps: linkerAttrs.dynamicDeps,
+ Implementation_dynamic_deps: linkerAttrs.implementationDynamicDeps,
+ Whole_archive_deps: linkerAttrs.wholeArchiveDeps,
+ Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
+ System_dynamic_deps: linkerAttrs.systemDynamicDeps,
}
var attrs interface{}
if isStatic {
+ commonAttrs.Deps.Add(baseAttributes.protoDependency)
attrs = &bazelCcLibraryStaticAttributes{
staticOrSharedAttributes: commonAttrs,
- Linkopts: linkerAttrs.linkopts,
- Use_libcrt: linkerAttrs.useLibcrt,
- Rtti: compilerAttrs.rtti,
- Stl: compilerAttrs.stl,
- Export_includes: exportedIncludes.Includes,
- Export_system_includes: exportedIncludes.SystemIncludes,
- Local_includes: compilerAttrs.localIncludes,
- Absolute_includes: compilerAttrs.absoluteIncludes,
+ Use_libcrt: linkerAttrs.useLibcrt,
+ Use_version_lib: linkerAttrs.useVersionLib,
+
+ Rtti: compilerAttrs.rtti,
+ Stl: compilerAttrs.stl,
+ Cpp_std: compilerAttrs.cppStd,
+ C_std: compilerAttrs.cStd,
+
+ Export_includes: exportedIncludes.Includes,
+ Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+ Export_system_includes: exportedIncludes.SystemIncludes,
+ Local_includes: compilerAttrs.localIncludes,
+ Absolute_includes: compilerAttrs.absoluteIncludes,
Cppflags: compilerAttrs.cppFlags,
Conlyflags: compilerAttrs.conlyFlags,
Asflags: asFlags,
+
+ Features: linkerAttrs.features,
}
} else {
+ commonAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
+
attrs = &bazelCcLibrarySharedAttributes{
staticOrSharedAttributes: commonAttrs,
Cppflags: compilerAttrs.cppFlags,
Conlyflags: compilerAttrs.conlyFlags,
Asflags: asFlags,
- Linkopts: linkerAttrs.linkopts,
- Use_libcrt: linkerAttrs.useLibcrt,
- Rtti: compilerAttrs.rtti,
- Stl: compilerAttrs.stl,
+ Linkopts: linkerAttrs.linkopts,
+ Link_crt: linkerAttrs.linkCrt,
+ Use_libcrt: linkerAttrs.useLibcrt,
+ Use_version_lib: linkerAttrs.useVersionLib,
- Export_includes: exportedIncludes.Includes,
- Export_system_includes: exportedIncludes.SystemIncludes,
- Local_includes: compilerAttrs.localIncludes,
- Absolute_includes: compilerAttrs.absoluteIncludes,
- Version_script: linkerAttrs.versionScript,
+ Rtti: compilerAttrs.rtti,
+ Stl: compilerAttrs.stl,
+ Cpp_std: compilerAttrs.cppStd,
+ C_std: compilerAttrs.cStd,
+
+ Export_includes: exportedIncludes.Includes,
+ Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+ Export_system_includes: exportedIncludes.SystemIncludes,
+ Local_includes: compilerAttrs.localIncludes,
+ Absolute_includes: compilerAttrs.absoluteIncludes,
+ Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
Strip: stripAttributes{
Keep_symbols: linkerAttrs.stripKeepSymbols,
@@ -2432,12 +2543,23 @@
All: linkerAttrs.stripAll,
None: linkerAttrs.stripNone,
},
+
+ Features: linkerAttrs.features,
+
+ Stubs_symbol_file: compilerAttrs.stubsSymbolFile,
+ Stubs_versions: compilerAttrs.stubsVersions,
}
}
+ var modType string
+ if isStatic {
+ modType = "cc_library_static"
+ } else {
+ modType = "cc_library_shared"
+ }
props := bazel.BazelTargetModuleProperties{
Rule_class: modType,
- Bzl_load_location: fmt.Sprintf("//build/bazel/rules:%s.bzl", modType),
+ Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType),
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
@@ -2447,49 +2569,60 @@
type bazelCcLibraryStaticAttributes struct {
staticOrSharedAttributes
- Linkopts bazel.StringListAttribute
- Use_libcrt bazel.BoolAttribute
- Rtti bazel.BoolAttribute
- Stl *string
+ Use_libcrt bazel.BoolAttribute
+ Use_version_lib bazel.BoolAttribute
- Export_includes bazel.StringListAttribute
- Export_system_includes bazel.StringListAttribute
- Local_includes bazel.StringListAttribute
- Absolute_includes bazel.StringListAttribute
- Hdrs bazel.LabelListAttribute
+ Rtti bazel.BoolAttribute
+ Stl *string
+ Cpp_std *string
+ C_std *string
+
+ Export_includes bazel.StringListAttribute
+ Export_absolute_includes bazel.StringListAttribute
+ Export_system_includes bazel.StringListAttribute
+ Local_includes bazel.StringListAttribute
+ Absolute_includes bazel.StringListAttribute
+ Hdrs bazel.LabelListAttribute
Cppflags bazel.StringListAttribute
Conlyflags bazel.StringListAttribute
Asflags bazel.StringListAttribute
-}
-func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
- ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_static")
+ Features bazel.StringListAttribute
}
// TODO(b/199902614): Can this be factored to share with the other Attributes?
type bazelCcLibrarySharedAttributes struct {
staticOrSharedAttributes
- Linkopts bazel.StringListAttribute
- Use_libcrt bazel.BoolAttribute
- Rtti bazel.BoolAttribute
- Stl *string
+ Linkopts bazel.StringListAttribute
+ Link_crt bazel.BoolAttribute // Only for linking shared library (and cc_binary)
- Export_includes bazel.StringListAttribute
- Export_system_includes bazel.StringListAttribute
- Local_includes bazel.StringListAttribute
- Absolute_includes bazel.StringListAttribute
- Hdrs bazel.LabelListAttribute
+ Use_libcrt bazel.BoolAttribute
+ Use_version_lib bazel.BoolAttribute
- Strip stripAttributes
- Version_script bazel.LabelAttribute
+ Rtti bazel.BoolAttribute
+ Stl *string
+ Cpp_std *string
+ C_std *string
+
+ Export_includes bazel.StringListAttribute
+ Export_absolute_includes bazel.StringListAttribute
+ Export_system_includes bazel.StringListAttribute
+ Local_includes bazel.StringListAttribute
+ Absolute_includes bazel.StringListAttribute
+ Hdrs bazel.LabelListAttribute
+
+ Strip stripAttributes
+ Additional_linker_inputs bazel.LabelListAttribute
Cppflags bazel.StringListAttribute
Conlyflags bazel.StringListAttribute
Asflags bazel.StringListAttribute
-}
-func CcLibrarySharedBp2Build(ctx android.TopDownMutatorContext) {
- ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_shared")
+ Features bazel.StringListAttribute
+
+ Stubs_symbol_file *string
+ Stubs_versions bazel.StringListAttribute
+ Inject_bssl_hash bazel.BoolAttribute
}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 51c1eb8..5d38fba 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -25,7 +25,6 @@
// Register sdk member types.
android.RegisterSdkMemberType(headersLibrarySdkMemberType)
- android.RegisterBp2BuildMutator("cc_library_headers", CcLibraryHeadersBp2Build)
}
var headersLibrarySdkMemberType = &librarySdkMemberType{
@@ -57,7 +56,7 @@
func (h *libraryHeaderBazelHander) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+ ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil {
ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
return false
@@ -96,55 +95,48 @@
module, library := NewLibrary(android.HostAndDeviceSupported)
library.HeaderOnly()
module.sdkMemberTypes = []android.SdkMemberType{headersLibrarySdkMemberType}
+ module.bazelable = true
module.bazelHandler = &libraryHeaderBazelHander{module: module, library: library}
return module.Init()
}
// cc_prebuilt_library_headers is a prebuilt version of cc_library_headers
func prebuiltLibraryHeaderFactory() android.Module {
- module, library := NewPrebuiltLibrary(android.HostAndDeviceSupported)
+ module, library := NewPrebuiltLibrary(android.HostAndDeviceSupported, "")
library.HeaderOnly()
+ module.bazelable = true
+ module.bazelHandler = &ccLibraryBazelHandler{module: module}
return module.Init()
}
type bazelCcLibraryHeadersAttributes struct {
- Hdrs bazel.LabelListAttribute
- Export_includes bazel.StringListAttribute
- Export_system_includes bazel.StringListAttribute
- Deps bazel.LabelListAttribute
- Implementation_deps bazel.LabelListAttribute
- System_dynamic_deps bazel.LabelListAttribute
+ Hdrs bazel.LabelListAttribute
+ Export_includes bazel.StringListAttribute
+ Export_absolute_includes bazel.StringListAttribute
+ Export_system_includes bazel.StringListAttribute
+ Deps bazel.LabelListAttribute
+ Implementation_deps bazel.LabelListAttribute
+ System_dynamic_deps bazel.LabelListAttribute
}
-func CcLibraryHeadersBp2Build(ctx android.TopDownMutatorContext) {
- module, ok := ctx.Module().(*Module)
- if !ok {
- // Not a cc module
- return
- }
-
- if !module.ConvertWithBp2build(ctx) {
- return
- }
-
- if ctx.ModuleType() != "cc_library_headers" {
- return
- }
-
- exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
- linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
+func libraryHeadersBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+ baseAttributes := bp2BuildParseBaseProps(ctx, module)
+ exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, baseAttributes.includes)
+ linkerAttrs := baseAttributes.linkerAttributes
attrs := &bazelCcLibraryHeadersAttributes{
- Export_includes: exportedIncludes.Includes,
- Export_system_includes: exportedIncludes.SystemIncludes,
- Implementation_deps: linkerAttrs.implementationDeps,
- Deps: linkerAttrs.deps,
- System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+ 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,
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "cc_library_headers",
- Bzl_load_location: "//build/bazel/rules:cc_library_headers.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
diff --git a/cc/library_headers_test.go b/cc/library_headers_test.go
index 564ef61..3e448ba 100644
--- a/cc/library_headers_test.go
+++ b/cc/library_headers_test.go
@@ -15,48 +15,85 @@
package cc
import (
- "strings"
+ "fmt"
"testing"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint"
)
func TestLibraryHeaders(t *testing.T) {
- ctx := testCc(t, `
- cc_library_headers {
- name: "headers",
- export_include_dirs: ["my_include"],
- }
- cc_library_static {
- name: "lib",
- srcs: ["foo.c"],
- header_libs: ["headers"],
- }
- `)
+ bp := `
+ %s {
+ name: "headers",
+ export_include_dirs: ["my_include"],
+ }
+ cc_library_static {
+ name: "lib",
+ srcs: ["foo.c"],
+ header_libs: ["headers"],
+ }
+ `
- // test if header search paths are correctly added
- cc := ctx.ModuleForTests("lib", "android_arm64_armv8-a_static").Rule("cc")
- cflags := cc.Args["cFlags"]
- if !strings.Contains(cflags, " -Imy_include ") {
- t.Errorf("cflags for libsystem must contain -Imy_include, but was %#v.", cflags)
+ for _, headerModule := range []string{"cc_library_headers", "cc_prebuilt_library_headers"} {
+ t.Run(headerModule, func(t *testing.T) {
+ ctx := testCc(t, fmt.Sprintf(bp, headerModule))
+
+ // test if header search paths are correctly added
+ cc := ctx.ModuleForTests("lib", "android_arm64_armv8-a_static").Rule("cc")
+ android.AssertStringDoesContain(t, "cFlags for lib module", cc.Args["cFlags"], " -Imy_include ")
+
+ // Test that there's a valid AndroidMk entry.
+ headers := ctx.ModuleForTests("headers", "android_arm64_armv8-a").Module()
+ e := android.AndroidMkEntriesForTest(t, ctx, headers)[0]
+
+ // This duplicates the tests done in AndroidMkEntries.write. It would be
+ // better to test its output, but there are no test functions that capture that.
+ android.AssertBoolEquals(t, "AndroidMkEntries.Disabled", false, e.Disabled)
+ android.AssertBoolEquals(t, "AndroidMkEntries.OutputFile.Valid()", true, e.OutputFile.Valid())
+
+ android.AssertStringListContains(t, "LOCAL_EXPORT_CFLAGS for headers module", e.EntryMap["LOCAL_EXPORT_CFLAGS"], "-Imy_include")
+ })
}
}
-func TestPrebuiltLibraryHeaders(t *testing.T) {
- ctx := testCc(t, `
- cc_prebuilt_library_headers {
- name: "headers",
- export_include_dirs: ["my_include"],
- }
- cc_library_static {
- name: "lib",
- srcs: ["foo.c"],
- header_libs: ["headers"],
- }
- `)
+func TestPrebuiltLibraryHeadersPreferred(t *testing.T) {
+ bp := `
+ cc_library_headers {
+ name: "headers",
+ export_include_dirs: ["my_include"],
+ }
+ cc_prebuilt_library_headers {
+ name: "headers",
+ prefer: %t,
+ export_include_dirs: ["my_include"],
+ }
+ cc_library_static {
+ name: "lib",
+ srcs: ["foo.c"],
+ header_libs: ["headers"],
+ }
+ `
- // test if header search paths are correctly added
- cc := ctx.ModuleForTests("lib", "android_arm64_armv8-a_static").Rule("cc")
- cflags := cc.Args["cFlags"]
- if !strings.Contains(cflags, " -Imy_include ") {
- t.Errorf("cflags for libsystem must contain -Imy_include, but was %#v.", cflags)
+ for _, prebuiltPreferred := range []bool{false, true} {
+ t.Run(fmt.Sprintf("prebuilt prefer %t", prebuiltPreferred), func(t *testing.T) {
+ ctx := testCc(t, fmt.Sprintf(bp, prebuiltPreferred))
+ lib := ctx.ModuleForTests("lib", "android_arm64_armv8-a_static")
+ sourceDep := ctx.ModuleForTests("headers", "android_arm64_armv8-a")
+ prebuiltDep := ctx.ModuleForTests("prebuilt_headers", "android_arm64_armv8-a")
+ hasSourceDep := false
+ hasPrebuiltDep := false
+ ctx.VisitDirectDeps(lib.Module(), func(dep blueprint.Module) {
+ if dep == sourceDep.Module() {
+ hasSourceDep = true
+ }
+ if dep == prebuiltDep.Module() {
+ hasPrebuiltDep = true
+ }
+ })
+ android.AssertBoolEquals(t, "depends on source headers", !prebuiltPreferred, hasSourceDep)
+ android.AssertBoolEquals(t, "depends on prebuilt headers", prebuiltPreferred, hasPrebuiltDep)
+ })
}
}
diff --git a/cc/library_test.go b/cc/library_test.go
index 7ddfaa7..6d5eda2 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -257,9 +257,16 @@
CcObjectFiles: []string{"foo.o"},
Includes: []string{"include"},
SystemIncludes: []string{"system_include"},
- RootStaticArchives: []string{"foo.a"},
+ Headers: []string{"foo.h"},
RootDynamicLibraries: []string{"foo.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"},
+ },
},
}
ctx := testCcWithConfig(t, config)
@@ -273,18 +280,25 @@
expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.a"}
android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+ 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_object outputfiles %s", err)
+ 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())
- 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)
+ 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 TestLibraryVersionScript(t *testing.T) {
@@ -365,3 +379,77 @@
gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
}
+
+func TestWholeStaticLibPrebuilts(t *testing.T) {
+ result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+ cc_prebuilt_library_static {
+ name: "libprebuilt",
+ srcs: ["foo.a"],
+ }
+
+ cc_library_static {
+ name: "libdirect",
+ whole_static_libs: ["libprebuilt"],
+ }
+
+ cc_library_static {
+ name: "libtransitive",
+ whole_static_libs: ["libdirect"],
+ }
+
+ cc_library_static {
+ name: "libdirect_with_srcs",
+ srcs: ["bar.c"],
+ whole_static_libs: ["libprebuilt"],
+ }
+
+ cc_library_static {
+ name: "libtransitive_with_srcs",
+ srcs: ["baz.c"],
+ whole_static_libs: ["libdirect_with_srcs"],
+ }
+ `)
+
+ libdirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static").Rule("arWithLibs")
+ libtransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+ libdirectWithSrcs := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+ libtransitiveWithSrcs := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+ barObj := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+ bazObj := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libdirect.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libdirect.Args["arLibs"], "foo.a")
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libtransitive.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libtransitive.Args["arLibs"], "foo.a")
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libdirectWithSrcs.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libdirectWithSrcs.Args["arLibs"], "foo.a")
+ android.AssertStringListContains(t, "missing dependency on bar.o",
+ libdirectWithSrcs.Inputs.Strings(), barObj.Output.String())
+ android.AssertStringDoesContain(t, "missing flag for bar.o",
+ libdirectWithSrcs.Args["arObjs"], barObj.Output.String())
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libtransitiveWithSrcs.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libtransitiveWithSrcs.Args["arLibs"], "foo.a")
+
+ android.AssertStringListContains(t, "missing dependency on bar.o",
+ libtransitiveWithSrcs.Inputs.Strings(), barObj.Output.String())
+ android.AssertStringDoesContain(t, "missing flag for bar.o",
+ libtransitiveWithSrcs.Args["arObjs"], barObj.Output.String())
+
+ android.AssertStringListContains(t, "missing dependency on baz.o",
+ libtransitiveWithSrcs.Inputs.Strings(), bazObj.Output.String())
+ android.AssertStringDoesContain(t, "missing flag for baz.o",
+ libtransitiveWithSrcs.Args["arObjs"], bazObj.Output.String())
+}
diff --git a/cc/linkable.go b/cc/linkable.go
index b510508..d4b4770 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -110,6 +110,7 @@
BaseModuleName() string
OutputFile() android.OptionalPath
+ UnstrippedOutputFile() android.Path
CoverageFiles() android.Paths
NonCcVariants() bool
@@ -351,6 +352,11 @@
Objects Objects
ReuseObjects Objects
+ // A static library may contain prebuilt static libraries included with whole_static_libs
+ // that won't appear in Objects. They are transitively available in
+ // WholeStaticLibsFromPrebuilts.
+ WholeStaticLibsFromPrebuilts android.Paths
+
// This isn't the actual transitive DepSet, shared library dependencies have been
// converted into static library analogues. It is only used to order the static
// library dependencies that were specified for the current module.
@@ -383,9 +389,13 @@
includes := android.PathsForBazelOut(ctx, ccInfo.Includes)
systemIncludes := android.PathsForBazelOut(ctx, ccInfo.SystemIncludes)
+ headers := android.PathsForBazelOut(ctx, ccInfo.Headers)
return FlagExporterInfo{
IncludeDirs: android.FirstUniquePaths(includes),
SystemIncludeDirs: android.FirstUniquePaths(systemIncludes),
+ GeneratedHeaders: headers,
+ // necessary to ensure generated headers are considered implicit deps of dependent actions
+ Deps: headers,
}
}
diff --git a/cc/linker.go b/cc/linker.go
index 0d612b5..bea65d4 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -27,6 +27,10 @@
// This file contains the basic functionality for linking against static libraries and shared
// libraries. Final linking into libraries or executables is handled in library.go, binary.go, etc.
+const (
+ packRelocationsDefault = true
+)
+
type BaseLinkerProperties struct {
// list of modules whose object files should be linked into this module
// in their entirety. For static library modules, all of the .o files from the intermediate
@@ -238,6 +242,19 @@
return &ret
}
+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)
}
@@ -410,7 +427,7 @@
deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...)
- if ctx.Windows() {
+ if ctx.Windows() && ctx.ModuleName() != "libwinpthread" {
deps.LateStaticLibs = append(deps.LateStaticLibs, "libwinpthread")
}
@@ -458,7 +475,7 @@
if linker.useClangLld(ctx) {
flags.Global.LdFlags = append(flags.Global.LdFlags, fmt.Sprintf("${config.%sGlobalLldflags}", hod))
- if !BoolDefault(linker.Properties.Pack_relocations, true) {
+ if !BoolDefault(linker.Properties.Pack_relocations, packRelocationsDefault) {
flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=none")
} else if ctx.Device() {
// SHT_RELR relocations are only supported at API level >= 30.
diff --git a/cc/lto.go b/cc/lto.go
index d9a0118..2c274bd 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -49,8 +49,9 @@
// 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"`
+ FullDep bool `blueprint:"mutated"`
+ ThinDep bool `blueprint:"mutated"`
+ NoLtoDep bool `blueprint:"mutated"`
// Use clang lld instead of gnu ld.
Use_clang_lld *bool
@@ -70,15 +71,6 @@
func (lto *lto) begin(ctx BaseModuleContext) {
if ctx.Config().IsEnvTrue("DISABLE_LTO") {
lto.Properties.Lto.Never = proptools.BoolPtr(true)
- } else if ctx.Config().IsEnvTrue("GLOBAL_THINLTO") {
- staticLib := ctx.static() && !ctx.staticBinary()
- hostBin := ctx.Host()
- vndk := ctx.isVndk() // b/169217596
- if !staticLib && !hostBin && !vndk {
- if !lto.Never() && !lto.FullLTO() {
- lto.Properties.Lto.Thin = proptools.BoolPtr(true)
- }
- }
}
}
@@ -96,22 +88,27 @@
return flags
}
- if lto.LTO() {
- var ltoFlag string
+ if lto.LTO(ctx) {
+ var ltoCFlag string
+ var ltoLdFlag string
if lto.ThinLTO() {
- ltoFlag = "-flto=thin -fsplit-lto-unit"
+ ltoCFlag = "-flto=thin -fsplit-lto-unit"
+ } else if lto.FullLTO() {
+ ltoCFlag = "-flto"
} else {
- ltoFlag = "-flto"
+ ltoCFlag = "-flto=thin -fsplit-lto-unit"
+ ltoLdFlag = "-Wl,--lto-O0"
}
- flags.Local.CFlags = append(flags.Local.CFlags, ltoFlag)
- flags.Local.LdFlags = append(flags.Local.LdFlags, ltoFlag)
+ flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlag)
+ flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlag)
+ flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlag)
if Bool(lto.Properties.Whole_program_vtables) {
flags.Local.CFlags = append(flags.Local.CFlags, "-fwhole-program-vtables")
}
- if lto.ThinLTO() && ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && lto.useClangLld(ctx) {
+ if (lto.DefaultThinLTO(ctx) || lto.ThinLTO()) && ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && lto.useClangLld(ctx) {
// Set appropriate ThinLTO cache policy
cacheDirFormat := "-Wl,--thinlto-cache-dir="
cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
@@ -126,7 +123,7 @@
// If the module does not have a profile, be conservative and limit cross TU inline
// limit to 5 LLVM IR instructions, to balance binary size increase and performance.
- if !ctx.isPgoCompile() {
+ if !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
flags.Local.LdFlags = append(flags.Local.LdFlags,
"-Wl,-plugin-opt,-import-instr-limit=5")
}
@@ -134,33 +131,40 @@
return flags
}
-// Can be called with a null receiver
-func (lto *lto) LTO() bool {
- if lto == nil || lto.Never() {
- return false
- }
+func (lto *lto) LTO(ctx BaseModuleContext) bool {
+ return lto.ThinLTO() || lto.FullLTO() || lto.DefaultThinLTO(ctx)
+}
- return lto.FullLTO() || lto.ThinLTO()
+func (lto *lto) DefaultThinLTO(ctx BaseModuleContext) bool {
+ host := ctx.Host()
+ vndk := ctx.isVndk() // b/169217596
+ return GlobalThinLTO(ctx) && !lto.Never() && !host && !vndk
}
func (lto *lto) FullLTO() bool {
- return Bool(lto.Properties.Lto.Full)
+ return lto != nil && Bool(lto.Properties.Lto.Full)
}
func (lto *lto) ThinLTO() bool {
- return Bool(lto.Properties.Lto.Thin)
+ return lto != nil && Bool(lto.Properties.Lto.Thin)
}
-// Is lto.never explicitly set to true?
func (lto *lto) Never() bool {
- return Bool(lto.Properties.Lto.Never)
+ return lto != nil && Bool(lto.Properties.Lto.Never)
+}
+
+func GlobalThinLTO(ctx android.BaseModuleContext) bool {
+ return ctx.Config().IsEnvTrue("GLOBAL_THINLTO")
}
// Propagate lto requirements down from binaries
func ltoDepsMutator(mctx android.TopDownMutatorContext) {
- if m, ok := mctx.Module().(*Module); ok && m.lto.LTO() {
+ globalThinLTO := GlobalThinLTO(mctx)
+
+ if m, ok := mctx.Module().(*Module); ok {
full := m.lto.FullLTO()
thin := m.lto.ThinLTO()
+ never := m.lto.Never()
if full && thin {
mctx.PropertyErrorf("LTO", "FullLTO and ThinLTO are mutually exclusive")
}
@@ -180,14 +184,16 @@
}
}
- if dep, ok := dep.(*Module); ok && dep.lto != nil &&
- !dep.lto.Never() {
+ if dep, ok := dep.(*Module); ok {
if full && !dep.lto.FullLTO() {
dep.lto.Properties.FullDep = true
}
- if thin && !dep.lto.ThinLTO() {
+ if !globalThinLTO && thin && !dep.lto.ThinLTO() {
dep.lto.Properties.ThinDep = true
}
+ if globalThinLTO && never && !dep.lto.Never() {
+ dep.lto.Properties.NoLtoDep = true
+ }
}
// Recursively walk static dependencies
@@ -198,6 +204,8 @@
// Create lto variants for modules that need them
func ltoMutator(mctx android.BottomUpMutatorContext) {
+ globalThinLTO := GlobalThinLTO(mctx)
+
if m, ok := mctx.Module().(*Module); ok && m.lto != nil {
// Create variations for LTO types required as static
// dependencies
@@ -205,18 +213,25 @@
if m.lto.Properties.FullDep && !m.lto.FullLTO() {
variationNames = append(variationNames, "lto-full")
}
- if m.lto.Properties.ThinDep && !m.lto.ThinLTO() {
+ if !globalThinLTO && m.lto.Properties.ThinDep && !m.lto.ThinLTO() {
variationNames = append(variationNames, "lto-thin")
}
+ if globalThinLTO && m.lto.Properties.NoLtoDep && !m.lto.Never() {
+ variationNames = append(variationNames, "lto-none")
+ }
// Use correct dependencies if LTO property is explicitly set
// (mutually exclusive)
if m.lto.FullLTO() {
mctx.SetDependencyVariation("lto-full")
}
- if m.lto.ThinLTO() {
+ if !globalThinLTO && m.lto.ThinLTO() {
mctx.SetDependencyVariation("lto-thin")
}
+ // Never must be the last, it overrides Thin or Full.
+ if globalThinLTO && m.lto.Never() {
+ mctx.SetDependencyVariation("lto-none")
+ }
if len(variationNames) > 1 {
modules := mctx.CreateVariations(variationNames...)
@@ -232,16 +247,18 @@
// LTO properties for dependencies
if name == "lto-full" {
variation.lto.Properties.Lto.Full = proptools.BoolPtr(true)
- variation.lto.Properties.Lto.Thin = proptools.BoolPtr(false)
}
if name == "lto-thin" {
- variation.lto.Properties.Lto.Full = proptools.BoolPtr(false)
variation.lto.Properties.Lto.Thin = proptools.BoolPtr(true)
}
+ if name == "lto-none" {
+ variation.lto.Properties.Lto.Never = proptools.BoolPtr(true)
+ }
variation.Properties.PreventInstall = true
variation.Properties.HideFromMake = true
variation.lto.Properties.FullDep = false
variation.lto.Properties.ThinDep = false
+ variation.lto.Properties.NoLtoDep = false
}
}
}
diff --git a/cc/makevars.go b/cc/makevars.go
index 8d7a163..6752f8c 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -95,6 +95,7 @@
ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ExternalCflags}")
ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
+ ctx.Strict("GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideExternalGlobalCflags}")
ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
ctx.Strict("RECOVERY_SNAPSHOT_VERSION", ctx.DeviceConfig().RecoverySnapshotVersion())
@@ -226,7 +227,6 @@
}
clangPrefix := secondPrefix + "CLANG_" + typePrefix
- clangExtras := "-B" + config.ToolPath(toolchain)
ctx.Strict(clangPrefix+"TRIPLE", toolchain.ClangTriple())
ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
@@ -234,7 +234,6 @@
"${config.CommonGlobalCflags}",
fmt.Sprintf("${config.%sGlobalCflags}", hod),
toolchain.ToolchainCflags(),
- clangExtras,
productExtraCflags,
}, " "))
ctx.Strict(clangPrefix+"GLOBAL_CPPFLAGS", strings.Join([]string{
@@ -247,25 +246,52 @@
toolchain.Ldflags(),
toolchain.ToolchainLdflags(),
productExtraLdflags,
- clangExtras,
}, " "))
ctx.Strict(clangPrefix+"GLOBAL_LLDFLAGS", strings.Join([]string{
fmt.Sprintf("${config.%sGlobalLldflags}", hod),
toolchain.Lldflags(),
toolchain.ToolchainLdflags(),
productExtraLdflags,
- clangExtras,
}, " "))
if target.Os.Class == android.Device {
- ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_STATIC_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerStaticLibrary(toolchain), ".a"))
- ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
- ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"SCUDO_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"SCUDO_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoMinimalRuntimeLibrary(toolchain), ".so"))
+ sanitizerVariables := map[string]string{
+ "ADDRESS_SANITIZER_RUNTIME_LIBRARY": config.AddressSanitizerRuntimeLibrary(toolchain),
+ "HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(toolchain),
+ "HWADDRESS_SANITIZER_STATIC_LIBRARY": config.HWAddressSanitizerStaticLibrary(toolchain),
+ "UBSAN_RUNTIME_LIBRARY": config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain),
+ "UBSAN_MINIMAL_RUNTIME_LIBRARY": config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain),
+ "TSAN_RUNTIME_LIBRARY": config.ThreadSanitizerRuntimeLibrary(toolchain),
+ "SCUDO_RUNTIME_LIBRARY": config.ScudoRuntimeLibrary(toolchain),
+ "SCUDO_MINIMAL_RUNTIME_LIBRARY": config.ScudoMinimalRuntimeLibrary(toolchain),
+ }
+
+ for variable, value := range sanitizerVariables {
+ ctx.Strict(secondPrefix+variable, value)
+ }
+
+ sanitizerLibs := android.SortedStringValues(sanitizerVariables)
+ var sanitizerLibStems []string
+ ctx.VisitAllModules(func(m android.Module) {
+ if !m.Enabled() {
+ return
+ }
+
+ ccModule, _ := m.(*Module)
+ if ccModule == nil || ccModule.library == nil || !ccModule.library.shared() {
+ return
+ }
+
+ if android.InList(strings.TrimPrefix(ctx.ModuleName(m), "prebuilt_"), sanitizerLibs) &&
+ m.Target().Os == target.Os && m.Target().Arch.ArchType == target.Arch.ArchType {
+ outputFile := ccModule.outputFile
+ if outputFile.Valid() {
+ sanitizerLibStems = append(sanitizerLibStems, outputFile.Path().Base())
+ }
+ }
+ })
+ sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
+ ctx.Strict(secondPrefix+"SANITIZER_STEMS", strings.Join(sanitizerLibStems, " "))
}
// This is used by external/gentoo/...
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 7879a7d..fc682ff 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/cc/config"
)
func init() {
@@ -208,20 +209,26 @@
*ndkKnownLibs = append(*ndkKnownLibs, name)
}
+var stubLibraryCompilerFlags = []string{
+ // We're knowingly doing some otherwise unsightly things with builtin
+ // functions here. We're just generating stub libraries, so ignore it.
+ "-Wno-incompatible-library-redeclaration",
+ "-Wno-incomplete-setjmp-declaration",
+ "-Wno-builtin-requires-header",
+ "-Wno-invalid-noreturn",
+ "-Wall",
+ "-Werror",
+ // These libraries aren't actually used. Don't worry about unwinding
+ // (avoids the need to link an unwinder into a fake library).
+ "-fno-unwind-tables",
+}
+
+func init() {
+ config.ExportStringList("StubLibraryCompilerFlags", stubLibraryCompilerFlags)
+}
+
func addStubLibraryCompilerFlags(flags Flags) Flags {
- flags.Global.CFlags = append(flags.Global.CFlags,
- // We're knowingly doing some otherwise unsightly things with builtin
- // functions here. We're just generating stub libraries, so ignore it.
- "-Wno-incompatible-library-redeclaration",
- "-Wno-incomplete-setjmp-declaration",
- "-Wno-builtin-requires-header",
- "-Wno-invalid-noreturn",
- "-Wall",
- "-Werror",
- // These libraries aren't actually used. Don't worry about unwinding
- // (avoids the need to link an unwinder into a fake library).
- "-fno-unwind-tables",
- )
+ flags.Global.CFlags = append(flags.Global.CFlags, stubLibraryCompilerFlags...)
// All symbols in the stubs library should be visible.
if inList("-fvisibility=hidden", flags.Local.CFlags) {
flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default")
@@ -272,7 +279,7 @@
func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
return compileObjs(ctx, flagsToBuilderFlags(flags), "",
- android.Paths{src}, nil, nil, nil)
+ android.Paths{src}, nil, nil, nil, nil)
}
func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 2726b1a..6c200f5 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -33,7 +33,7 @@
// Component 3: Stub Libraries
// The shared libraries in the NDK are not the actual shared libraries they
// refer to (to prevent people from accidentally loading them), but stub
-// libraries with dummy implementations of everything for use at build time
+// libraries with placeholder implementations of everything for use at build time
// only.
//
// Since we don't actually need to know anything about the stub libraries aside
@@ -82,6 +82,12 @@
return android.PathForOutput(ctx, "ndk_base.timestamp")
}
+// The headers timestamp file depends only on the NDK headers.
+// This is used mainly for .tidy files that do not need any stub libraries.
+func getNdkHeadersTimestampFile(ctx android.PathContext) android.WritablePath {
+ return android.PathForOutput(ctx, "ndk_headers.timestamp")
+}
+
// The full timestamp file depends on the base timestamp *and* the static
// libraries.
func getNdkFullTimestampFile(ctx android.PathContext) android.WritablePath {
@@ -96,6 +102,7 @@
func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
var staticLibInstallPaths android.Paths
+ var headerPaths android.Paths
var installPaths android.Paths
var licensePaths android.Paths
ctx.VisitAllModules(func(module android.Module) {
@@ -104,16 +111,19 @@
}
if m, ok := module.(*headerModule); ok {
+ headerPaths = append(headerPaths, m.installPaths...)
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
if m, ok := module.(*versionedHeaderModule); ok {
+ headerPaths = append(headerPaths, m.installPaths...)
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
if m, ok := module.(*preprocessedHeadersModule); ok {
+ headerPaths = append(headerPaths, m.installPaths...)
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
@@ -153,6 +163,12 @@
Validation: getNdkAbiDiffTimestampFile(ctx),
})
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Touch,
+ Output: getNdkHeadersTimestampFile(ctx),
+ Implicits: headerPaths,
+ })
+
fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
// There's a phony "ndk" rule defined in core/main.mk that depends on this.
diff --git a/cc/object.go b/cc/object.go
index d8bb08f..fdd0b11 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -29,7 +29,6 @@
android.RegisterModuleType("cc_object", ObjectFactory)
android.RegisterSdkMemberType(ccObjectSdkMemberType)
- android.RegisterBp2BuildMutator("cc_object", ObjectBp2Build)
}
var ccObjectSdkMemberType = &librarySdkMemberType{
@@ -54,7 +53,7 @@
func (handler *objectBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
- objPaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
+ 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)
@@ -117,6 +116,7 @@
module.sdkMemberTypes = []android.SdkMemberType{ccObjectSdkMemberType}
+ module.bazelable = true
return module.Init()
}
@@ -135,26 +135,17 @@
Linker_script bazel.LabelAttribute
}
-// ObjectBp2Build is the bp2build converter from cc_object modules to the
+// objectBp2Build is the bp2build converter from cc_object modules to the
// Bazel equivalent target, plus any necessary include deps for the cc_object.
-func ObjectBp2Build(ctx android.TopDownMutatorContext) {
- m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build(ctx) {
- return
- }
-
- // a Module can be something other than a cc_object.
- if ctx.ModuleType() != "cc_object" {
- return
- }
-
+func objectBp2Build(ctx android.TopDownMutatorContext, m *Module) {
if m.compiler == nil {
// a cc_object must have access to the compiler decorator for its props.
ctx.ModuleErrorf("compiler must not be nil for a cc_object module")
}
// Set arch-specific configurable attributes
- compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
+ baseAttributes := bp2BuildParseBaseProps(ctx, m)
+ compilerAttrs := baseAttributes.compilerAttributes
var deps bazel.LabelListAttribute
systemDynamicDeps := bazel.LabelListAttribute{ForceSpecifyEmptyList: true}
@@ -164,7 +155,8 @@
for config, props := range configToProps {
if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok {
if objectLinkerProps.Linker_script != nil {
- linkerScript.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *objectLinkerProps.Linker_script))
+ label := android.BazelLabelForModuleSrcSingle(ctx, *objectLinkerProps.Linker_script)
+ linkerScript.SetSelectValue(axis, config, label)
}
deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs))
systemSharedLibs := objectLinkerProps.System_shared_libs
@@ -203,7 +195,7 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "cc_object",
- Bzl_load_location: "//build/bazel/rules:cc_object.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_object.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
diff --git a/cc/pgo.go b/cc/pgo.go
index e78549e..aa0feae 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -96,10 +96,6 @@
flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrumentFlag)
return flags
}
-func (props *PgoProperties) addSamplingProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
- flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
- return flags
-}
func (props *PgoProperties) getPgoProfileFile(ctx BaseModuleContext) android.OptionalPath {
profileFile := *props.Pgo.Profile_file
@@ -212,6 +208,10 @@
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")
}
@@ -313,10 +313,6 @@
if (props.ShouldProfileModule && props.isInstrumentation()) || props.PgoInstrLink {
// Instrumentation PGO use and gather flags cannot coexist.
return props.addInstrumentationProfileGatherFlags(ctx, flags)
- } else if props.ShouldProfileModule && props.isSampling() {
- flags = props.addSamplingProfileGatherFlags(ctx, flags)
- } else if ctx.DeviceConfig().SamplingPGO() {
- flags = props.addSamplingProfileGatherFlags(ctx, flags)
}
if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index d324241..339a16d 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,9 +16,9 @@
import (
"path/filepath"
- "strings"
"android/soong/android"
+ "android/soong/bazel"
)
func init() {
@@ -112,8 +112,6 @@
// TODO(ccross): verify shared library dependencies
srcs := p.prebuiltSrcs(ctx)
if len(srcs) > 0 {
- builderFlags := flagsToBuilderFlags(flags)
-
if len(srcs) > 1 {
ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
return nil
@@ -150,7 +148,7 @@
// depending on a table of contents file instead of the library itself.
tocFile := android.PathForModuleOut(ctx, libName+".toc")
p.tocFile = android.OptionalPathForPath(tocFile)
- transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
+ TransformSharedObjectToToc(ctx, outputFile, tocFile)
if ctx.Windows() && p.properties.Windows_import_lib != nil {
// Consumers of this library actually links to the import library in build
@@ -197,7 +195,13 @@
if p.header() {
ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
- return nil
+ // Need to return an output path so that the AndroidMk logic doesn't skip
+ // the prebuilt header. For compatibility, in case Android.mk files use a
+ // header lib in LOCAL_STATIC_LIBRARIES, create an empty ar file as
+ // placeholder, just like non-prebuilt header modules do in linkStatic().
+ ph := android.PathForModuleOut(ctx, ctx.ModuleName()+staticLibraryExtension)
+ transformObjToStaticLib(ctx, nil, nil, builderFlags{}, ph, nil, nil)
+ return ph
}
return nil
@@ -232,10 +236,10 @@
// Implements versionedInterface
func (p *prebuiltLibraryLinker) implementationModuleName(name string) string {
- return strings.TrimPrefix(name, "prebuilt_")
+ return android.RemoveOptionalPrebuiltPrefix(name)
}
-func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
+func NewPrebuiltLibrary(hod android.HostOrDeviceSupported, srcsProperty string) (*Module, *libraryDecorator) {
module, library := NewLibrary(hod)
module.compiler = nil
@@ -247,11 +251,15 @@
module.AddProperties(&prebuilt.properties)
- srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string {
- return prebuilt.prebuiltSrcs(ctx)
- }
+ if srcsProperty == "" {
+ android.InitPrebuiltModuleWithoutSrcs(module)
+ } else {
+ srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string {
+ return prebuilt.prebuiltSrcs(ctx)
+ }
- android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
+ android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, srcsProperty)
+ }
// Prebuilt libraries can be used in SDKs.
android.InitSdkAwareModule(module)
@@ -261,7 +269,7 @@
// cc_prebuilt_library installs a precompiled shared library that are
// listed in the srcs property in the device's directory.
func PrebuiltLibraryFactory() android.Module {
- module, _ := NewPrebuiltLibrary(android.HostAndDeviceSupported)
+ module, _ := NewPrebuiltLibrary(android.HostAndDeviceSupported, "srcs")
// Prebuilt shared libraries can be included in APEXes
android.InitApexModule(module)
@@ -280,15 +288,17 @@
// to be used as a data dependency of a test-related module (such as cc_test, or
// cc_test_library).
func PrebuiltSharedTestLibraryFactory() android.Module {
- module, library := NewPrebuiltLibrary(android.HostAndDeviceSupported)
+ module, library := NewPrebuiltLibrary(android.HostAndDeviceSupported, "srcs")
library.BuildOnlyShared()
library.baseInstaller = NewTestInstaller()
return module.Init()
}
func NewPrebuiltSharedLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
- module, library := NewPrebuiltLibrary(hod)
+ 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)
@@ -304,12 +314,58 @@
}
func NewPrebuiltStaticLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
- module, library := NewPrebuiltLibrary(hod)
+ module, library := NewPrebuiltLibrary(hod, "srcs")
library.BuildOnlyStatic()
+ module.bazelable = true
module.bazelHandler = &prebuiltStaticLibraryBazelHandler{module: module, library: library}
return module, library
}
+type bazelPrebuiltLibraryStaticAttributes struct {
+ Static_library bazel.LabelAttribute
+ Export_includes bazel.StringListAttribute
+ Export_system_includes bazel.StringListAttribute
+}
+
+func prebuiltLibraryStaticBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+ prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module)
+ exportedIncludes := Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx, module)
+
+ attrs := &bazelPrebuiltLibraryStaticAttributes{
+ Static_library: prebuiltAttrs.Src,
+ Export_includes: exportedIncludes.Includes,
+ Export_system_includes: exportedIncludes.SystemIncludes,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "prebuilt_library_static",
+ Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_static.bzl",
+ }
+
+ name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+}
+
+type bazelPrebuiltLibrarySharedAttributes struct {
+ Shared_library bazel.LabelAttribute
+}
+
+func prebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+ prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module)
+
+ attrs := &bazelPrebuiltLibrarySharedAttributes{
+ Shared_library: prebuiltAttrs.Src,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "prebuilt_library_shared",
+ Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_shared.bzl",
+ }
+
+ name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+}
+
type prebuiltObjectProperties struct {
Srcs []string `android:"path,arch_variant"`
}
@@ -330,7 +386,7 @@
func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+ ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil {
ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
}
@@ -371,6 +427,69 @@
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
+ }
+ sharedLibs := ccInfo.CcSharedLibraryFiles
+ if len(sharedLibs) != 1 {
+ ctx.ModuleErrorf("expected 1 shared library from bazel target %s, got %q", label, sharedLibs)
+ return false
+ }
+
+ // TODO(b/184543518): cc_prebuilt_library_shared may have properties for re-exporting flags
+
+ // 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)
+
+ // FIXME(b/214600441): We don't yet strip prebuilt shared libraries
+ h.library.unstrippedOutputFile = out
+
+ 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
+ }
+
+ info := SharedLibraryInfo{
+ SharedLibrary: out,
+ TableOfContents: android.OptionalPathForPath(toc),
+ Target: ctx.Target(),
+ }
+ ctx.SetProvider(SharedLibraryInfoProvider, info)
+
+ h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo)
+ h.module.maybeUnhideFromMake()
+
+ return true
+}
+
func (p *prebuiltObjectLinker) prebuilt() *android.Prebuilt {
return &p.Prebuilt
}
@@ -497,7 +616,7 @@
}
func NewPrebuiltBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
- module, binary := NewBinary(hod)
+ module, binary := newBinary(hod, false)
module.compiler = nil
prebuilt := &prebuiltBinaryLinker{
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index c8f8103..94f75fe 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -19,7 +19,7 @@
"testing"
"android/soong/android"
-
+ "android/soong/bazel/cquery"
"github.com/google/blueprint"
)
@@ -378,3 +378,74 @@
static2 = ctx.ModuleForTests("libtest_static", "android_arm64_armv8-a_static_hwasan").Module().(*Module)
assertString(t, static2.OutputFile().Path().Base(), "libf.hwasan.a")
}
+
+func TestPrebuiltLibrarySharedWithBazelWithoutToc(t *testing.T) {
+ const bp = `
+cc_prebuilt_library_shared {
+ name: "foo",
+ srcs: ["foo.so"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ outBaseDir := "outputbase"
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcSharedLibraryFiles: []string{"foo.so"},
+ },
+ },
+ }
+ ctx := testCcWithConfig(t, config)
+ sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ pathPrefix := outBaseDir + "/execroot/__main__/"
+
+ info := ctx.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ android.AssertPathRelativeToTopEquals(t, "prebuilt shared library",
+ pathPrefix+"foo.so", info.SharedLibrary)
+ android.AssertPathRelativeToTopEquals(t, "prebuilt's 'nullary' ToC",
+ pathPrefix+"foo.so", info.TableOfContents.Path())
+
+ 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, "output files", expectedOutputFiles, outputFiles.Strings())
+}
+
+func TestPrebuiltLibrarySharedWithBazelWithToc(t *testing.T) {
+ const bp = `
+cc_prebuilt_library_shared {
+ name: "foo",
+ srcs: ["foo.so"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ outBaseDir := "outputbase"
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcSharedLibraryFiles: []string{"foo.so"},
+ TocFile: "toc",
+ },
+ },
+ }
+ ctx := testCcWithConfig(t, config)
+ sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ pathPrefix := outBaseDir + "/execroot/__main__/"
+
+ info := ctx.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ android.AssertPathRelativeToTopEquals(t, "prebuilt shared library's ToC",
+ pathPrefix+"toc", info.TableOfContents.Path())
+ android.AssertPathRelativeToTopEquals(t, "prebuilt shared library",
+ pathPrefix+"foo.so", info.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, "output files", expectedOutputFiles, outputFiles.Strings())
+}
diff --git a/cc/proto.go b/cc/proto.go
index 4466144..3cf1453 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -16,8 +16,14 @@
import (
"github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
+)
+
+const (
+ protoTypeDefault = "lite"
)
// genProto creates a rule to convert a .proto file to generated .pb.cc and .pb.h files and returns
@@ -63,7 +69,7 @@
var lib string
if String(p.Proto.Plugin) == "" {
- switch String(p.Proto.Type) {
+ switch proptools.StringDefault(p.Proto.Type, protoTypeDefault) {
case "full":
if ctx.useSdk() {
lib = "libprotobuf-cpp-full-ndk"
@@ -71,7 +77,7 @@
} else {
lib = "libprotobuf-cpp-full"
}
- case "lite", "":
+ case "lite":
if ctx.useSdk() {
lib = "libprotobuf-cpp-lite-ndk"
static = true
@@ -157,3 +163,69 @@
return flags
}
+
+type protoAttributes struct {
+ Deps bazel.LabelListAttribute
+}
+
+type bp2buildProtoDeps struct {
+ wholeStaticLib *bazel.LabelAttribute
+ implementationWholeStaticLib *bazel.LabelAttribute
+ protoDep *bazel.LabelAttribute
+}
+
+func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) bp2buildProtoDeps {
+ var ret bp2buildProtoDeps
+
+ protoInfo, ok := android.Bp2buildProtoProperties(ctx, m, protoSrcs)
+ if !ok {
+ return ret
+ }
+
+ var depName string
+ typ := proptools.StringDefault(protoInfo.Type, protoTypeDefault)
+ var rule_class string
+ suffix := "_cc_proto"
+ switch typ {
+ case "lite":
+ suffix += "_lite"
+ rule_class = "cc_lite_proto_library"
+ depName = "libprotobuf-cpp-lite"
+ case "full":
+ rule_class = "cc_proto_library"
+ depName = "libprotobuf-cpp-full"
+ default:
+ ctx.PropertyErrorf("proto.type", "cannot handle conversion at this time: %q", typ)
+ }
+
+ 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}})
+
+ name := m.Name() + suffix
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: rule_class,
+ Bzl_load_location: "//build/bazel/rules/cc:cc_proto.bzl",
+ },
+ android.CommonAttributes{Name: name},
+ &protoAttrs)
+
+ var privateHdrs bool
+ if lib, ok := m.linker.(*libraryDecorator); ok {
+ privateHdrs = !proptools.Bool(lib.Properties.Proto.Export_proto_headers)
+ }
+
+ labelAttr := &bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + name}}
+ if privateHdrs {
+ ret.implementationWholeStaticLib = labelAttr
+ } else {
+ ret.wholeStaticLib = labelAttr
+ }
+
+ return ret
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index f6a9d5b..f8661a6 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -35,12 +35,19 @@
asanCflags = []string{
"-fno-omit-frame-pointer",
- "-fno-experimental-new-pass-manager",
}
asanLdflags = []string{"-Wl,-u,__asan_preinit"}
- hwasanCflags = []string{"-fno-omit-frame-pointer", "-Wno-frame-larger-than=",
+ hwasanCflags = []string{
+ "-fno-omit-frame-pointer",
+ "-Wno-frame-larger-than=",
"-fsanitize-hwaddress-abi=platform",
+ "-mllvm", "-hwasan-use-after-scope=1",
+ }
+
+ // ThinLTO performs codegen during link time, thus these flags need to
+ // passed to both CFLAGS and LDFLAGS.
+ hwasanCommonflags = []string{
// The following improves debug location information
// availability at the cost of its accuracy. It increases
// the likelihood of a stack variable's frame offset
@@ -48,11 +55,11 @@
// for the quality of hwasan reports. The downside is a
// higher number of "optimized out" stack variables.
// b/112437883.
- "-mllvm", "-instcombine-lower-dbg-declare=0",
+ "-instcombine-lower-dbg-declare=0",
// TODO(b/159343917): HWASan and GlobalISel don't play nicely, and
// GlobalISel is the default at -O0 on aarch64.
- "-mllvm", "--aarch64-enable-global-isel-at-O=-1",
- "-mllvm", "-fast-isel=false",
+ "--aarch64-enable-global-isel-at-O=-1",
+ "-fast-isel=false",
}
cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
@@ -81,7 +88,7 @@
intOverflow
scs
Fuzzer
- memtag_heap
+ Memtag_heap
cfi // cfi is last to prevent it running before incompatible mutators
)
@@ -92,7 +99,7 @@
intOverflow,
scs,
Fuzzer,
- memtag_heap,
+ Memtag_heap,
cfi, // cfi is last to prevent it running before incompatible mutators
}
@@ -111,7 +118,7 @@
return "cfi"
case scs:
return "scs"
- case memtag_heap:
+ case Memtag_heap:
return "memtag_heap"
case Fuzzer:
return "fuzzer"
@@ -127,7 +134,7 @@
return "address"
case Hwasan:
return "hwaddress"
- case memtag_heap:
+ case Memtag_heap:
return "memtag_heap"
case tsan:
return "thread"
@@ -149,7 +156,7 @@
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 Memtag_heap, intOverflow:
// do nothing
default:
panic(fmt.Errorf("unknown SanitizerType %d", t))
@@ -172,6 +179,8 @@
return true
case Fuzzer:
return true
+ case Memtag_heap:
+ return true
default:
return false
}
@@ -460,7 +469,7 @@
s.Scs = nil
}
- // memtag_heap is only implemented on AArch64.
+ // Memtag_heap is only implemented on AArch64.
if ctx.Arch().ArchType != android.Arm64 {
s.Memtag_heap = nil
}
@@ -531,11 +540,6 @@
if Bool(s.Hwaddress) {
s.Address = nil
s.Thread = nil
- // Disable ubsan diagnosic as a workaround for a compiler bug.
- // TODO(b/191808836): re-enable.
- s.Diag.Undefined = nil
- s.Diag.Integer_overflow = nil
- s.Diag.Misc_undefined = nil
}
// TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
@@ -579,20 +583,12 @@
func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
- minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
- builtinsRuntimeLib := config.BuiltinsRuntimeLibrary(ctx.toolchain()) + ".a"
- builtinsRuntimePath := "${config.ClangAsanLibDir}/" + builtinsRuntimeLib
if sanitize.Properties.MinimalRuntimeDep {
flags.Local.LdFlags = append(flags.Local.LdFlags,
- minimalRuntimePath,
"-Wl,--exclude-libs,"+minimalRuntimeLib)
}
- if sanitize.Properties.BuiltinsDep {
- flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
- }
-
if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
return flags
}
@@ -629,6 +625,14 @@
if Bool(sanitize.Properties.Sanitize.Hwaddress) {
flags.Local.CFlags = append(flags.Local.CFlags, hwasanCflags...)
+
+ for _, flag := range hwasanCommonflags {
+ flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", flag)
+ }
+ for _, flag := range hwasanCommonflags {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,"+flag)
+ }
+
if Bool(sanitize.Properties.Sanitize.Writeonly) {
flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-hwasan-instrument-reads=0")
}
@@ -649,9 +653,6 @@
flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-coverage=stack-depth")
flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-coverage=stack-depth")
- // TODO(b/133876586): Experimental PM breaks sanitizer coverage.
- flags.Local.CFlags = append(flags.Local.CFlags, "-fno-experimental-new-pass-manager")
-
// Disable fortify for fuzzing builds. Generally, we'll be building with
// UBSan or ASan here and the fortify checks pollute the stack traces.
flags.Local.CFlags = append(flags.Local.CFlags, "-U_FORTIFY_SOURCE")
@@ -697,28 +698,27 @@
if len(sanitize.Properties.Sanitizers) > 0 {
sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
-
flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
- if ctx.Host() {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
+
+ if ctx.toolchain().Bionic() {
+ // Bionic sanitizer runtimes have already been added as dependencies so that
+ // the right variant of the runtime will be used (with the "-android"
+ // suffix), so don't let clang the runtime library.
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-link-runtime")
+ } else {
// Host sanitizers only link symbols in the final executable, so
// there will always be undefined symbols in intermediate libraries.
_, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
- flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
- // non-Bionic toolchain prebuilts are missing UBSan's vptr and function sanitizers
- if !ctx.toolchain().Bionic() {
- flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
- }
+ // non-Bionic toolchain prebuilts are missing UBSan's vptr and function san
+ flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
}
if enableMinimalRuntime(sanitize) {
flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
- flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
- if !ctx.toolchain().Bionic() {
- flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
- }
}
if Bool(sanitize.Properties.Sanitize.Fuzzer) {
@@ -798,7 +798,7 @@
return sanitize.Properties.Sanitize.Cfi
case scs:
return sanitize.Properties.Sanitize.Scs
- case memtag_heap:
+ case Memtag_heap:
return sanitize.Properties.Sanitize.Memtag_heap
case Fuzzer:
return sanitize.Properties.Sanitize.Fuzzer
@@ -814,7 +814,7 @@
!sanitize.isSanitizerEnabled(tsan) &&
!sanitize.isSanitizerEnabled(cfi) &&
!sanitize.isSanitizerEnabled(scs) &&
- !sanitize.isSanitizerEnabled(memtag_heap) &&
+ !sanitize.isSanitizerEnabled(Memtag_heap) &&
!sanitize.isSanitizerEnabled(Fuzzer)
}
@@ -844,7 +844,7 @@
sanitize.Properties.Sanitize.Cfi = bPtr
case scs:
sanitize.Properties.Sanitize.Scs = bPtr
- case memtag_heap:
+ case Memtag_heap:
sanitize.Properties.Sanitize.Memtag_heap = bPtr
case Fuzzer:
sanitize.Properties.Sanitize.Fuzzer = bPtr
@@ -1133,7 +1133,7 @@
if lib, ok := snapshot.StaticLibs[noteDep]; ok {
noteDep = lib
}
- depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true}
+ depTag := StaticDepTag(true)
variations := append(mctx.Target().Variations(),
blueprint.Variation{Mutator: "link", Variation: "static"})
if c.Device() {
@@ -1187,6 +1187,36 @@
}
}
+ addStaticDeps := func(deps ...string) {
+ // If we're using snapshots, redirect to snapshot whenever possible
+ snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+ for idx, dep := range deps {
+ if lib, ok := snapshot.StaticLibs[dep]; ok {
+ deps[idx] = lib
+ }
+ }
+
+ // static executable gets static runtime libs
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency}
+ variations := append(mctx.Target().Variations(),
+ blueprint.Variation{Mutator: "link", Variation: "static"})
+ if c.Device() {
+ variations = append(variations, c.ImageVariation())
+ }
+ if c.UseSdk() {
+ variations = append(variations,
+ blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+ }
+ mctx.AddFarVariationDependencies(variations, depTag, deps...)
+
+ }
+ if enableMinimalRuntime(c.sanitize) || c.sanitize.Properties.MinimalRuntimeDep {
+ addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain))
+ }
+ if c.sanitize.Properties.BuiltinsDep {
+ addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain))
+ }
+
if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
// UBSan is supported on non-bionic linux host builds as well
@@ -1198,23 +1228,8 @@
// Note that by adding dependency with {static|shared}DepTag, the lib is
// added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module
if c.staticBinary() {
- deps := append(extraStaticDeps, runtimeLibrary)
- // If we're using snapshots, redirect to snapshot whenever possible
- snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
- for idx, dep := range deps {
- if lib, ok := snapshot.StaticLibs[dep]; ok {
- deps[idx] = lib
- }
- }
-
- // static executable gets static runtime libs
- depTag := libraryDependencyTag{Kind: staticLibraryDependency}
- variations := append(mctx.Target().Variations(),
- blueprint.Variation{Mutator: "link", Variation: "static"})
- if c.Device() {
- variations = append(variations, c.ImageVariation())
- }
- mctx.AddFarVariationDependencies(variations, depTag, deps...)
+ addStaticDeps(runtimeLibrary)
+ addStaticDeps(extraStaticDeps...)
} else if !c.static() && !c.Header() {
// If we're using snapshots, redirect to snapshot whenever possible
snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
@@ -1239,6 +1254,10 @@
if c.Device() {
variations = append(variations, c.ImageVariation())
}
+ if c.UseSdk() {
+ variations = append(variations,
+ blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+ }
AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeLibrary, "", true)
}
// static lib does not have dependency to the runtime library. The
@@ -1303,6 +1322,10 @@
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)
@@ -1323,7 +1346,6 @@
// is redirected to the sanitized variant of the dependent module.
defaultVariation := t.variationName()
// Not all PlatformSanitizeable modules support the CFI sanitizer
- cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
mctx.SetDefaultDependencyVariation(&defaultVariation)
modules := mctx.CreateVariations("", t.variationName())
@@ -1370,7 +1392,7 @@
modules[0].(PlatformSanitizeable).SetInSanitizerDir()
}
- if mctx.Device() && t.incompatibleWithCfi() {
+ 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)
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 9570664..753d74c 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -66,7 +66,7 @@
// Override existing vendor and recovery snapshot for cc module specific extra functions
var VendorSnapshotImageSingleton vendorSnapshotImage = vendorSnapshotImage{&snapshot.VendorSnapshotImageSingleton}
-var recoverySnapshotImageSingleton recoverySnapshotImage = recoverySnapshotImage{&snapshot.RecoverySnapshotImageSingleton}
+var RecoverySnapshotImageSingleton recoverySnapshotImage = recoverySnapshotImage{&snapshot.RecoverySnapshotImageSingleton}
func RegisterVendorSnapshotModules(ctx android.RegistrationContext) {
ctx.RegisterModuleType("vendor_snapshot", vendorSnapshotFactory)
@@ -231,7 +231,7 @@
}
func recoverySnapshotFactory() android.Module {
- return snapshotFactory(recoverySnapshotImageSingleton)
+ return snapshotFactory(RecoverySnapshotImageSingleton)
}
func snapshotFactory(image SnapshotImage) android.Module {
@@ -326,7 +326,7 @@
return
}
- images := []SnapshotImage{VendorSnapshotImageSingleton, recoverySnapshotImageSingleton}
+ images := []SnapshotImage{VendorSnapshotImageSingleton, RecoverySnapshotImageSingleton}
for _, image := range images {
if p.Image == image {
@@ -476,13 +476,12 @@
if p.shared() {
libName := in.Base()
- builderFlags := flagsToBuilderFlags(flags)
// Optimize out relinking against shared libraries whose interface hasn't changed by
// depending on a table of contents file instead of the library itself.
tocFile := android.PathForModuleOut(ctx, libName+".toc")
p.tocFile = android.OptionalPathForPath(tocFile)
- transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
+ TransformSharedObjectToToc(ctx, in, tocFile)
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
SharedLibrary: in,
@@ -507,7 +506,7 @@
}
func (p *snapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
- if p.MatchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
+ if p.MatchesWithDevice(ctx.DeviceConfig()) && p.shared() {
p.baseInstaller.install(ctx, file)
}
}
@@ -584,7 +583,7 @@
// overrides the recovery variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
// is set.
func RecoverySnapshotSharedFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, SnapshotSharedSuffix)
+ module, prebuilt := snapshotLibraryFactory(RecoverySnapshotImageSingleton, SnapshotSharedSuffix)
prebuilt.libraryDecorator.BuildOnlyShared()
return module.Init()
}
@@ -604,7 +603,7 @@
// overrides the recovery variant of the cc static library with the same name, if BOARD_VNDK_VERSION
// is set.
func RecoverySnapshotStaticFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, SnapshotStaticSuffix)
+ module, prebuilt := snapshotLibraryFactory(RecoverySnapshotImageSingleton, SnapshotStaticSuffix)
prebuilt.libraryDecorator.BuildOnlyStatic()
return module.Init()
}
@@ -624,7 +623,7 @@
// overrides the recovery variant of the cc header library with the same name, if BOARD_VNDK_VERSION
// is set.
func RecoverySnapshotHeaderFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotHeaderSuffix)
+ module, prebuilt := snapshotLibraryFactory(RecoverySnapshotImageSingleton, snapshotHeaderSuffix)
prebuilt.libraryDecorator.HeaderOnly()
return module.Init()
}
@@ -699,7 +698,7 @@
// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_binary
// overrides the recovery variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
func RecoverySnapshotBinaryFactory() android.Module {
- return snapshotBinaryFactory(recoverySnapshotImageSingleton, snapshotBinarySuffix)
+ return snapshotBinaryFactory(RecoverySnapshotImageSingleton, snapshotBinarySuffix)
}
func snapshotBinaryFactory(image SnapshotImage, moduleSuffix string) android.Module {
@@ -801,7 +800,7 @@
}
module.linker = prebuilt
- prebuilt.Init(module, recoverySnapshotImageSingleton, snapshotObjectSuffix)
+ prebuilt.Init(module, RecoverySnapshotImageSingleton, snapshotObjectSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 24abcce..de50ef5 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -113,7 +113,7 @@
return ctx.Config().VndkSnapshotBuildArtifacts()
}
- for _, image := range []SnapshotImage{VendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
+ for _, image := range []SnapshotImage{VendorSnapshotImageSingleton, RecoverySnapshotImageSingleton} {
if isSnapshotAware(ctx.DeviceConfig(), m, image.IsProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), apexInfo, image) {
return true
}
diff --git a/cc/test.go b/cc/test.go
index c589165..d8b7833 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -105,11 +105,6 @@
// Add RunCommandTargetPreparer to stop framework before the test and start it after the test.
Disable_framework *bool
- // Add ShippingApiLevelModuleController to auto generated test config. If the device properties
- // for the shipping api level is less than the test_min_api_level, skip this module.
- // Deprecated (b/187258404). Use test_options.min_shipping_api_level instead.
- Test_min_api_level *int64
-
// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
// explicitly.
@@ -378,31 +373,26 @@
ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
- ccDep, ok := dep.(LinkableInterface)
-
+ linkableDep, ok := dep.(LinkableInterface)
if !ok {
- ctx.ModuleErrorf("data_lib %q is not a linkable cc module", depName)
+ ctx.ModuleErrorf("data_lib %q is not a LinkableInterface module", depName)
}
- ccModule, ok := dep.(*Module)
- if !ok {
- ctx.ModuleErrorf("data_lib %q is not a cc module", depName)
- }
- if ccDep.OutputFile().Valid() {
+ if linkableDep.OutputFile().Valid() {
test.data = append(test.data,
- android.DataPath{SrcPath: ccDep.OutputFile().Path(),
- RelativeInstallPath: ccModule.installer.relativeInstallPath()})
+ android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
+ RelativeInstallPath: linkableDep.RelativeInstallPath()})
}
})
ctx.VisitDirectDepsWithTag(dataBinDepTag, func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
- ccModule, ok := dep.(*Module)
+ linkableDep, ok := dep.(LinkableInterface)
if !ok {
- ctx.ModuleErrorf("data_bin %q is not a cc module", depName)
+ ctx.ModuleErrorf("data_bin %q is not a LinkableInterface module", depName)
}
- if ccModule.OutputFile().Valid() {
+ if linkableDep.OutputFile().Valid() {
test.data = append(test.data,
- android.DataPath{SrcPath: ccModule.OutputFile().Path(),
- RelativeInstallPath: ccModule.installer.relativeInstallPath()})
+ android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
+ RelativeInstallPath: linkableDep.RelativeInstallPath()})
}
})
@@ -437,14 +427,6 @@
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})
- } else if test.Properties.Test_min_api_level != nil {
- // TODO: (b/187258404) Remove test.Properties.Test_min_api_level
- if test.Properties.Test_options.Vsr_min_shipping_api_level != nil {
- ctx.PropertyErrorf("test_min_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_min_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
@@ -479,7 +461,7 @@
}
func NewTest(hod android.HostOrDeviceSupported) *Module {
- module, binary := NewBinary(hod)
+ module, binary := newBinary(hod, false)
module.multilib = android.MultilibBoth
binary.baseInstaller = NewTestInstaller()
@@ -569,6 +551,10 @@
testConfig android.Path
}
+func (benchmark *benchmarkDecorator) benchmarkBinary() bool {
+ return true
+}
+
func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
runpath := "../../lib"
if ctx.toolchain().Is64Bit() {
@@ -606,7 +592,7 @@
}
func NewBenchmark(hod android.HostOrDeviceSupported) *Module {
- module, binary := NewBinary(hod)
+ module, binary := newBinary(hod, false)
module.multilib = android.MultilibBoth
binary.baseInstaller = NewBaseInstaller("benchmarktest", "benchmarktest64", InstallInData)
diff --git a/cc/testing.go b/cc/testing.go
index b0a220c..a03d147 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -30,7 +30,6 @@
RegisterLibraryBuildComponents(ctx)
RegisterLibraryHeadersBuildComponents(ctx)
- ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
ctx.RegisterModuleType("cc_object", ObjectFactory)
ctx.RegisterModuleType("cc_genrule", GenRuleFactory)
@@ -63,41 +62,14 @@
func commonDefaultModules() string {
return `
- toolchain_library {
- name: "libcompiler_rt-extras",
+ cc_defaults {
+ name: "toolchain_libs_defaults",
vendor_available: true,
- vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
- src: "",
- }
-
- toolchain_library {
- name: "libclang_rt.builtins-arm-android",
- vendor_available: true,
- vendor_ramdisk_available: true,
- product_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- src: "",
- }
-
- toolchain_library {
- name: "libclang_rt.builtins-aarch64-android",
- vendor_available: true,
- vendor_ramdisk_available: true,
- product_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- src: "",
- }
-
- cc_prebuilt_library_shared {
- name: "libclang_rt.hwasan-aarch64-android",
+ no_libcrt: true,
+ sdk_version: "minimum",
nocrt: true,
- vendor_available: true,
- product_available: true,
- recovery_available: true,
system_shared_libs: [],
stl: "none",
srcs: [""],
@@ -107,87 +79,115 @@
},
}
- toolchain_library {
+ cc_prebuilt_library_static {
+ name: "libcompiler_rt-extras",
+ defaults: ["toolchain_libs_defaults"],
+ vendor_ramdisk_available: true,
+ }
+
+ cc_prebuilt_library_static {
+ name: "libclang_rt.builtins-arm-android",
+ defaults: ["toolchain_libs_defaults"],
+ native_bridge_supported: true,
+ vendor_ramdisk_available: true,
+ }
+
+ cc_prebuilt_library_static {
+ name: "libclang_rt.builtins-aarch64-android",
+ defaults: ["toolchain_libs_defaults"],
+ native_bridge_supported: true,
+ vendor_ramdisk_available: true,
+ }
+
+ cc_prebuilt_library_static {
+ name: "libclang_rt.builtins-x86_64",
+ defaults: ["toolchain_libs_defaults"],
+ host_supported: true,
+ }
+
+ cc_prebuilt_library_static {
+ name: "libclang_rt.builtins-i386",
+ defaults: ["toolchain_libs_defaults"],
+ host_supported: true,
+ }
+
+ cc_prebuilt_library_shared {
+ name: "libclang_rt.hwasan-aarch64-android",
+ defaults: ["toolchain_libs_defaults"],
+ }
+
+ cc_prebuilt_library_static {
name: "libclang_rt.builtins-i686-android",
- vendor_available: true,
+ defaults: ["toolchain_libs_defaults"],
vendor_ramdisk_available: true,
- product_available: true,
- recovery_available: true,
native_bridge_supported: true,
- src: "",
}
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libclang_rt.builtins-x86_64-android",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- vendor_ramdisk_available: true,
- product_available: true,
- recovery_available: true,
+ defaults: [
+ "linux_bionic_supported",
+ "toolchain_libs_defaults",
+ ],
native_bridge_supported: true,
- src: "",
+ vendor_ramdisk_available: true,
}
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libunwind",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
+ defaults: [
+ "linux_bionic_supported",
+ "toolchain_libs_defaults",
+ ],
vendor_ramdisk_available: true,
- product_available: true,
- recovery_available: true,
native_bridge_supported: true,
- src: "",
}
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libclang_rt.fuzzer-arm-android",
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- src: "",
+ defaults: ["toolchain_libs_defaults"],
}
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libclang_rt.fuzzer-aarch64-android",
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- src: "",
+ defaults: ["toolchain_libs_defaults"],
}
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libclang_rt.fuzzer-i686-android",
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- src: "",
+ defaults: ["toolchain_libs_defaults"],
}
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libclang_rt.fuzzer-x86_64-android",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- src: "",
+ defaults: [
+ "linux_bionic_supported",
+ "toolchain_libs_defaults",
+ ],
}
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libclang_rt.fuzzer-x86_64",
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- src: "",
+ defaults: [
+ "linux_bionic_supported",
+ "toolchain_libs_defaults",
+ ],
}
// Needed for sanitizer
cc_prebuilt_library_shared {
name: "libclang_rt.ubsan_standalone-aarch64-android",
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- system_shared_libs: [],
- srcs: [""],
+ defaults: ["toolchain_libs_defaults"],
+ }
+
+ cc_prebuilt_library_static {
+ name: "libclang_rt.ubsan_minimal-aarch64-android",
+ defaults: ["toolchain_libs_defaults"],
+ }
+
+ cc_prebuilt_library_static {
+ name: "libclang_rt.ubsan_minimal-arm-android",
+ defaults: ["toolchain_libs_defaults"],
}
cc_library {
@@ -480,7 +480,7 @@
func withWindowsModules() string {
return `
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libwinpthread",
host_supported: true,
enabled: false,
@@ -489,7 +489,8 @@
enabled: true,
},
},
- src: "",
+ stl: "none",
+ srcs:[""],
}
`
}
@@ -763,3 +764,11 @@
}
return paths
}
+
+func AssertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool, variant string) {
+ t.Helper()
+ m := ctx.ModuleForTests(name, variant).Module().(LinkableInterface)
+ if m.ExcludeFromRecoverySnapshot() != expected {
+ t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
+ }
+}
diff --git a/cc/tidy.go b/cc/tidy.go
index 53ff156..750e9de 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -15,6 +15,7 @@
package cc
import (
+ "path/filepath"
"regexp"
"strings"
@@ -70,13 +71,17 @@
return flags
}
- // If not explicitly set, check the global tidy flag
- if tidy.Properties.Tidy == nil && !ctx.Config().ClangTidy() {
- 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,
+ // set flags.NeedTidyFiles to make this module depend on .tidy files.
+ if ctx.Config().ClangTidy() || Bool(tidy.Properties.Tidy) {
+ flags.NeedTidyFiles = true
+ }
+
// Add global WITH_TIDY_FLAGS and local tidy_flags.
withTidyFlags := ctx.Config().Getenv("WITH_TIDY_FLAGS")
if len(withTidyFlags) > 0 {
@@ -97,6 +102,12 @@
}
flags.TidyFlags = append(flags.TidyFlags, headerFilter)
}
+ // Work around RBE bug in parsing clang-tidy flags, replace "--flag" with "-flag".
+ // Some C/C++ modules added local tidy flags like --header-filter= and --extra-arg-before=.
+ doubleDash := regexp.MustCompile("^('?)--(.*)$")
+ for i, s := range flags.TidyFlags {
+ flags.TidyFlags[i] = doubleDash.ReplaceAllString(s, "$1-$2")
+ }
// If clang-tidy is not enabled globally, add the -quiet flag.
if !ctx.Config().ClangTidy() {
@@ -151,6 +162,9 @@
// 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") {
@@ -183,3 +197,154 @@
}
return flags
}
+
+func init() {
+ android.RegisterSingletonType("tidy_phony_targets", TidyPhonySingleton)
+}
+
+// This TidyPhonySingleton generates both tidy-* and obj-* phony targets for C/C++ files.
+func TidyPhonySingleton() android.Singleton {
+ return &tidyPhonySingleton{}
+}
+
+type tidyPhonySingleton struct{}
+
+// Given a final module, add its tidy/obj phony targets to tidy/objModulesInDirGroup.
+func collectTidyObjModuleTargets(ctx android.SingletonContext, module android.Module,
+ tidyModulesInDirGroup, objModulesInDirGroup map[string]map[string]android.Paths) {
+ allObjFileGroups := make(map[string]android.Paths) // variant group name => obj file Paths
+ allTidyFileGroups := make(map[string]android.Paths) // variant group name => tidy file Paths
+ subsetObjFileGroups := make(map[string]android.Paths) // subset group name => obj file Paths
+ subsetTidyFileGroups := make(map[string]android.Paths) // subset group name => tidy file Paths
+
+ // (1) Collect all obj/tidy files into OS-specific groups.
+ ctx.VisitAllModuleVariants(module, func(variant android.Module) {
+ if ctx.Config().KatiEnabled() && android.ShouldSkipAndroidMkProcessing(variant) {
+ return
+ }
+ if m, ok := variant.(*Module); ok {
+ osName := variant.Target().Os.Name
+ addToOSGroup(osName, m.objFiles, allObjFileGroups, subsetObjFileGroups)
+ addToOSGroup(osName, m.tidyFiles, allTidyFileGroups, subsetTidyFileGroups)
+ }
+ })
+
+ // (2) Add an all-OS group, with "" or "subset" name, to include all os-specific phony targets.
+ addAllOSGroup(ctx, module, allObjFileGroups, "", "obj")
+ addAllOSGroup(ctx, module, allTidyFileGroups, "", "tidy")
+ addAllOSGroup(ctx, module, subsetObjFileGroups, "subset", "obj")
+ addAllOSGroup(ctx, module, subsetTidyFileGroups, "subset", "tidy")
+
+ tidyTargetGroups := make(map[string]android.Path)
+ objTargetGroups := make(map[string]android.Path)
+ genObjTidyPhonyTargets(ctx, module, "obj", allObjFileGroups, objTargetGroups)
+ genObjTidyPhonyTargets(ctx, module, "obj", subsetObjFileGroups, objTargetGroups)
+ genObjTidyPhonyTargets(ctx, module, "tidy", allTidyFileGroups, tidyTargetGroups)
+ genObjTidyPhonyTargets(ctx, module, "tidy", subsetTidyFileGroups, tidyTargetGroups)
+
+ moduleDir := ctx.ModuleDir(module)
+ appendToModulesInDirGroup(tidyTargetGroups, moduleDir, tidyModulesInDirGroup)
+ appendToModulesInDirGroup(objTargetGroups, moduleDir, objModulesInDirGroup)
+}
+
+func (m *tidyPhonySingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ // For tidy-* directory phony targets, there are different variant groups.
+ // tidyModulesInDirGroup[G][D] is for group G, directory D, with Paths
+ // of all phony targets to be included into direct dependents of tidy-D_G.
+ tidyModulesInDirGroup := make(map[string]map[string]android.Paths)
+ // Also for obj-* directory phony targets.
+ objModulesInDirGroup := make(map[string]map[string]android.Paths)
+
+ // Collect tidy/obj targets from the 'final' modules.
+ ctx.VisitAllModules(func(module android.Module) {
+ if module == ctx.FinalModule(module) {
+ collectTidyObjModuleTargets(ctx, module, tidyModulesInDirGroup, objModulesInDirGroup)
+ }
+ })
+
+ suffix := ""
+ if ctx.Config().KatiEnabled() {
+ suffix = "-soong"
+ }
+ generateObjTidyPhonyTargets(ctx, suffix, "obj", objModulesInDirGroup)
+ generateObjTidyPhonyTargets(ctx, suffix, "tidy", tidyModulesInDirGroup)
+}
+
+// The name for an obj/tidy module variant group phony target is Name_group-obj/tidy,
+func objTidyModuleGroupName(module android.Module, group string, suffix string) string {
+ if group == "" {
+ return module.Name() + "-" + suffix
+ }
+ return module.Name() + "_" + group + "-" + suffix
+}
+
+// Generate obj-* or tidy-* phony targets.
+func generateObjTidyPhonyTargets(ctx android.SingletonContext, suffix string, prefix string, objTidyModulesInDirGroup map[string]map[string]android.Paths) {
+ // For each variant group, create a <prefix>-<directory>_group target that
+ // depends on all subdirectories and modules in the directory.
+ for group, modulesInDir := range objTidyModulesInDirGroup {
+ groupSuffix := ""
+ if group != "" {
+ groupSuffix = "_" + group
+ }
+ mmTarget := func(dir string) string {
+ return prefix + "-" + strings.Replace(filepath.Clean(dir), "/", "-", -1) + groupSuffix
+ }
+ dirs, topDirs := android.AddAncestors(ctx, modulesInDir, mmTarget)
+ // Create a <prefix>-soong_group target that depends on all <prefix>-dir_group of top level dirs.
+ var topDirPaths android.Paths
+ for _, dir := range topDirs {
+ topDirPaths = append(topDirPaths, android.PathForPhony(ctx, mmTarget(dir)))
+ }
+ ctx.Phony(prefix+suffix+groupSuffix, topDirPaths...)
+ // Create a <prefix>-dir_group target that depends on all targets in modulesInDir[dir]
+ for _, dir := range dirs {
+ if dir != "." && dir != "" {
+ ctx.Phony(mmTarget(dir), modulesInDir[dir]...)
+ }
+ }
+ }
+}
+
+// Append (obj|tidy)TargetGroups[group] into (obj|tidy)ModulesInDirGroups[group][moduleDir].
+func appendToModulesInDirGroup(targetGroups map[string]android.Path, moduleDir string, modulesInDirGroup map[string]map[string]android.Paths) {
+ for group, phonyPath := range targetGroups {
+ if _, found := modulesInDirGroup[group]; !found {
+ modulesInDirGroup[group] = make(map[string]android.Paths)
+ }
+ modulesInDirGroup[group][moduleDir] = append(modulesInDirGroup[group][moduleDir], phonyPath)
+ }
+}
+
+// Add given files to the OS group and subset group.
+func addToOSGroup(osName string, files android.Paths, allGroups, subsetGroups map[string]android.Paths) {
+ if len(files) > 0 {
+ subsetName := osName + "_subset"
+ allGroups[osName] = append(allGroups[osName], files...)
+ // Now include only the first variant in the subsetGroups.
+ // If clang and clang-tidy get faster, we might include more variants.
+ if _, found := subsetGroups[subsetName]; !found {
+ subsetGroups[subsetName] = files
+ }
+ }
+}
+
+// Add an all-OS group, with groupName, to include all os-specific phony targets.
+func addAllOSGroup(ctx android.SingletonContext, module android.Module, phonyTargetGroups map[string]android.Paths, groupName string, objTidyName string) {
+ if len(phonyTargetGroups) > 0 {
+ var targets android.Paths
+ for group, _ := range phonyTargetGroups {
+ targets = append(targets, android.PathForPhony(ctx, objTidyModuleGroupName(module, group, objTidyName)))
+ }
+ phonyTargetGroups[groupName] = targets
+ }
+}
+
+// Create one phony targets for each group and add them to the targetGroups.
+func genObjTidyPhonyTargets(ctx android.SingletonContext, module android.Module, objTidyName string, fileGroups map[string]android.Paths, targetGroups map[string]android.Path) {
+ for group, files := range fileGroups {
+ groupName := objTidyModuleGroupName(module, group, objTidyName)
+ ctx.Phony(groupName, files...)
+ targetGroups[group] = android.PathForPhony(ctx, groupName)
+ }
+}
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
deleted file mode 100644
index bda73ea..0000000
--- a/cc/toolchain_library.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cc
-
-import (
- "android/soong/android"
-)
-
-//
-// Device libraries shipped with gcc
-//
-
-func init() {
- android.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
-}
-
-type toolchainLibraryProperties struct {
- // the prebuilt toolchain library, as a path from the top of the source tree
- Src *string `android:"arch_variant"`
-
- // Repack the archive with only the selected objects.
- Repack_objects_to_keep []string `android:"arch_variant"`
-}
-
-type toolchainLibraryDecorator struct {
- *libraryDecorator
- stripper Stripper
-
- Properties toolchainLibraryProperties
-}
-
-func (*toolchainLibraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
- // toolchain libraries can't have any dependencies
- return deps
-}
-
-func (library *toolchainLibraryDecorator) linkerProps() []interface{} {
- var props []interface{}
- props = append(props, library.libraryDecorator.linkerProps()...)
- return append(props, &library.Properties, &library.stripper.StripProperties)
-}
-
-// toolchain_library is used internally by the build tool to link the specified
-// static library in src property to the device libraries that are shipped with
-// gcc.
-func ToolchainLibraryFactory() android.Module {
- module, library := NewLibrary(android.HostAndDeviceSupported)
- library.BuildOnlyStatic()
- toolchainLibrary := &toolchainLibraryDecorator{
- libraryDecorator: library,
- }
- module.compiler = toolchainLibrary
- module.linker = toolchainLibrary
- module.stl = nil
- module.sanitize = nil
- module.installer = nil
- module.library = toolchainLibrary
- module.Properties.Sdk_version = StringPtr("current")
- return module.Init()
-}
-
-func (library *toolchainLibraryDecorator) compile(ctx ModuleContext, flags Flags,
- deps PathDeps) Objects {
- return Objects{}
-}
-
-func (library *toolchainLibraryDecorator) link(ctx ModuleContext,
- flags Flags, deps PathDeps, objs Objects) android.Path {
-
- if library.Properties.Src == nil {
- ctx.PropertyErrorf("src", "No library source specified")
- return android.PathForSource(ctx, "")
- }
-
- srcPath := android.PathForSource(ctx, *library.Properties.Src)
- outputFile := android.Path(srcPath)
-
- if library.Properties.Repack_objects_to_keep != nil {
- fileName := ctx.ModuleName() + staticLibraryExtension
- repackedPath := android.PathForModuleOut(ctx, fileName)
- transformArchiveRepack(ctx, outputFile, repackedPath, library.Properties.Repack_objects_to_keep)
- outputFile = repackedPath
- }
-
- if library.stripper.StripProperties.Strip.Keep_symbols_list != nil {
- fileName := ctx.ModuleName() + staticLibraryExtension
- strippedPath := android.PathForModuleOut(ctx, fileName)
- stripFlags := flagsToStripFlags(flags)
- library.stripper.StripStaticLib(ctx, outputFile, strippedPath, stripFlags)
- outputFile = strippedPath
- }
-
- depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(outputFile).Build()
- ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
- StaticLibrary: outputFile,
-
- TransitiveStaticLibrariesForOrdering: depSet,
- })
-
- return outputFile
-}
-
-func (library *toolchainLibraryDecorator) nativeCoverage() bool {
- return false
-}
diff --git a/cc/util.go b/cc/util.go
index 88b0aba..b256b9a 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -85,6 +85,7 @@
toolchain: in.Toolchain,
gcovCoverage: in.GcovCoverage,
tidy: in.Tidy,
+ needTidyFiles: in.NeedTidyFiles,
sAbiDump: in.SAbiDump,
emitXrefs: in.EmitXrefs,
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index ca2f569..645b2cc 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -65,10 +65,13 @@
nocrt: true,
}
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libb",
vendor_available: true,
- src: "libb.a",
+ srcs: ["libb.a"],
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
}
cc_object {
@@ -1020,14 +1023,6 @@
assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
}
-func assertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
- t.Helper()
- m := ctx.ModuleForTests(name, recoveryVariant).Module().(*Module)
- if m.ExcludeFromRecoverySnapshot() != expected {
- t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
- }
-}
-
func TestVendorSnapshotExclude(t *testing.T) {
// This test verifies that the exclude_from_vendor_snapshot property
@@ -1230,10 +1225,13 @@
nocrt: true,
}
- toolchain_library {
+ cc_prebuilt_library_static {
name: "libb",
recovery_available: true,
- src: "libb.a",
+ srcs: ["libb.a"],
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
}
cc_object {
@@ -1371,13 +1369,13 @@
android.FailIfErrored(t, errs)
// Test an include and exclude framework module.
- assertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false)
- assertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true)
- assertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true)
+ AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false, recoveryVariant)
+ AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true, recoveryVariant)
+ AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true, recoveryVariant)
// A recovery module is excluded, but by its path, not the
// exclude_from_recovery_snapshot property.
- assertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false)
+ AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false, recoveryVariant)
// Verify the content of the recovery snapshot.
diff --git a/cc/vndk.go b/cc/vndk.go
index 1ae79de..c9c9f2c 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -495,7 +495,7 @@
filterOutFromMakeVar: filter,
}
m.AddProperties(&m.properties)
- android.InitAndroidModule(m)
+ android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
return m
}
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index da34f36..31b6d10 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -144,7 +144,6 @@
// current VNDK prebuilts are only shared libs.
in := p.singleSourcePath(ctx)
- builderFlags := flagsToBuilderFlags(flags)
p.unstrippedOutputFile = in
libName := in.Base()
if p.stripper.NeedsStrip(ctx) {
@@ -158,7 +157,7 @@
// depending on a table of contents file instead of the library itself.
tocFile := android.PathForModuleOut(ctx, libName+".toc")
p.tocFile = android.OptionalPathForPath(tocFile)
- transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
+ TransformSharedObjectToToc(ctx, in, tocFile)
p.androidMkSuffix = p.NameSuffix()
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
index 6e51a28..1cf64de 100644
--- a/cmd/extract_apks/main.go
+++ b/cmd/extract_apks/main.go
@@ -356,7 +356,7 @@
// Writes out selected entries, renaming them as needed
func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
- writer Zip2ZipWriter, partition string) ([]string, error) {
+ outFile io.Writer, zipWriter Zip2ZipWriter, partition string) ([]string, error) {
// Renaming rules:
// splits/MODULE-master.apk to STEM.apk
// else
@@ -406,8 +406,14 @@
origin, inName, outName)
}
entryOrigin[outName] = inName
- if err := writer.CopyFrom(apkFile, outName); err != nil {
- return nil, err
+ if outName == config.stem+".apk" {
+ if err := writeZipEntryToFile(outFile, apkFile); err != nil {
+ return nil, err
+ }
+ } else {
+ if err := zipWriter.CopyFrom(apkFile, outName); err != nil {
+ return nil, err
+ }
}
if partition != "" {
apkcerts = append(apkcerts, fmt.Sprintf(
@@ -426,14 +432,13 @@
if !ok {
return fmt.Errorf("Couldn't find apk path %s", selected.entries[0])
}
- inputReader, _ := apk.Open()
- _, err := io.Copy(outFile, inputReader)
- return err
+ return writeZipEntryToFile(outFile, apk)
}
// Arguments parsing
var (
- outputFile = flag.String("o", "", "output file containing extracted entries")
+ outputFile = flag.String("o", "", "output file for primary entry")
+ zipFile = flag.String("zip", "", "output file containing additional extracted entries")
targetConfig = TargetConfig{
screenDpi: map[android_bundle_proto.ScreenDensity_DensityAlias]bool{},
abis: map[android_bundle_proto.Abi_AbiAlias]int{},
@@ -494,7 +499,8 @@
func processArgs() {
flag.Usage = func() {
- fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> -sdk-version value -abis value `+
+ fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> [-zip <output-zip-file>] `+
+ `-sdk-version value -abis value `+
`-screen-densities value {-stem value | -extract-single} [-allow-prereleased] `+
`[-apkcerts <apkcerts output file> -partition <partition>] <APK set>`)
flag.PrintDefaults()
@@ -510,7 +516,8 @@
flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file")
flag.Parse()
if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 ||
- (targetConfig.stem == "" && !*extractSingle) || (*apkcertsOutput != "" && *partition == "") {
+ ((targetConfig.stem == "" || *zipFile == "") && !*extractSingle) ||
+ (*apkcertsOutput != "" && *partition == "") {
flag.Usage()
}
targetConfig.sdkVersion = int32(*version)
@@ -542,13 +549,20 @@
if *extractSingle {
err = apkSet.extractAndCopySingle(sel, outFile)
} else {
- writer := zip.NewWriter(outFile)
+ zipOutputFile, err := os.Create(*zipFile)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer zipOutputFile.Close()
+
+ zipWriter := zip.NewWriter(zipOutputFile)
defer func() {
- if err := writer.Close(); err != nil {
+ if err := zipWriter.Close(); err != nil {
log.Fatal(err)
}
}()
- apkcerts, err := apkSet.writeApks(sel, targetConfig, writer, *partition)
+
+ apkcerts, err := apkSet.writeApks(sel, targetConfig, outFile, zipWriter, *partition)
if err == nil && *apkcertsOutput != "" {
apkcertsFile, err := os.Create(*apkcertsOutput)
if err != nil {
@@ -567,3 +581,13 @@
log.Fatal(err)
}
}
+
+func writeZipEntryToFile(outFile io.Writer, zipEntry *zip.File) error {
+ reader, err := zipEntry.Open()
+ if err != nil {
+ return err
+ }
+ defer reader.Close()
+ _, err = io.Copy(outFile, reader)
+ return err
+}
diff --git a/cmd/extract_apks/main_test.go b/cmd/extract_apks/main_test.go
index 9fcf324..f5e4046 100644
--- a/cmd/extract_apks/main_test.go
+++ b/cmd/extract_apks/main_test.go
@@ -15,6 +15,7 @@
package main
import (
+ "bytes"
"fmt"
"reflect"
"testing"
@@ -437,8 +438,8 @@
stem string
partition string
// what we write from what
- expectedZipEntries map[string]string
- expectedApkcerts []string
+ zipEntries map[string]string
+ expectedApkcerts []string
}
func TestWriteApks(t *testing.T) {
@@ -448,7 +449,7 @@
moduleName: "mybase",
stem: "Foo",
partition: "system",
- expectedZipEntries: map[string]string{
+ zipEntries: map[string]string{
"Foo.apk": "splits/mybase-master.apk",
"Foo-xhdpi.apk": "splits/mybase-xhdpi.apk",
},
@@ -462,7 +463,7 @@
moduleName: "base",
stem: "Bar",
partition: "product",
- expectedZipEntries: map[string]string{
+ zipEntries: map[string]string{
"Bar.apk": "universal.apk",
},
expectedApkcerts: []string{
@@ -471,23 +472,46 @@
},
}
for _, testCase := range testCases {
- apkSet := ApkSet{entries: make(map[string]*zip.File)}
- sel := SelectionResult{moduleName: testCase.moduleName}
- for _, in := range testCase.expectedZipEntries {
- apkSet.entries[in] = &zip.File{FileHeader: zip.FileHeader{Name: in}}
- sel.entries = append(sel.entries, in)
- }
- writer := testZip2ZipWriter{make(map[string]string)}
- config := TargetConfig{stem: testCase.stem}
- apkcerts, err := apkSet.writeApks(sel, config, writer, testCase.partition)
- if err != nil {
- t.Error(err)
- }
- if !reflect.DeepEqual(testCase.expectedZipEntries, writer.entries) {
- t.Errorf("expected zip entries %v, got %v", testCase.expectedZipEntries, writer.entries)
- }
- if !reflect.DeepEqual(testCase.expectedApkcerts, apkcerts) {
- t.Errorf("expected apkcerts %v, got %v", testCase.expectedApkcerts, apkcerts)
- }
+ t.Run(testCase.name, func(t *testing.T) {
+ testZipBuf := &bytes.Buffer{}
+ testZip := zip.NewWriter(testZipBuf)
+ for _, in := range testCase.zipEntries {
+ f, _ := testZip.Create(in)
+ f.Write([]byte(in))
+ }
+ testZip.Close()
+
+ zipReader, _ := zip.NewReader(bytes.NewReader(testZipBuf.Bytes()), int64(testZipBuf.Len()))
+
+ apkSet := ApkSet{entries: make(map[string]*zip.File)}
+ sel := SelectionResult{moduleName: testCase.moduleName}
+ for _, f := range zipReader.File {
+ apkSet.entries[f.Name] = f
+ sel.entries = append(sel.entries, f.Name)
+ }
+
+ zipWriter := testZip2ZipWriter{make(map[string]string)}
+ outWriter := &bytes.Buffer{}
+ config := TargetConfig{stem: testCase.stem}
+ apkcerts, err := apkSet.writeApks(sel, config, outWriter, zipWriter, testCase.partition)
+ if err != nil {
+ t.Error(err)
+ }
+ expectedZipEntries := make(map[string]string)
+ for k, v := range testCase.zipEntries {
+ if k != testCase.stem+".apk" {
+ expectedZipEntries[k] = v
+ }
+ }
+ if !reflect.DeepEqual(expectedZipEntries, zipWriter.entries) {
+ t.Errorf("expected zip entries %v, got %v", testCase.zipEntries, zipWriter.entries)
+ }
+ if !reflect.DeepEqual(testCase.expectedApkcerts, apkcerts) {
+ t.Errorf("expected apkcerts %v, got %v", testCase.expectedApkcerts, apkcerts)
+ }
+ if g, w := outWriter.String(), testCase.zipEntries[testCase.stem+".apk"]; !reflect.DeepEqual(g, w) {
+ t.Errorf("expected output file contents %q, got %q", testCase.stem+".apk", outWriter.String())
+ }
+ })
}
}
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index 1280553..5603b41 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -114,6 +114,10 @@
fmt.Fprintln(asm, `.section .note.android.embedded_linker,"a",%note`)
+ // Discard the PT_INTERP section so that the linker doesn't need to be passed the
+ // --no-dynamic-linker flag.
+ fmt.Println(script, " /DISCARD/ : { *(.interp) }")
+
fmt.Fprintln(script, "}")
fmt.Fprintln(script, "INSERT BEFORE .note.android.embedded_linker;")
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
index e5be6c0..20ca2a3 100644
--- a/cmd/multiproduct_kati/Android.bp
+++ b/cmd/multiproduct_kati/Android.bp
@@ -31,4 +31,14 @@
testSrcs: [
"main_test.go",
],
+ linux: {
+ srcs: [
+ "main_linux.go",
+ ],
+ },
+ darwin: {
+ srcs: [
+ "main_darwin.go",
+ ],
+ },
}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 3c9cac1..7cb8ab7 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -166,15 +166,6 @@
MainLogsDir string
}
-func detectTotalRAM() uint64 {
- var info syscall.Sysinfo_t
- err := syscall.Sysinfo(&info)
- if err != nil {
- panic(err)
- }
- return info.Totalram * uint64(info.Unit)
-}
-
func findNamedProducts(soongUi string, log logger.Logger) []string {
cmd := exec.Command(soongUi, "--dumpvars-mode", "--vars=all_named_products")
output, err := cmd.Output()
@@ -301,7 +292,7 @@
jobs = runtime.NumCPU() / 4
ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
- if ramJobs := ramGb / 25; ramGb > 0 && jobs > ramJobs {
+ if ramJobs := ramGb / 30; ramGb > 0 && jobs > ramJobs {
jobs = ramJobs
}
@@ -416,10 +407,10 @@
s.Finish()
- if failures == 1 {
+ if failures.count == 1 {
log.Fatal("1 failure")
- } else if failures > 1 {
- log.Fatalf("%d failures", failures)
+ } else if failures.count > 1 {
+ log.Fatalf("%d failures %q", failures.count, failures.fails)
} else {
fmt.Fprintln(output, "Success")
}
@@ -531,19 +522,23 @@
})
}
-type failureCount int
+type failureCount struct {
+ count int
+ fails []string
+}
func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
if result.Error != nil {
- *f += 1
+ f.count += 1
+ f.fails = append(f.fails, result.Action.Description)
}
}
func (f *failureCount) Message(level status.MsgLevel, message string) {
if level >= status.ErrorLvl {
- *f += 1
+ f.count += 1
}
}
diff --git a/cmd/multiproduct_kati/main_darwin.go b/cmd/multiproduct_kati/main_darwin.go
new file mode 100644
index 0000000..3d1b12a
--- /dev/null
+++ b/cmd/multiproduct_kati/main_darwin.go
@@ -0,0 +1,20 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+func detectTotalRAM() uint64 {
+ // unimplemented stub on darwin
+ return 0
+}
diff --git a/cmd/multiproduct_kati/main_linux.go b/cmd/multiproduct_kati/main_linux.go
new file mode 100644
index 0000000..db74496
--- /dev/null
+++ b/cmd/multiproduct_kati/main_linux.go
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "syscall"
+)
+
+func detectTotalRAM() uint64 {
+ var info syscall.Sysinfo_t
+ err := syscall.Sysinfo(&info)
+ if err != nil {
+ panic(err)
+ }
+ return info.Totalram * uint64(info.Unit)
+}
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index c7f3f6a..4fa7486 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -164,7 +164,7 @@
if useSubDir {
localTempDir = filepath.Join(localTempDir, strconv.Itoa(i))
}
- depFile, err := runCommand(command, localTempDir)
+ depFile, err := runCommand(command, localTempDir, i)
if err != nil {
// Running the command failed, keep the temporary output directory around in
// case a user wants to inspect it for debugging purposes. Soong will delete
@@ -194,6 +194,28 @@
return nil
}
+// createCommandScript will create and return an exec.Cmd that runs rawCommand.
+//
+// rawCommand is executed via a script in the sandbox.
+// tempDir is the temporary where the script is created.
+// toDirInSandBox is the path containing the script in the sbox environment.
+// toDirInSandBox is the path containing the script in the sbox environment.
+// seed is a unique integer used to distinguish different scripts that might be at location.
+//
+// returns an exec.Cmd that can be ran from within sbox context if no error, or nil if error.
+// caller must ensure script is cleaned up if function succeeds.
+//
+func createCommandScript(rawCommand string, tempDir, toDirInSandbox string, seed int) (*exec.Cmd, error) {
+ scriptName := fmt.Sprintf("sbox_command.%d.bash", seed)
+ scriptPathAndName := joinPath(tempDir, scriptName)
+ err := os.WriteFile(scriptPathAndName, []byte(rawCommand), 0644)
+ if err != nil {
+ return nil, fmt.Errorf("failed to write command %s... to %s",
+ rawCommand[0:40], scriptPathAndName)
+ }
+ return exec.Command("bash", joinPath(toDirInSandbox, filepath.Base(scriptName))), nil
+}
+
// readManifest reads an sbox manifest from a textproto file.
func readManifest(file string) (*sbox_proto.Manifest, error) {
manifestData, err := ioutil.ReadFile(file)
@@ -213,7 +235,7 @@
// runCommand runs a single command from a manifest. If the command references the
// __SBOX_DEPFILE__ placeholder it returns the name of the depfile that was used.
-func runCommand(command *sbox_proto.Command, tempDir string) (depFile string, err error) {
+func runCommand(command *sbox_proto.Command, tempDir string, commandIndex int) (depFile string, err error) {
rawCommand := command.GetCommand()
if rawCommand == "" {
return "", fmt.Errorf("command is required")
@@ -255,7 +277,11 @@
return "", err
}
- cmd := exec.Command("bash", "-c", rawCommand)
+ cmd, err := createCommandScript(rawCommand, tempDir, pathToTempDirInSbox, commandIndex)
+ if err != nil {
+ return "", err
+ }
+
buf := &bytes.Buffer{}
cmd.Stdin = os.Stdin
cmd.Stdout = buf
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index c5e8896..b3a6ee0 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -48,6 +48,7 @@
delvePath string
moduleGraphFile string
+ moduleActionsFile string
docFile string
bazelQueryViewDir string
bp2buildMarker string
@@ -76,6 +77,7 @@
// 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")
@@ -176,15 +178,17 @@
}
}
-func writeJsonModuleGraph(ctx *android.Context, path string) {
- f, err := os.Create(shared.JoinPath(topDir, path))
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s", err)
+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)
}
- defer f.Close()
- ctx.Context.PrintJSONGraph(f)
+ defer graphFile.Close()
+ defer actionsFile.Close()
+ ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile)
}
func writeBuildGlobsNinjaFile(srcDir, buildDir string, globs func() pathtools.MultipleGlobResults, config interface{}) []string {
@@ -217,15 +221,6 @@
generateModuleGraphFile := moduleGraphFile != ""
generateDocFile := docFile != ""
- blueprintArgs := cmdlineArgs
-
- var stopBefore bootstrap.StopBefore
- if !generateModuleGraphFile && !generateQueryView && !generateDocFile {
- stopBefore = bootstrap.DoEverything
- } else {
- stopBefore = bootstrap.StopBeforePrepareBuildActions
- }
-
if generateBazelWorkspace {
// Run the alternate pipeline of bp2build mutators and singleton to convert
// Blueprint to BUILD files before everything else.
@@ -233,6 +228,19 @@
return bp2buildMarker
}
+ blueprintArgs := cmdlineArgs
+
+ var stopBefore bootstrap.StopBefore
+ if generateModuleGraphFile {
+ stopBefore = bootstrap.StopBeforeWriteNinja
+ } else if generateQueryView {
+ stopBefore = bootstrap.StopBeforePrepareBuildActions
+ } else if generateDocFile {
+ stopBefore = bootstrap.StopBeforePrepareBuildActions
+ } else {
+ stopBefore = bootstrap.DoEverything
+ }
+
ctx := newContext(configuration)
if mixedModeBuild {
runMixedModeBuild(configuration, ctx, extraNinjaDeps)
@@ -250,7 +258,7 @@
writeDepFile(queryviewMarkerFile, ninjaDeps)
return queryviewMarkerFile
} else if generateModuleGraphFile {
- writeJsonModuleGraph(ctx, moduleGraphFile)
+ writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
writeDepFile(moduleGraphFile, ninjaDeps)
return moduleGraphFile
} else if generateDocFile {
@@ -382,7 +390,7 @@
// - 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) []string {
+func getPathsToIgnoredBuildFiles(topDir string, generatedRoot string, srcDirBazelFiles []string, verbose bool) []string {
paths := make([]string, 0)
for _, srcDirBazelFileRelativePath := range srcDirBazelFiles {
@@ -412,7 +420,9 @@
// BUILD file clash resolution happens later in the symlink forest creation
continue
}
- fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath)
+ if verbose {
+ fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath)
+ }
paths = append(paths, srcDirBazelFileRelativePath)
}
@@ -460,6 +470,11 @@
// conversion for Bazel conversion.
bp2buildCtx := android.NewContext(configuration)
+ // 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()
+
// Propagate "allow misssing dependencies" bit. This is normally set in
// newContext(), but we create bp2buildCtx without calling that method.
bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
@@ -514,7 +529,7 @@
os.Exit(1)
}
- pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles)
+ pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
excludes = append(excludes, pathsToIgnoredBuildFiles...)
excludes = append(excludes, getTemporaryExcludes()...)
@@ -526,6 +541,7 @@
// for queryview, since that's a total repo-wide conversion and there's a
// 1:1 mapping for each module.
metrics.Print()
+ writeBp2BuildMetrics(&metrics, configuration)
ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
@@ -535,3 +551,13 @@
// Create an empty bp2build marker file.
touch(shared.JoinPath(topDir, bp2buildMarker))
}
+
+// Write Bp2Build metrics into $LOG_DIR
+func writeBp2BuildMetrics(metrics *bp2build.CodegenMetrics, configuration android.Config) {
+ metricsDir := configuration.Getenv("LOG_DIR")
+ if len(metricsDir) < 1 {
+ fmt.Fprintf(os.Stderr, "\nMissing required env var for generating bp2build metrics: LOG_DIR\n")
+ os.Exit(1)
+ }
+ metrics.Write(metricsDir)
+}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 9ee373e..a0cfbea 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -16,7 +16,6 @@
import (
"context"
- "encoding/json"
"flag"
"fmt"
"io/ioutil"
@@ -24,6 +23,7 @@
"path/filepath"
"strconv"
"strings"
+ "syscall"
"time"
"android/soong/shared"
@@ -36,11 +36,6 @@
"android/soong/ui/tracer"
)
-const (
- configDir = "vendor/google/tools/soong_config"
- jsonSuffix = "json"
-)
-
// A command represents an operation to be executed in the soong build
// system.
type command struct {
@@ -67,7 +62,7 @@
}
// list of supported commands (flags) supported by soong ui
-var commands []command = []command{
+var commands = []command{
{
flag: "--make-mode",
description: "build the modules by the target name (i.e. soong_docs)",
@@ -117,34 +112,6 @@
return indexList(s, list) != -1
}
-func loadEnvConfig() error {
- bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
- if bc == "" {
- return nil
- }
- cfgFile := filepath.Join(os.Getenv("TOP"), configDir, fmt.Sprintf("%s.%s", bc, jsonSuffix))
-
- envVarsJSON, err := ioutil.ReadFile(cfgFile)
- if err != nil {
- fmt.Fprintf(os.Stderr, "\033[33mWARNING:\033[0m failed to open config file %s: %s\n", cfgFile, err.Error())
- return nil
- }
-
- var envVars map[string]map[string]string
- if err := json.Unmarshal(envVarsJSON, &envVars); err != nil {
- return fmt.Errorf("env vars config file: %s did not parse correctly: %s", cfgFile, err.Error())
- }
- for k, v := range envVars["env"] {
- if os.Getenv(k) != "" {
- continue
- }
- if err := os.Setenv(k, v); err != nil {
- return err
- }
- }
- return nil
-}
-
// Main execution of soong_ui. The command format is as follows:
//
// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
@@ -207,11 +174,6 @@
Status: stat,
}}
- if err := loadEnvConfig(); err != nil {
- fmt.Fprintf(os.Stderr, "failed to parse env config files: %v", err)
- os.Exit(1)
- }
-
config := c.config(buildCtx, args...)
build.SetupOutDir(buildCtx, config)
@@ -243,6 +205,8 @@
buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
+ setMaxFiles(buildCtx)
+
{
// The order of the function calls is important. The last defer function call
// is the first one that is executed to save the rbe metrics to a protobuf
@@ -562,7 +526,11 @@
}
// command not found
- return nil, nil, fmt.Errorf("Command not found: %q", args)
+ 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)
}
// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
@@ -639,3 +607,24 @@
}
}
}
+
+func setMaxFiles(ctx build.Context) {
+ var limits syscall.Rlimit
+
+ err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
+ if err != nil {
+ ctx.Println("Failed to get file limit:", err)
+ return
+ }
+
+ ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
+ if limits.Cur == limits.Max {
+ return
+ }
+
+ limits.Cur = limits.Max
+ err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
+ if err != nil {
+ ctx.Println("Failed to increase file limit:", err)
+ }
+}
diff --git a/compliance/build_license_metadata/Android.bp b/compliance/build_license_metadata/Android.bp
new file mode 100644
index 0000000..4826526
--- /dev/null
+++ b/compliance/build_license_metadata/Android.bp
@@ -0,0 +1,30 @@
+// 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"],
+}
+
+blueprint_go_binary {
+ name: "build_license_metadata",
+ srcs: [
+ "build_license_metadata.go",
+ ],
+ deps: [
+ "license_metadata_proto",
+ "golang-protobuf-proto",
+ "golang-protobuf-encoding-prototext",
+ "soong-response",
+ ],
+}
diff --git a/compliance/build_license_metadata/build_license_metadata.go b/compliance/build_license_metadata/build_license_metadata.go
new file mode 100644
index 0000000..53d2407
--- /dev/null
+++ b/compliance/build_license_metadata/build_license_metadata.go
@@ -0,0 +1,196 @@
+// 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 main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/proto"
+
+ "android/soong/compliance/license_metadata_proto"
+ "android/soong/response"
+)
+
+func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
+ var f multiString
+ flags.Var(&f, name, usage)
+ return &f
+}
+
+type multiString []string
+
+func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+ var expandedArgs []string
+ for _, arg := range os.Args[1:] {
+ if strings.HasPrefix(arg, "@") {
+ f, err := os.Open(strings.TrimPrefix(arg, "@"))
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+
+ respArgs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ expandedArgs = append(expandedArgs, respArgs...)
+ } else {
+ expandedArgs = append(expandedArgs, arg)
+ }
+ }
+
+ flags := flag.NewFlagSet("flags", flag.ExitOnError)
+
+ packageName := flags.String("p", "", "license package name")
+ moduleType := newMultiString(flags, "mt", "module type")
+ kinds := newMultiString(flags, "k", "license kinds")
+ moduleClass := newMultiString(flags, "mc", "module class")
+ conditions := newMultiString(flags, "c", "license conditions")
+ notices := newMultiString(flags, "n", "license notice file")
+ deps := newMultiString(flags, "d", "license metadata file dependency")
+ sources := newMultiString(flags, "s", "source (input) dependency")
+ built := newMultiString(flags, "t", "built targets")
+ installed := newMultiString(flags, "i", "installed targets")
+ roots := newMultiString(flags, "r", "root directory of project")
+ installedMap := newMultiString(flags, "m", "map dependent targets to their installed names")
+ isContainer := flags.Bool("is_container", false, "preserved dependent target name when given")
+ outFile := flags.String("o", "", "output file")
+
+ flags.Parse(expandedArgs)
+
+ metadata := license_metadata_proto.LicenseMetadata{}
+ metadata.PackageName = proto.String(*packageName)
+ metadata.ModuleTypes = *moduleType
+ metadata.ModuleClasses = *moduleClass
+ metadata.IsContainer = proto.Bool(*isContainer)
+ metadata.Projects = findGitRoots(*roots)
+ metadata.LicenseKinds = *kinds
+ metadata.LicenseConditions = *conditions
+ metadata.LicenseTexts = *notices
+ metadata.Built = *built
+ metadata.Installed = *installed
+ metadata.InstallMap = convertInstalledMap(*installedMap)
+ metadata.Sources = *sources
+ metadata.Deps = convertDependencies(*deps)
+
+ err := writeMetadata(*outFile, &metadata)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
+ os.Exit(2)
+ }
+}
+
+func findGitRoots(dirs []string) []string {
+ ret := make([]string, len(dirs))
+ for i, dir := range dirs {
+ ret[i] = findGitRoot(dir)
+ }
+ return ret
+}
+
+// findGitRoot finds the directory at or above dir that contains a ".git" directory. This isn't
+// guaranteed to exist, for example during remote execution, when sandboxed, when building from
+// infrastructure that doesn't use git, or when the .git directory has been removed to save space,
+// but it should be good enough for local builds. If no .git directory is found the original value
+// is returned.
+func findGitRoot(dir string) string {
+ orig := dir
+ for dir != "" && dir != "." && dir != "/" {
+ _, err := os.Stat(filepath.Join(dir, ".git"))
+ if err == nil {
+ // Found dir/.git, return dir.
+ return dir
+ } else if !os.IsNotExist(err) {
+ // Error finding .git, return original input.
+ return orig
+ }
+ dir, _ = filepath.Split(dir)
+ dir = strings.TrimSuffix(dir, "/")
+ }
+ return orig
+}
+
+// convertInstalledMap converts a list of colon-separated from:to pairs into InstallMap proto
+// messages.
+func convertInstalledMap(installMaps []string) []*license_metadata_proto.InstallMap {
+ var ret []*license_metadata_proto.InstallMap
+
+ for _, installMap := range installMaps {
+ components := strings.Split(installMap, ":")
+ if len(components) != 2 {
+ panic(fmt.Errorf("install map entry %q contains %d colons, expected 1", installMap, len(components)-1))
+ }
+ ret = append(ret, &license_metadata_proto.InstallMap{
+ FromPath: proto.String(components[0]),
+ ContainerPath: proto.String(components[1]),
+ })
+ }
+
+ return ret
+}
+
+// convertDependencies converts a colon-separated tuple of dependency:annotation:annotation...
+// into AnnotatedDependency proto messages.
+func convertDependencies(deps []string) []*license_metadata_proto.AnnotatedDependency {
+ var ret []*license_metadata_proto.AnnotatedDependency
+
+ for _, d := range deps {
+ components := strings.Split(d, ":")
+ dep := components[0]
+ components = components[1:]
+ ad := &license_metadata_proto.AnnotatedDependency{
+ File: proto.String(dep),
+ Annotations: make([]string, 0, len(components)),
+ }
+ for _, ann := range components {
+ if len(ann) == 0 {
+ continue
+ }
+ ad.Annotations = append(ad.Annotations, ann)
+ }
+ ret = append(ret, ad)
+ }
+
+ return ret
+}
+
+func writeMetadata(file string, metadata *license_metadata_proto.LicenseMetadata) error {
+ buf, err := prototext.MarshalOptions{Multiline: true}.Marshal(metadata)
+ if err != nil {
+ return fmt.Errorf("error marshalling textproto: %w", err)
+ }
+
+ if file != "" {
+ err = ioutil.WriteFile(file, buf, 0666)
+ if err != nil {
+ return fmt.Errorf("error writing textproto %q: %w", file, err)
+ }
+ } else {
+ _, _ = os.Stdout.Write(buf)
+ }
+
+ return nil
+}
diff --git a/compliance/license_metadata_proto/Android.bp b/compliance/license_metadata_proto/Android.bp
new file mode 100644
index 0000000..3c041e4
--- /dev/null
+++ b/compliance/license_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: "license_metadata_proto",
+ pkgPath: "android/soong/compliance/license_metadata_proto",
+ srcs: ["license_metadata.pb.go"],
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+}
diff --git a/compliance/license_metadata_proto/license_metadata.pb.go b/compliance/license_metadata_proto/license_metadata.pb.go
new file mode 100644
index 0000000..44dbc78
--- /dev/null
+++ b/compliance/license_metadata_proto/license_metadata.pb.go
@@ -0,0 +1,451 @@
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.19.0
+// source: license_metadata.proto
+
+package license_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)
+)
+
+type LicenseMetadata struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ 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"`
+ 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.
+ Projects []string `protobuf:"bytes,4,rep,name=projects" json:"projects,omitempty"`
+ // license_kinds lists the kinds of licenses. e.g. SPDX-license-identifier-Apache-2.0 or legacy_notice.
+ LicenseKinds []string `protobuf:"bytes,5,rep,name=license_kinds,json=licenseKinds" json:"license_kinds,omitempty"`
+ // license_conditions lists the conditions that apply to the license kinds. e.g. notice or restricted.
+ LicenseConditions []string `protobuf:"bytes,6,rep,name=license_conditions,json=licenseConditions" json:"license_conditions,omitempty"`
+ // license_texts lists the filenames of the associated license text(s).
+ LicenseTexts []string `protobuf:"bytes,7,rep,name=license_texts,json=licenseTexts" json:"license_texts,omitempty"`
+ // is_container is true for target types that merely aggregate. e.g. .img or .zip files.
+ IsContainer *bool `protobuf:"varint,8,opt,name=is_container,json=isContainer" json:"is_container,omitempty"`
+ // built lists the built targets.
+ Built []string `protobuf:"bytes,9,rep,name=built" json:"built,omitempty"`
+ // installed lists the installed targets.
+ Installed []string `protobuf:"bytes,10,rep,name=installed" json:"installed,omitempty"`
+ // installMap identifies the substitutions to make to path names when moving into installed location.
+ InstallMap []*InstallMap `protobuf:"bytes,11,rep,name=install_map,json=installMap" json:"install_map,omitempty"`
+ // sources lists the targets depended on.
+ Sources []string `protobuf:"bytes,12,rep,name=sources" json:"sources,omitempty"`
+ // deps lists the license metadata files depended on.
+ Deps []*AnnotatedDependency `protobuf:"bytes,13,rep,name=deps" json:"deps,omitempty"`
+}
+
+func (x *LicenseMetadata) Reset() {
+ *x = LicenseMetadata{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_license_metadata_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *LicenseMetadata) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LicenseMetadata) ProtoMessage() {}
+
+func (x *LicenseMetadata) ProtoReflect() protoreflect.Message {
+ mi := &file_license_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 LicenseMetadata.ProtoReflect.Descriptor instead.
+func (*LicenseMetadata) Descriptor() ([]byte, []int) {
+ return file_license_metadata_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *LicenseMetadata) GetPackageName() string {
+ if x != nil && x.PackageName != nil {
+ return *x.PackageName
+ }
+ return ""
+}
+
+func (x *LicenseMetadata) GetModuleTypes() []string {
+ if x != nil {
+ return x.ModuleTypes
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetModuleClasses() []string {
+ if x != nil {
+ return x.ModuleClasses
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetProjects() []string {
+ if x != nil {
+ return x.Projects
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetLicenseKinds() []string {
+ if x != nil {
+ return x.LicenseKinds
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetLicenseConditions() []string {
+ if x != nil {
+ return x.LicenseConditions
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetLicenseTexts() []string {
+ if x != nil {
+ return x.LicenseTexts
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetIsContainer() bool {
+ if x != nil && x.IsContainer != nil {
+ return *x.IsContainer
+ }
+ return false
+}
+
+func (x *LicenseMetadata) GetBuilt() []string {
+ if x != nil {
+ return x.Built
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetInstalled() []string {
+ if x != nil {
+ return x.Installed
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetInstallMap() []*InstallMap {
+ if x != nil {
+ return x.InstallMap
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetSources() []string {
+ if x != nil {
+ return x.Sources
+ }
+ return nil
+}
+
+func (x *LicenseMetadata) GetDeps() []*AnnotatedDependency {
+ if x != nil {
+ return x.Deps
+ }
+ return nil
+}
+
+// InstallMap messages describe the mapping from an input filesystem file to the path to the file
+// in a container.
+type InstallMap struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The input path on the filesystem.
+ FromPath *string `protobuf:"bytes,1,opt,name=from_path,json=fromPath" json:"from_path,omitempty"`
+ // The path to the file inside the container or installed location.
+ ContainerPath *string `protobuf:"bytes,2,opt,name=container_path,json=containerPath" json:"container_path,omitempty"`
+}
+
+func (x *InstallMap) Reset() {
+ *x = InstallMap{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_license_metadata_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *InstallMap) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*InstallMap) ProtoMessage() {}
+
+func (x *InstallMap) ProtoReflect() protoreflect.Message {
+ mi := &file_license_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 InstallMap.ProtoReflect.Descriptor instead.
+func (*InstallMap) Descriptor() ([]byte, []int) {
+ return file_license_metadata_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *InstallMap) GetFromPath() string {
+ if x != nil && x.FromPath != nil {
+ return *x.FromPath
+ }
+ return ""
+}
+
+func (x *InstallMap) GetContainerPath() string {
+ if x != nil && x.ContainerPath != nil {
+ return *x.ContainerPath
+ }
+ return ""
+}
+
+// AnnotateDepencency messages describe edges in the build graph.
+type AnnotatedDependency struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The path to the dependency license metadata file.
+ File *string `protobuf:"bytes,1,opt,name=file" json:"file,omitempty"`
+ // The annotations attached to the dependency.
+ Annotations []string `protobuf:"bytes,2,rep,name=annotations" json:"annotations,omitempty"`
+}
+
+func (x *AnnotatedDependency) Reset() {
+ *x = AnnotatedDependency{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_license_metadata_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *AnnotatedDependency) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AnnotatedDependency) ProtoMessage() {}
+
+func (x *AnnotatedDependency) ProtoReflect() protoreflect.Message {
+ mi := &file_license_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 AnnotatedDependency.ProtoReflect.Descriptor instead.
+func (*AnnotatedDependency) Descriptor() ([]byte, []int) {
+ return file_license_metadata_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *AnnotatedDependency) GetFile() string {
+ if x != nil && x.File != nil {
+ return *x.File
+ }
+ return ""
+}
+
+func (x *AnnotatedDependency) GetAnnotations() []string {
+ if x != nil {
+ return x.Annotations
+ }
+ return nil
+}
+
+var File_license_metadata_proto protoreflect.FileDescriptor
+
+var file_license_metadata_proto_rawDesc = []byte{
+ 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,
+ 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,
+}
+
+var (
+ file_license_metadata_proto_rawDescOnce sync.Once
+ file_license_metadata_proto_rawDescData = file_license_metadata_proto_rawDesc
+)
+
+func file_license_metadata_proto_rawDescGZIP() []byte {
+ file_license_metadata_proto_rawDescOnce.Do(func() {
+ file_license_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_license_metadata_proto_rawDescData)
+ })
+ return file_license_metadata_proto_rawDescData
+}
+
+var file_license_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_license_metadata_proto_goTypes = []interface{}{
+ (*LicenseMetadata)(nil), // 0: build_license_metadata.LicenseMetadata
+ (*InstallMap)(nil), // 1: build_license_metadata.InstallMap
+ (*AnnotatedDependency)(nil), // 2: build_license_metadata.AnnotatedDependency
+}
+var file_license_metadata_proto_depIdxs = []int32{
+ 1, // 0: build_license_metadata.LicenseMetadata.install_map:type_name -> build_license_metadata.InstallMap
+ 2, // 1: build_license_metadata.LicenseMetadata.deps:type_name -> build_license_metadata.AnnotatedDependency
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_license_metadata_proto_init() }
+func file_license_metadata_proto_init() {
+ if File_license_metadata_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_license_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*LicenseMetadata); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_license_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*InstallMap); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_license_metadata_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*AnnotatedDependency); 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_license_metadata_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 3,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_license_metadata_proto_goTypes,
+ DependencyIndexes: file_license_metadata_proto_depIdxs,
+ MessageInfos: file_license_metadata_proto_msgTypes,
+ }.Build()
+ File_license_metadata_proto = out.File
+ file_license_metadata_proto_rawDesc = nil
+ file_license_metadata_proto_goTypes = nil
+ file_license_metadata_proto_depIdxs = nil
+}
diff --git a/compliance/license_metadata_proto/license_metadata.proto b/compliance/license_metadata_proto/license_metadata.proto
new file mode 100644
index 0000000..1b4f34f
--- /dev/null
+++ b/compliance/license_metadata_proto/license_metadata.proto
@@ -0,0 +1,76 @@
+// 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.
+
+syntax = "proto2";
+
+package build_license_metadata;
+option go_package = "android/soong/compliance/license_metadata_proto";
+
+message LicenseMetadata {
+ // package_name identifies the source package. License texts are named relative to the package name.
+ optional string package_name = 1;
+
+ repeated string module_types = 2;
+
+ repeated string module_classes = 3;
+
+ // projects identifies the git project(s) containing the associated source code.
+ repeated string projects = 4;
+
+ // license_kinds lists the kinds of licenses. e.g. SPDX-license-identifier-Apache-2.0 or legacy_notice.
+ repeated string license_kinds = 5;
+
+ // license_conditions lists the conditions that apply to the license kinds. e.g. notice or restricted.
+ repeated string license_conditions = 6;
+
+ // license_texts lists the filenames of the associated license text(s).
+ repeated string license_texts = 7;
+
+ // is_container is true for target types that merely aggregate. e.g. .img or .zip files.
+ optional bool is_container = 8;
+
+ // built lists the built targets.
+ repeated string built = 9;
+
+ // installed lists the installed targets.
+ repeated string installed = 10;
+
+ // installMap identifies the substitutions to make to path names when moving into installed location.
+ repeated InstallMap install_map = 11;
+
+ // sources lists the targets depended on.
+ repeated string sources = 12;
+
+ // deps lists the license metadata files depended on.
+ repeated AnnotatedDependency deps = 13;
+}
+
+// InstallMap messages describe the mapping from an input filesystem file to the path to the file
+// in a container.
+message InstallMap {
+ // The input path on the filesystem.
+ optional string from_path = 1;
+
+ // The path to the file inside the container or installed location.
+ optional string container_path = 2;
+}
+
+// AnnotateDepencency messages describe edges in the build graph.
+message AnnotatedDependency {
+ // The path to the dependency license metadata file.
+ optional string file = 1;
+
+ // The annotations attached to the dependency.
+ repeated string annotations = 2;
+}
diff --git a/cuj/run_cuj_tests.sh b/cuj/run_cuj_tests.sh
index b4f9f88..a746bd5 100755
--- a/cuj/run_cuj_tests.sh
+++ b/cuj/run_cuj_tests.sh
@@ -18,11 +18,10 @@
cd "$ANDROID_TOP"
export OUT_DIR="${OUT_DIR:-out}"
-readonly SOONG_OUT="${OUT_DIR}/soong"
-build/soong/soong_ui.bash --make-mode "${SOONG_OUT}/host/${OS}-x86/bin/cuj_tests"
+build/soong/soong_ui.bash --make-mode "${OUT_DIR}/host/${OS}-x86/bin/cuj_tests"
-"${SOONG_OUT}/host/${OS}-x86/bin/cuj_tests" || true
+"${OUT_DIR}/host/${OS}-x86/bin/cuj_tests" || true
if [ -n "${DIST_DIR}" ]; then
cp -r "${OUT_DIR}/cuj_tests/logs" "${DIST_DIR}"
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 1bdd040..36513b6 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -210,6 +210,34 @@
Subcontexts []*ClassLoaderContext
}
+// excludeLibs excludes the libraries from this ClassLoaderContext.
+//
+// This treats the supplied context as being immutable (as it may come from a dependency). So, it
+// implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
+// within this context then this will return a deep copy of this without those libraries.
+//
+// If this ClassLoaderContext matches one of the libraries to exclude then this returns (nil, true)
+// to indicate that this context should be excluded from the containing list.
+//
+// If any of this ClassLoaderContext's Subcontexts reference the excluded libraries then this
+// returns a pointer to a copy of this without the excluded libraries and true to indicate that this
+// was copied.
+//
+// Otherwise, this returns a pointer to this and false to indicate that this was not copied.
+func (c *ClassLoaderContext) excludeLibs(excludedLibs []string) (*ClassLoaderContext, bool) {
+ if android.InList(c.Name, excludedLibs) {
+ return nil, true
+ }
+
+ if excludedList, modified := excludeLibsFromCLCList(c.Subcontexts, excludedLibs); modified {
+ clcCopy := *c
+ clcCopy.Subcontexts = excludedList
+ return &clcCopy, true
+ }
+
+ return c, false
+}
+
// ClassLoaderContextMap is a map from SDK version to CLC. There is a special entry with key
// AnySdkVersion that stores unconditional CLC that is added regardless of the target SDK version.
//
@@ -408,6 +436,67 @@
return string(bytes)
}
+// excludeLibsFromCLCList excludes the libraries from the ClassLoaderContext in this list.
+//
+// This treats the supplied list as being immutable (as it may come from a dependency). So, it
+// implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
+// within the contexts in the list then this will return a deep copy of the list without those
+// libraries.
+//
+// If any of the ClassLoaderContext in the list reference the excluded libraries then this returns a
+// copy of this list without the excluded libraries and true to indicate that this was copied.
+//
+// Otherwise, this returns the list and false to indicate that this was not copied.
+func excludeLibsFromCLCList(clcList []*ClassLoaderContext, excludedLibs []string) ([]*ClassLoaderContext, bool) {
+ modifiedList := false
+ copiedList := make([]*ClassLoaderContext, 0, len(clcList))
+ for _, clc := range clcList {
+ resultClc, modifiedClc := clc.excludeLibs(excludedLibs)
+ if resultClc != nil {
+ copiedList = append(copiedList, resultClc)
+ }
+ modifiedList = modifiedList || modifiedClc
+ }
+
+ if modifiedList {
+ return copiedList, true
+ } else {
+ return clcList, false
+ }
+}
+
+// ExcludeLibs excludes the libraries from the ClassLoaderContextMap.
+//
+// If the list o libraries is empty then this returns the ClassLoaderContextMap.
+//
+// This treats the ClassLoaderContextMap as being immutable (as it may come from a dependency). So,
+// it implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
+// within the contexts in the map then this will return a deep copy of the map without those
+// libraries.
+//
+// Otherwise, this returns the map unchanged.
+func (clcMap ClassLoaderContextMap) ExcludeLibs(excludedLibs []string) ClassLoaderContextMap {
+ if len(excludedLibs) == 0 {
+ return clcMap
+ }
+
+ excludedClcMap := make(ClassLoaderContextMap)
+ modifiedMap := false
+ for sdkVersion, clcList := range clcMap {
+ excludedList, modifiedList := excludeLibsFromCLCList(clcList, excludedLibs)
+ if len(excludedList) != 0 {
+ excludedClcMap[sdkVersion] = excludedList
+ }
+ modifiedMap = modifiedMap || modifiedList
+ }
+
+ if modifiedMap {
+ return excludedClcMap
+ } else {
+ return clcMap
+ }
+}
+
// Now that the full unconditional context is known, reconstruct conditional context.
// Apply filters for individual libraries, mirroring what the PackageManager does when it
// constructs class loader context on device.
@@ -598,11 +687,18 @@
func toJsonClassLoaderContextRec(clcs []*ClassLoaderContext) []*jsonClassLoaderContext {
jClcs := make([]*jsonClassLoaderContext, len(clcs))
for i, clc := range clcs {
+ var host string
+ if clc.Host == nil {
+ // Defer build failure to when this CLC is actually used.
+ host = fmt.Sprintf("implementation-jar-for-%s-is-not-available.jar", clc.Name)
+ } else {
+ host = clc.Host.String()
+ }
jClcs[i] = &jsonClassLoaderContext{
Name: clc.Name,
Optional: clc.Optional,
Implicit: clc.Implicit,
- Host: clc.Host.String(),
+ 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 d81ac2c..5d3a9d9 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -115,16 +115,16 @@
// Test that class loader context structure is correct.
t.Run("string", func(t *testing.T) {
wantStr := " --host-context-for-sdk 29 " +
- "PCL[out/" + AndroidHidlManager + ".jar]#" +
- "PCL[out/" + AndroidHidlBase + ".jar]" +
+ "PCL[out/soong/" + AndroidHidlManager + ".jar]#" +
+ "PCL[out/soong/" + AndroidHidlBase + ".jar]" +
" --target-context-for-sdk 29 " +
"PCL[/system/framework/" + AndroidHidlManager + ".jar]#" +
"PCL[/system/framework/" + AndroidHidlBase + ".jar]" +
" --host-context-for-sdk any " +
- "PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]" +
- "{PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]" +
- "{PCL[out/a1.jar]#PCL[out/b1.jar]}}#" +
- "PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" +
+ "PCL[out/soong/a.jar]#PCL[out/soong/b.jar]#PCL[out/soong/c.jar]#PCL[out/soong/d.jar]" +
+ "{PCL[out/soong/a2.jar]#PCL[out/soong/b2.jar]#PCL[out/soong/c2.jar]" +
+ "{PCL[out/soong/a1.jar]#PCL[out/soong/b1.jar]}}#" +
+ "PCL[out/soong/f.jar]#PCL[out/soong/a3.jar]#PCL[out/soong/b3.jar]" +
" --target-context-for-sdk any " +
"PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]" +
"{PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]" +
@@ -138,11 +138,11 @@
// Test that all expected build paths are gathered.
t.Run("paths", func(t *testing.T) {
wantPaths := []string{
- "out/android.hidl.manager-V1.0-java.jar", "out/android.hidl.base-V1.0-java.jar",
- "out/a.jar", "out/b.jar", "out/c.jar", "out/d.jar",
- "out/a2.jar", "out/b2.jar", "out/c2.jar",
- "out/a1.jar", "out/b1.jar",
- "out/f.jar", "out/a3.jar", "out/b3.jar",
+ "out/soong/android.hidl.manager-V1.0-java.jar", "out/soong/android.hidl.base-V1.0-java.jar",
+ "out/soong/a.jar", "out/soong/b.jar", "out/soong/c.jar", "out/soong/d.jar",
+ "out/soong/a2.jar", "out/soong/b2.jar", "out/soong/c2.jar",
+ "out/soong/a1.jar", "out/soong/b1.jar",
+ "out/soong/f.jar", "out/soong/a3.jar", "out/soong/b3.jar",
}
if !reflect.DeepEqual(wantPaths, havePaths.Strings()) {
t.Errorf("\nwant paths: %s\nhave paths: %s", wantPaths, havePaths)
@@ -270,13 +270,13 @@
// Test that class loader context structure is correct.
t.Run("string", func(t *testing.T) {
- wantStr := " --host-context-for-sdk 30 PCL[out/c.jar]" +
+ wantStr := " --host-context-for-sdk 30 PCL[out/soong/c.jar]" +
" --target-context-for-sdk 30 PCL[/system/c.jar]" +
- " --host-context-for-sdk 29 PCL[out/b.jar]" +
+ " --host-context-for-sdk 29 PCL[out/soong/b.jar]" +
" --target-context-for-sdk 29 PCL[/system/b.jar]" +
- " --host-context-for-sdk 28 PCL[out/a.jar]" +
+ " --host-context-for-sdk 28 PCL[out/soong/a.jar]" +
" --target-context-for-sdk 28 PCL[/system/a.jar]" +
- " --host-context-for-sdk any PCL[out/d.jar]" +
+ " --host-context-for-sdk any PCL[out/soong/d.jar]" +
" --target-context-for-sdk any PCL[/system/d.jar]"
if wantStr != haveStr {
t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
@@ -284,6 +284,111 @@
})
}
+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.
+ before := m.Dump()
+
+ // Create a new CLCM that excludes some libraries.
+ c := m.ExcludeLibs(excluded_libs)
+
+ // Make sure that the original CLCM was not changed.
+ after := m.Dump()
+ android.AssertStringEquals(t, "input CLCM modified", before, after)
+
+ return c
+ }
+
+ t.Run("exclude nothing", func(t *testing.T) {
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+
+ a := excludeLibs(t, m)
+
+ android.AssertStringEquals(t, "output CLCM ", `{
+ "28": [
+ {
+ "Name": "a",
+ "Optional": false,
+ "Implicit": true,
+ "Host": "out/soong/a.jar",
+ "Device": "/system/a.jar",
+ "Subcontexts": []
+ }
+ ]
+}`, a.Dump())
+ })
+
+ t.Run("one item from list", func(t *testing.T) {
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 28, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+
+ a := excludeLibs(t, m, "a")
+
+ expected := `{
+ "28": [
+ {
+ "Name": "b",
+ "Optional": false,
+ "Implicit": true,
+ "Host": "out/soong/b.jar",
+ "Device": "/system/b.jar",
+ "Subcontexts": []
+ }
+ ]
+}`
+ android.AssertStringEquals(t, "output CLCM ", expected, a.Dump())
+ })
+
+ t.Run("all items from a list", func(t *testing.T) {
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 28, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+
+ a := excludeLibs(t, m, "a", "b")
+
+ android.AssertStringEquals(t, "output CLCM ", `{}`, a.Dump())
+ })
+
+ t.Run("items from a subcontext", func(t *testing.T) {
+ s := make(ClassLoaderContextMap)
+ s.AddContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ s.AddContext(ctx, AnySdkVersion, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), s)
+
+ a := excludeLibs(t, m, "b")
+
+ android.AssertStringEquals(t, "output CLCM ", `{
+ "28": [
+ {
+ "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": []
+ }
+ ]
+ }
+ ]
+}`, a.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 de3666a..153b025 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -49,10 +49,12 @@
ArtApexJars android.ConfiguredJarList // modules for jars that are in the ART APEX
- SystemServerJars android.ConfiguredJarList // jars that form the system server
- SystemServerApps []string // apps that are loaded into system server
- ApexSystemServerJars android.ConfiguredJarList // jars within apex that are loaded into system server
- SpeedApps []string // apps that should be speed optimized
+ SystemServerJars android.ConfiguredJarList // system_server classpath jars on the platform
+ SystemServerApps []string // apps that are loaded into system server
+ ApexSystemServerJars android.ConfiguredJarList // system_server classpath jars delivered via apex
+ StandaloneSystemServerJars android.ConfiguredJarList // jars on the platform that system_server loads dynamically using separate classloaders
+ ApexStandaloneSystemServerJars android.ConfiguredJarList // jars delivered via apex that system_server loads dynamically using separate classloaders
+ SpeedApps []string // apps that should be speed optimized
BrokenSuboptimalOrderOfSystemServerJars bool // if true, sub-optimal order does not cause a build error
@@ -98,6 +100,48 @@
RelaxUsesLibraryCheck bool
}
+var allPlatformSystemServerJarsKey = android.NewOnceKey("allPlatformSystemServerJars")
+
+// Returns all jars on the platform that system_server loads, including those on classpath and those
+// loaded dynamically.
+func (g *GlobalConfig) AllPlatformSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
+ return ctx.Config().Once(allPlatformSystemServerJarsKey, func() interface{} {
+ res := g.SystemServerJars.AppendList(&g.StandaloneSystemServerJars)
+ return &res
+ }).(*android.ConfiguredJarList)
+}
+
+var allApexSystemServerJarsKey = android.NewOnceKey("allApexSystemServerJars")
+
+// Returns all jars delivered via apex that system_server loads, including those on classpath and
+// those loaded dynamically.
+func (g *GlobalConfig) AllApexSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
+ return ctx.Config().Once(allApexSystemServerJarsKey, func() interface{} {
+ res := g.ApexSystemServerJars.AppendList(&g.ApexStandaloneSystemServerJars)
+ return &res
+ }).(*android.ConfiguredJarList)
+}
+
+var allSystemServerClasspathJarsKey = android.NewOnceKey("allSystemServerClasspathJars")
+
+// Returns all system_server classpath jars.
+func (g *GlobalConfig) AllSystemServerClasspathJars(ctx android.PathContext) *android.ConfiguredJarList {
+ return ctx.Config().Once(allSystemServerClasspathJarsKey, func() interface{} {
+ res := g.SystemServerJars.AppendList(&g.ApexSystemServerJars)
+ return &res
+ }).(*android.ConfiguredJarList)
+}
+
+var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars")
+
+// Returns all jars that system_server loads.
+func (g *GlobalConfig) AllSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
+ return ctx.Config().Once(allSystemServerJarsKey, func() interface{} {
+ res := g.AllPlatformSystemServerJars(ctx).AppendList(g.AllApexSystemServerJars(ctx))
+ return &res
+ }).(*android.ConfiguredJarList)
+}
+
// GlobalSoongConfig contains the global config that is generated from Soong,
// stored in dexpreopt_soong.config.
type GlobalSoongConfig struct {
@@ -364,6 +408,7 @@
type dex2oatDependencyTag struct {
blueprint.BaseDependencyTag
+ android.LicenseAnnotationToolchainDependencyTag
}
func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() {
@@ -619,6 +664,8 @@
SystemServerJars: android.EmptyConfiguredJarList(),
SystemServerApps: nil,
ApexSystemServerJars: android.EmptyConfiguredJarList(),
+ StandaloneSystemServerJars: android.EmptyConfiguredJarList(),
+ ApexStandaloneSystemServerJars: android.EmptyConfiguredJarList(),
SpeedApps: nil,
PreoptFlags: nil,
DefaultCompilerFilter: "",
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 965b755..de139c4 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -115,7 +115,7 @@
// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
// or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
if global.OnlyPreoptBootImageAndSystemServer && !global.BootJars.ContainsJar(module.Name) &&
- !AllSystemServerJars(ctx, global).ContainsJar(module.Name) && !module.PreoptExtractedApk {
+ !global.AllSystemServerJars(ctx).ContainsJar(module.Name) && !module.PreoptExtractedApk {
return true
}
@@ -197,13 +197,24 @@
}
// Returns the dex location of a system server java library.
-func GetSystemServerDexLocation(global *GlobalConfig, lib string) string {
- if apex := global.ApexSystemServerJars.ApexOfJar(lib); apex != "" {
+func GetSystemServerDexLocation(ctx android.PathContext, global *GlobalConfig, lib string) string {
+ if apex := global.AllApexSystemServerJars(ctx).ApexOfJar(lib); apex != "" {
return fmt.Sprintf("/apex/%s/javalib/%s.jar", apex, lib)
}
return fmt.Sprintf("/system/framework/%s.jar", lib)
}
+// Returns the location to the odex file for the dex file at `path`.
+func ToOdexPath(path string, arch android.ArchType) string {
+ if strings.HasPrefix(path, "/apex/") {
+ return filepath.Join("/system/framework/oat", arch.String(),
+ strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
+ }
+
+ return filepath.Join(filepath.Dir(path), "oat", arch.String(),
+ pathtools.ReplaceExtension(filepath.Base(path), "odex"))
+}
+
func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
appImage bool, generateDM bool) {
@@ -218,23 +229,8 @@
base = "package.apk"
}
- toOdexPath := func(path string) string {
- if global.ApexSystemServerJars.ContainsJar(module.Name) {
- return filepath.Join(
- "/system/framework/oat",
- arch.String(),
- strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
- }
-
- return filepath.Join(
- filepath.Dir(path),
- "oat",
- arch.String(),
- pathtools.ReplaceExtension(filepath.Base(path), "odex"))
- }
-
odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
- odexInstallPath := toOdexPath(module.DexLocation)
+ odexInstallPath := ToOdexPath(module.DexLocation, arch)
if odexOnSystemOther(module, global) {
odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
}
@@ -244,7 +240,8 @@
invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
- systemServerJars := AllSystemServerJars(ctx, global)
+ systemServerJars := global.AllSystemServerJars(ctx)
+ systemServerClasspathJars := global.AllSystemServerClasspathJars(ctx)
rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
rule.Command().FlagWithOutput("rm -f ", odexPath)
@@ -255,10 +252,15 @@
var clcHost android.Paths
var clcTarget []string
- for i := 0; i < jarIndex; i++ {
- lib := systemServerJars.Jar(i)
+ endIndex := systemServerClasspathJars.IndexOfJar(module.Name)
+ if endIndex < 0 {
+ // The jar is a standalone one. Use the full classpath as the class loader context.
+ endIndex = systemServerClasspathJars.Len()
+ }
+ for i := 0; i < endIndex; i++ {
+ lib := systemServerClasspathJars.Jar(i)
clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib))
- clcTarget = append(clcTarget, GetSystemServerDexLocation(global, lib))
+ clcTarget = append(clcTarget, GetSystemServerDexLocation(ctx, global, lib))
}
if DexpreoptRunningInSoong {
@@ -274,12 +276,22 @@
// cannot see the rule in the generated dexpreopt.sh script).
}
- checkSystemServerOrder(ctx, jarIndex)
+ clcHostString := "PCL[" + strings.Join(clcHost.Strings(), ":") + "]"
+ clcTargetString := "PCL[" + strings.Join(clcTarget, ":") + "]"
+
+ if systemServerClasspathJars.ContainsJar(module.Name) {
+ checkSystemServerOrder(ctx, jarIndex)
+ } else {
+ // Standalone jars are loaded by separate class loaders with SYSTEMSERVERCLASSPATH as the
+ // parent.
+ clcHostString = "PCL[];" + clcHostString
+ clcTargetString = "PCL[];" + clcTargetString
+ }
rule.Command().
- Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clcHost.Strings(), ":") + "]").
+ Text(`class_loader_context_arg=--class-loader-context="` + clcHostString + `"`).
Implicits(clcHost).
- Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clcTarget, ":") + "]")
+ Text(`stored_class_loader_context_arg=--stored-class-loader-context="` + clcTargetString + `"`)
} else {
// There are three categories of Java modules handled here:
@@ -537,17 +549,6 @@
}
}
-var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars")
-
-// TODO: eliminate the superficial global config parameter by moving global config definition
-// from java subpackage to dexpreopt.
-func AllSystemServerJars(ctx android.PathContext, global *GlobalConfig) *android.ConfiguredJarList {
- return ctx.Config().Once(allSystemServerJarsKey, func() interface{} {
- allSystemServerJars := global.SystemServerJars.AppendList(global.ApexSystemServerJars)
- return &allSystemServerJars
- }).(*android.ConfiguredJarList)
-}
-
// A predefined location for the system server dex jars. This is needed in order to generate
// class loader context for dex2oat, as the path to the jar in the Soong module may be unknown
// at that time (Soong processes the jars in dependency order, which may be different from the
@@ -571,7 +572,7 @@
mctx, isModule := ctx.(android.ModuleContext)
if isModule {
config := GetGlobalConfig(ctx)
- jars := AllSystemServerJars(ctx, config)
+ jars := config.AllSystemServerClasspathJars(ctx)
mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
depIndex := jars.IndexOfJar(dep.Name())
if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 798d776..07e4fad 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -50,6 +50,15 @@
android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
}
+func testPlatformSystemServerModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
+ return createTestModuleConfig(
+ name,
+ fmt.Sprintf("/system/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,
@@ -181,6 +190,52 @@
android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
}
+func TestDexPreoptStandaloneSystemServerJars(t *testing.T) {
+ config := android.TestConfig("out", nil, "", nil)
+ ctx := android.BuilderContextForTesting(config)
+ globalSoong := globalSoongConfigForTests()
+ global := GlobalConfigForTests(ctx)
+ module := testPlatformSystemServerModuleConfig(ctx, "service-A")
+
+ global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
+ []string{"platform: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/framework/oat/arm/service-A.odex"},
+ {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system/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)
+ globalSoong := globalSoongConfigForTests()
+ global := GlobalConfigForTests(ctx)
+ module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+
+ global.ApexStandaloneSystemServerJars = android.CreateTestConfiguredJarList(
+ []string{"com.android.apex1: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/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.odex"},
+ {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.vdex"},
+ }
+
+ android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+}
+
func TestDexPreoptProfile(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config)
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 8f5c315..47ae494 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -85,12 +85,12 @@
// Prepares a test fixture by enabling dexpreopt, registering the fake_tool_binary module type and
// using that to define the `dex2oatd` module.
var PrepareForTestByEnablingDexpreopt = android.GroupFixturePreparers(
- FixtureModifyGlobalConfig(func(*GlobalConfig) {}),
+ FixtureModifyGlobalConfig(func(android.PathContext, *GlobalConfig) {}),
)
// FixtureModifyGlobalConfig enables dexpreopt (unless modified by the mutator) and modifies the
// configuration.
-func FixtureModifyGlobalConfig(configModifier func(dexpreoptConfig *GlobalConfig)) android.FixturePreparer {
+func FixtureModifyGlobalConfig(configModifier func(ctx android.PathContext, dexpreoptConfig *GlobalConfig)) android.FixturePreparer {
return android.FixtureModifyConfig(func(config android.Config) {
// Initialize the dexpreopt GlobalConfig to an empty structure. This has no effect if it has
// already been set.
@@ -100,48 +100,77 @@
// Retrieve the existing configuration and modify it.
dexpreoptConfig = GetGlobalConfig(pathCtx)
- configModifier(dexpreoptConfig)
+ configModifier(pathCtx, dexpreoptConfig)
})
}
// FixtureSetArtBootJars enables dexpreopt and sets the ArtApexJars property.
func FixtureSetArtBootJars(bootJars ...string) android.FixturePreparer {
- return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
dexpreoptConfig.ArtApexJars = android.CreateTestConfiguredJarList(bootJars)
})
}
// FixtureSetBootJars enables dexpreopt and sets the BootJars property.
func FixtureSetBootJars(bootJars ...string) android.FixturePreparer {
- return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList(bootJars)
})
}
// FixtureSetApexBootJars sets the ApexBootJars property in the global config.
func FixtureSetApexBootJars(bootJars ...string) android.FixturePreparer {
- return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
dexpreoptConfig.ApexBootJars = android.CreateTestConfiguredJarList(bootJars)
})
}
+// FixtureSetStandaloneSystemServerJars sets the StandaloneSystemServerJars property.
+func FixtureSetStandaloneSystemServerJars(jars ...string) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(jars)
+ })
+}
+
// FixtureSetSystemServerJars sets the SystemServerJars property.
func FixtureSetSystemServerJars(jars ...string) android.FixturePreparer {
- return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
dexpreoptConfig.SystemServerJars = android.CreateTestConfiguredJarList(jars)
})
}
// FixtureSetApexSystemServerJars sets the ApexSystemServerJars property in the global config.
func FixtureSetApexSystemServerJars(jars ...string) android.FixturePreparer {
- return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
dexpreoptConfig.ApexSystemServerJars = android.CreateTestConfiguredJarList(jars)
})
}
+// FixtureSetApexStandaloneSystemServerJars sets the ApexStandaloneSystemServerJars property in the
+// global config.
+func FixtureSetApexStandaloneSystemServerJars(jars ...string) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.ApexStandaloneSystemServerJars = android.CreateTestConfiguredJarList(jars)
+ })
+}
+
// FixtureSetPreoptWithUpdatableBcp sets the PreoptWithUpdatableBcp property in the global config.
func FixtureSetPreoptWithUpdatableBcp(value bool) android.FixturePreparer {
- return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
dexpreoptConfig.PreoptWithUpdatableBcp = value
})
}
+
+// FixtureSetBootImageProfiles sets the BootImageProfiles property in the global config.
+func FixtureSetBootImageProfiles(profiles ...string) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(ctx android.PathContext, dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.BootImageProfiles = android.PathsForSource(ctx, profiles)
+ })
+}
+
+// FixtureDisableGenerateProfile sets the DisableGenerateProfile property in the global config.
+func FixtureDisableGenerateProfile(disable bool) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.DisableGenerateProfile = disable
+ })
+}
diff --git a/docs/OWNERS b/docs/OWNERS
index d143317..2d71271 100644
--- a/docs/OWNERS
+++ b/docs/OWNERS
@@ -1 +1,3 @@
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/native_code_coverage.md b/docs/native_code_coverage.md
new file mode 100644
index 0000000..fabbfa0
--- /dev/null
+++ b/docs/native_code_coverage.md
@@ -0,0 +1,241 @@
+## Native Code Coverage for Android
+
+## Scope
+
+These instructions are for Android developers to collect and inspect code
+coverage for C++ and Rust code on the Android platform.
+
+## Building with Native Code Coverage Instrumentation
+
+Identify the paths where native code-coverage instrumentation should be enabled
+and set up the following environment variables.
+
+```
+ export CLANG_COVERAGE=true
+ export NATIVE_COVERAGE_PATHS="<paths-to-instrument-for-coverage>"
+```
+
+`NATIVE_COVERAGE_PATHS` should be a list of paths. Any Android.bp module defined
+under these paths is instrumented for code-coverage. E.g:
+
+```
+export NATIVE_COVERAGE_PATHS="external/libcxx system/core/adb"
+```
+
+### Additional Notes
+
+- Native Code coverage is not supported for host modules or `Android.mk`
+ modules.
+- `NATIVE_COVERAGE_PATHS="*"` enables coverage instrumentation for all paths.
+- Set `native_coverage: false` blueprint property to always disable code
+ coverage instrumentation for a module. This is useful if this module has
+ issues when building or running with coverage.
+- `NATIVE_COVERAGE_EXCLUDE_PATHS` can be set to exclude subdirs under
+ `NATIVE_COVERAGE_PATHS` from coverage instrumentation. E.g.
+ `NATIVE_COVERAGE_PATHS=frameworks/native
+ NATIVE_COVERAGE_PATHS=frameworks/native/vulkan` will instrument all native
+ code under `frameworks/native` except`frameworks/native/vulkan`.
+
+## Running Tests
+
+### Collecting Profiles
+
+When an instrumented program is run, the profiles are stored to the path and
+name specified in the `LLVM_PROFILE_FILE` environment variable. On Android
+coverage builds it is set to `/data/misc/trace/clang-%p-%20m.profraw`.
+
+* `%`p is replaced by the pid of the process
+* `%m` by the hash of the library/binary
+* The `20` in`%20m` creates a pool of 20 profraw files and "online" profile
+ merging is used to merge coverage to profiles onto this pool.
+
+Reference:`LLVM_PROFILE_FILE` can include additional specifiers as described
+[here](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program).
+
+For this and following steps, use the `acov-llvm.py` script:
+`$ANDROID_BUILD_TOP/development/scripts/acov-llvm.py`.
+
+There may be profiles in `/data/misc/trace` collected before the test is run.
+Clear this data before running the test.
+
+```
+ # Clear any coverage that's already written to /data/misc/trace
+ # and reset coverage for all daemons.
+ <host>$ acov-llvm.py clean-device
+
+ # Run the test. The exact command depends on the nature of the test.
+ <device>$ /data/local/tmp/$MY_TEST
+```
+
+For tests that exercise a daemon/service running in another process, write out
+the coverage for those processes as well.
+
+```
+ # Flush coverage of all daemons/processes running on the device.
+ <host>$ acov-llvm.py flush
+
+ # Flush coverage for a particular daemon, say adbd.
+ <host>$ acov-llvm.py flush adbd
+```
+
+## Viewing Coverage Data (acov-llvm.py)
+
+To post-process and view coverage information we use the `acov-llvm.py report`
+command. It invokes two LLVM utilities `llvm-profdata` and `llvm-cov`. An
+advanced user can manually invoke these utilities for fine-grained control. This
+is discussed [below](#viewing-coverage-data-manual).
+
+To generate coverage report need the following parameters. These are dependent
+on the test/module:
+
+1. One or more binaries and shared libraries from which coverage was collected.
+ E.g.:
+
+ 1. ART mainline module contains a few libraries such as `libart.so`,
+ `libart-compiler.so`.
+ 2. Bionic tests exercise code in `libc.so` and `libm.so`.
+
+ We need the *unstripped* copies of these binaries. Source information
+ included in the debuginfo is used to process the coverage data.
+
+2. One or more source directories under `$ANDROID_BUILD_TOP` for which coverage
+ needs to be reported.
+
+Invoke the report subcommand of acov-llvm.py to produce a html coverage summary:
+
+```
+ $ acov-llvm.py report \
+ -s <one-or-more-source-paths-in-$ANDROID_BUILD_TOP \
+ -b <one-or-more-(unstripped)-binaries-in-$OUT>
+```
+
+E.g.:
+
+```
+ $ acov-llvm.py report \
+ -s bionic \
+ -b \
+ $OUT/symbols/apex/com.android.runtime/lib/bionic/libc.so \
+ $OUT/symbols/apex/com.android.runtime/lib/bionic/libm.so
+```
+
+The script will produce a report in a temporary directory under
+`$ANDROID_BUILD_TOP`. It'll produce a log as below:
+
+```
+ generating coverage report in covreport-xxxxxx
+```
+
+A html report would be generated under `covreport-xxxxxx/html`.
+
+## Viewing Coverage Data (manual)
+
+`acov-llvm.py report` does a few operations under the hood which we can also
+manually invoke for flexibility.
+
+### Post-processing Coverage Files
+
+Fetch coverage files from the device and post-process them to a `.profdata` file
+as follows:
+
+```
+ # Fetch the coverage data from the device.
+ <host>$ cd coverage_data
+ <host>$ adb pull /data/misc/trace/ $TRACE_DIR_HOST
+
+ # Convert from .profraw format to the .profdata format.
+ <host>$ llvm-profdata merge --output=$MY_TEST.profdata \
+ $TRACE_DIR_HOST/clang-*.profraw
+```
+
+For added specificity, restrict the above command to just the <PID>s of the
+daemon or test processes of interest.
+
+```
+ <host>$ llvm-profdata merge --output=$MY_TEST.profdata \
+ $MY_TEST.profraw \
+ trace/clang-<pid1>.profraw trace/clang-<pid2>.profraw ...
+```
+
+### Generating Coverage report
+
+Documentation on Clang source-instrumentation-based coverage is available
+[here](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#creating-coverage-reports).
+The `llvm-cov` utility is used to show coverage from a `.profdata` file. The
+documentation for commonly used `llvm-cov` command-line arguments is available
+[here](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report). (Try
+`llvm-cov show --help` for a complete list).
+
+#### `show` subcommand
+
+The `show` command displays the function and line coverage for each source file
+in the binary.
+
+```
+ <host>$ llvm-cov show \
+ --show-region-summary=false
+ --format=html --output-dir=coverage-html \
+ --instr-profile=$MY_TEST.profdata \
+ $MY_BIN \
+```
+
+* In the above command, `$MY_BIN` should be the unstripped binary (i.e. with
+ debuginfo) since `llvm-cov` reads some debuginfo to process the coverage
+ data.
+
+ E.g.:
+
+ ~~~
+ ```
+ <host>$ llvm-cov report \
+ --instr-profile=adbd.profdata \
+ $LOCATION_OF_UNSTRIPPED_ADBD/adbd \
+ --show-region-summary=false
+ ```
+ ~~~
+
+* The `-ignore-filename-regex=<regex>` option can be used to ignore files that
+ are not of interest. E.g: `-ignore-filename-regex="external/*"`
+
+* Use the `--object=<BIN>` argument to specify additional binaries and shared
+ libraries whose coverage is included in this profdata. See the earlier
+ [section](#viewing-coverage-data-acov-llvm-py) for examples where more than
+ one binary may need to be used.
+
+ E.g., the following command is used for `bionic-unit-tests`, which tests
+ both `libc.so` and `libm.so`:
+
+ ~~~
+ ```
+ <host>$ llvm-cov report \
+ --instr-profile=bionic.profdata \
+ $OUT/.../libc.so \
+ --object=$OUT/.../libm.so
+ ```
+ ~~~
+
+* `llvm-cov` also takes positional SOURCES argument to consider/display only
+ particular paths of interest. E.g:
+
+ ~~~
+ ```
+ <host>$ llvm-cov report \
+ --instr-profile=adbd.profdata \
+ $LOCATION_OF_ADBD/adbd \
+ --show-region-summary=false \
+ /proc/self/cwd/system/core/adb
+ ```
+ ~~~
+
+Note that the paths for the sources need to be prepended with
+'`/proc/self/cwd/`'. This is because Android C/C++ compilations run with
+`PWD=/proc/self/cwd` and consequently the source names are recorded with that
+prefix. Alternatively, the
+[`--path-equivalence`](https://llvm.org/docs/CommandGuide/llvm-cov.html#cmdoption-llvm-cov-show-path-equivalence)
+option to `llvm-cov` can be used.
+
+#### `report` subcommand
+
+The [`report`](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report)
+subcommand summarizes the percentage of covered lines to the console. It takes
+options similar to the `show` subcommand.
diff --git a/docs/tidy.md b/docs/tidy.md
new file mode 100644
index 0000000..890c3a0
--- /dev/null
+++ b/docs/tidy.md
@@ -0,0 +1,254 @@
+# Clang-Tidy Rules and Checks
+
+Android C/C++ source files can be checked by clang-tidy for issues like
+coding style, error-prone/performance patterns, and static flow analysis.
+See the official
+[clang-tidy document](https://clang.llvm.org/extra/clang-tidy)
+and list of
+[clang-tidy checks](https://clang.llvm.org/extra/clang-tidy/checks/list.html).
+
+## Global defaults for Android builds
+
+The simplest way to enable clang-tidy checks is
+to set environment variable `WITH_TIDY`.
+```
+$ WITH_TIDY=1 make
+```
+
+This will turn on the global default to run clang-tidy for every required
+C/C++ source file compilation. The global default clang-tidy checks
+do not include time-consuming static analyzer checks. To enable those
+checks, set the `CLANG_ANALYZER_CHECKS` variable.
+```
+$ WITH_TIDY=1 CLANG_ANALYZER_CHECKS=1 make
+```
+
+The default global clang-tidy checks and flags are defined in
+[build/soong/cc/config/tidy.go](https://android.googlesource.com/platform/build/soong/+/refs/heads/master/cc/config/tidy.go).
+
+
+## Module clang-tidy properties
+
+The global default can be overwritten by module properties in Android.bp.
+
+### `tidy` and `tidy_checks`
+
+For example, in
+[system/bpf/Android.bp](https://android.googlesource.com/platform/system/bpf/+/refs/heads/master/Android.bp),
+clang-tidy is enabled explicitly and with a different check list:
+```
+cc_defaults {
+ name: "bpf_defaults",
+ // snipped
+ tidy: true,
+ tidy_checks: [
+ "android-*",
+ "cert-*",
+ "-cert-err34-c",
+ "clang-analyzer-security*",
+ // Disabling due to many unavoidable warnings from POSIX API usage.
+ "-google-runtime-int",
+ ],
+}
+```
+That means in normal builds, even without `WITH_TIDY=1`,
+the modules that use `bpf_defaults` will run clang-tidy
+over C/C++ source files with the given `tidy_checks`.
+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.
+
+Some modules might want to disable clang-tidy even when
+environment variable `WITH_TIDY=1` is set.
+Examples can be found in
+[system/netd/tests/Android.bp](https://android.googlesource.com/platform/system/netd/+/refs/heads/master/tests/Android.bp)
+```
+cc_test {
+ name: "netd_integration_test",
+ // snipped
+ defaults: ["netd_defaults"],
+ tidy: false, // cuts test build time by almost 1 minute
+```
+and in
+[bionic/tests/Android.bp](https://android.googlesource.com/platform/bionic/+/refs/heads/master/tests/Android.bp).
+```
+cc_test_library {
+ name: "fortify_disabled_for_tidy",
+ // snipped
+ srcs: ["clang_fortify_tests.cpp"],
+ tidy: false,
+}
+```
+
+### `tidy_checks_as_errors`
+
+The global tidy checks are enabled as warnings.
+If a C/C++ module wants to be free of certain clang-tidy warnings,
+it can chose those checks to be treated as errors.
+For example
+[system/core/libsysutils/Android.bp](https://android.googlesource.com/platform/system/core/+/refs/heads/master/libsysutils/Android.bp)
+has enabled clang-tidy explicitly, selected its own tidy checks,
+and set three groups of tidy checks as errors:
+```
+cc_library {
+ name: "libsysutils",
+ // snipped
+ tidy: true,
+ tidy_checks: [
+ "-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ "android-*",
+ ],
+ tidy_checks_as_errors: [
+ "cert-*",
+ "clang-analyzer-security*",
+ "android-*",
+ ],
+ // snipped
+}
+```
+
+### `tidy_flags` and `tidy_disabled_srcs`
+
+Extra clang-tidy flags can be passed with the `tidy_flags` property.
+
+Some Android modules use the `tidy_flags` to pass "-warnings-as-errors="
+to clang-tidy. This usage should now be replaced with the
+`tidy_checks_as_errors` property.
+
+Some other tidy flags examples are `-format-style=` and `-header-filter=`
+For example, in
+[art/odrefresh/Android.bp](https://android.googlesource.com/platform/art/+/refs/heads/master/odrefresh/Android.bp),
+we found
+```
+cc_defaults {
+ name: "odrefresh-defaults",
+ srcs: [
+ "odrefresh.cc",
+ "odr_common.cc",
+ "odr_compilation_log.cc",
+ "odr_fs_utils.cc",
+ "odr_metrics.cc",
+ "odr_metrics_record.cc",
+ ],
+ // snipped
+ generated_sources: [
+ "apex-info-list-tinyxml",
+ "art-apex-cache-info",
+ "art-odrefresh-operator-srcs",
+ ],
+ // snipped
+ tidy: true,
+ tidy_disabled_srcs: [":art-apex-cache-info"],
+ tidy_flags: [
+ "-format-style=file",
+ "-header-filter=(art/odrefresh/|system/apex/)",
+ ],
+}
+```
+That means all modules with the `odrefresh-defaults` will
+have clang-tidy enabled, but not for generated source
+files in `art-apex-cache-info`.
+The clang-tidy is called with extra flags to specify the
+format-style and header-filter.
+
+Note that the globally set default for header-filter is to
+include only the module directory. So, the default clang-tidy
+warnings for `art/odrefresh` modules will include source files
+under that directory. Now `odrefresh-defaults` is interested
+in seeing warnings from both `art/odrefresh/` and `system/apex/`
+and it redefines `-header-filter` in its `tidy_flags`.
+
+
+## Phony tidy-* targets
+
+### The tidy-*directory* targets
+
+Setting `WITH_TIDY=1` is easy to enable clang-tidy globally for any build.
+However, it adds extra compilation time.
+
+For developers focusing on just one directory, they only want to compile
+their files with clang-tidy and wish to build other Android components as
+fast as possible. Changing the `WITH_TIDY=1` variable setting is also expensive
+since the build.ninja file will be regenerated due to any such variable change.
+
+To manually select only some directories or modules to compile with clang-tidy,
+do not set the `WITH_TIDY=1` variable, but use the special `tidy-<directory>`
+phony target. For example, a person working on `system/libbase` can build
+Android quickly with
+```
+unset WITH_TIDY # Optional, not if you haven't set WITH_TIDY
+make droid tidy-system-libbase
+```
+
+For any directory `d1/d2/d3`, a phony target tidy-d1-d2-d3 is generated
+if there is any C/C++ source file under `d1/d2/d3`.
+
+Note that with `make droid tidy-system-libbase`, some C/C++ files
+that are not needed by the `droid` target will be passed to clang-tidy
+if they are under `system/libbase`. This is like a `checkbuild`
+under `system/libbase` to include all modules, but only C/C++
+files of those modules are compiled with clang-tidy.
+
+### The tidy-soong target
+
+A special `tidy-soong` target is defined to include all C/C++
+source files in *all* directories. This phony target is sometimes
+used to test if all source files compile with a new clang-tidy release.
+
+### The tidy-*_subset targets
+
+A *subset* of each tidy-* phony target is defined to reduce test time.
+Since any Android module, a C/C++ library or binary, can be built
+for many different *variants*, one C/C++ source file is usually
+compiled multiple times with different compilation flags.
+Many of such *variant* flags have little or no effect on clang-tidy
+checks. To reduce clang-tidy check time, a *subset* target like
+`tidy-soong_subset` or `tidy-system-libbase_subset` is generated
+to include only a subset, the first variant, of each module in
+the directory.
+
+Hence, for C/C++ source code quality, instead of a long
+"make checkbuild", we can use "make tidy-soong_subset".
+
+
+## Limit clang-tidy runtime
+
+Some Android modules have large files that take a long time to compile
+with clang-tidy, with or without the clang-analyzer checks.
+To limit clang-tidy time, an environment variable can be set as
+```base
+WITH_TIDY=1 TIDY_TIMEOUT=90 make
+```
+This 90-second limit is actually the default time limit
+in several Android continuous builds where `WITH_TIDY=1` and
+`CLANG_ANALYZER_CHECKS=1` are set.
+
+Similar to `tidy_disabled_srcs` a `tidy_timeout_srcs` list
+can be used to include all source files that took too much time to compile
+with clang-tidy. Files listed in `tidy_timeout_srcs` will not
+be compiled by clang-tidy when `TIDY_TIMEOUT` is defined.
+This can save global build time, when it is necessary to set some
+time limit globally to finish in an acceptable time.
+For developers who want to find all clang-tidy warnings and
+are willing to spend more time on all files in a project,
+they should not define `TIDY_TIMEOUT` and build only the wanted project directories.
+
+## Capabilities for Android.bp and Android.mk
+
+Some of the previously mentioned features are defined only
+for modules in Android.bp files, not for Android.mk modules yet.
+
+* The global `WITH_TIDY=1` variable will enable clang-tidy for all C/C++
+ modules in Android.bp or Android.mk files.
+
+* The global `TIDY_TIMEOUT` variable is recognized by Android prebuilt
+ clang-tidy, so it should work for any clang-tidy invocation.
+
+* The clang-tidy module level properties are defined for Android.bp modules.
+ For Android.mk modules, old `LOCAL_TIDY`, `LOCAL_TIDY_CHECKS`,
+ `LOCAL_TIDY_FLAGS` work similarly, but it would be better to convert
+ those modules to use Android.bp files.
+
+* The `tidy-*` phony targets are only generated for Android.bp modules.
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 81896bd..a142833 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -54,6 +54,7 @@
ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
ctx.RegisterModuleType("prebuilt_root", PrebuiltRootFactory)
+ ctx.RegisterModuleType("prebuilt_root_host", PrebuiltRootHostFactory)
ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
@@ -63,7 +64,6 @@
ctx.RegisterModuleType("prebuilt_defaults", defaultsFactory)
- android.RegisterBp2BuildMutator("prebuilt_etc", PrebuiltEtcBp2Build)
}
var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
@@ -377,7 +377,7 @@
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_TAGS", "optional")
- entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
if len(p.properties.Symlinks) > 0 {
entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
@@ -439,6 +439,8 @@
InitPrebuiltEtcModule(module, "etc")
// This module is host-only
android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitBazelModule(module)
return module
}
@@ -449,6 +451,18 @@
InitPrebuiltRootModule(module)
// This module is device-only
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitDefaultableModule(module)
+ return module
+}
+
+// prebuilt_root_host is for a host prebuilt artifact that is installed in $(HOST_OUT)/<sub_dir>
+// directory.
+func PrebuiltRootHostFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, ".")
+ // This module is host-only
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
return module
}
@@ -459,6 +473,7 @@
InitPrebuiltEtcModule(module, "usr/share")
// This module is device-only
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitDefaultableModule(module)
return module
}
@@ -469,6 +484,7 @@
InitPrebuiltEtcModule(module, "usr/share")
// This module is host-only
android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
return module
}
@@ -478,6 +494,7 @@
InitPrebuiltEtcModule(module, "fonts")
// This module is device-only
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitDefaultableModule(module)
return module
}
@@ -491,6 +508,7 @@
InitPrebuiltEtcModule(module, "etc/firmware")
// This module is device-only
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitDefaultableModule(module)
return module
}
@@ -503,6 +521,7 @@
InitPrebuiltEtcModule(module, "etc/dsp")
// This module is device-only
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitDefaultableModule(module)
return module
}
@@ -516,6 +535,7 @@
InitPrebuiltEtcModule(module, "lib/rfsa")
// This module is device-only
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitDefaultableModule(module)
return module
}
@@ -655,26 +675,29 @@
Installable bazel.BoolAttribute
}
-func PrebuiltEtcBp2Build(ctx android.TopDownMutatorContext) {
- module, ok := ctx.Module().(*PrebuiltEtc)
- if !ok {
- // Not an prebuilt_etc
- return
- }
- if !module.ConvertWithBp2build(ctx) {
- return
- }
- if ctx.ModuleType() != "prebuilt_etc" {
+// ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
+func (p *PrebuiltEtc) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ // All prebuilt_* modules are PrebuiltEtc, but at this time, we only convert prebuilt_etc modules.
+ if p.installDirBase != "etc" {
return
}
- prebuiltEtcBp2BuildInternal(ctx, module)
+ prebuiltEtcBp2BuildInternal(ctx, p)
}
func prebuiltEtcBp2BuildInternal(ctx android.TopDownMutatorContext, module *PrebuiltEtc) {
var srcLabelAttribute bazel.LabelAttribute
- if module.properties.Src != nil {
- srcLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Src))
+ for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltEtcProperties{}) {
+ for config, p := range configToProps {
+ props, ok := p.(*prebuiltEtcProperties)
+ if !ok {
+ continue
+ }
+ if props.Src != nil {
+ label := android.BazelLabelForModuleSrcSingle(ctx, *props.Src)
+ srcLabelAttribute.SetSelectValue(axis, config, label)
+ }
+ }
}
var filename string
diff --git a/etc/snapshot_etc.go b/etc/snapshot_etc.go
index 9a25d5a..b54a8a6 100644
--- a/etc/snapshot_etc.go
+++ b/etc/snapshot_etc.go
@@ -128,7 +128,7 @@
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_TAGS", "optional")
- entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
},
},
diff --git a/example_config.json b/example_config.json
new file mode 100644
index 0000000..7489840
--- /dev/null
+++ b/example_config.json
@@ -0,0 +1,6 @@
+{
+ "env": {
+ "ENV_VAR_1": "Value1",
+ "ENV_VAR_2": "Value2"
+ }
+}
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 73d807d..33beb37 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -145,12 +145,10 @@
}
dtbName := proptools.String(b.properties.Dtb_prebuilt)
- if dtbName == "" {
- ctx.PropertyErrorf("dtb_prebuilt", "must be set")
- return output
+ if dtbName != "" {
+ dtb := android.PathForModuleSrc(ctx, dtbName)
+ cmd.FlagWithInput("--dtb ", dtb)
}
- dtb := android.PathForModuleSrc(ctx, dtbName)
- cmd.FlagWithInput("--dtb ", dtb)
cmdline := strings.Join(b.properties.Cmdline, " ")
if cmdline != "" {
@@ -178,20 +176,18 @@
cmd.FlagWithArg("--header_version ", headerVersion)
ramdiskName := proptools.String(b.properties.Ramdisk_module)
- if ramdiskName == "" {
- ctx.PropertyErrorf("ramdisk_module", "must be set")
- return output
- }
- ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
- if filesystem, ok := ramdisk.(*filesystem); ok {
- flag := "--ramdisk "
- if vendor {
- flag = "--vendor_ramdisk "
+ if ramdiskName != "" {
+ ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
+ if filesystem, ok := ramdisk.(*filesystem); ok {
+ flag := "--ramdisk "
+ if vendor {
+ flag = "--vendor_ramdisk "
+ }
+ cmd.FlagWithInput(flag, filesystem.OutputPath())
+ } else {
+ ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name())
+ return output
}
- cmd.FlagWithInput(flag, filesystem.OutputPath())
- } else {
- ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name())
- return output
}
bootconfig := proptools.String(b.properties.Bootconfig)
@@ -267,7 +263,7 @@
OutputFile: android.OptionalPathForPath(b.output),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", b.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", b.installDir.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", b.installFileName())
},
},
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index b2caa51..0796258 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -394,7 +394,7 @@
OutputFile: android.OptionalPathForPath(f.output),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", f.installDir.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
},
},
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index 739e609..e2f7d7b 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -215,7 +215,7 @@
OutputFile: android.OptionalPathForPath(l.output),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", l.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", l.installDir.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName())
},
},
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 3f16c0d..63e0aba 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -247,7 +247,7 @@
OutputFile: android.OptionalPathForPath(v.output),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", v.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", v.installDir.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName())
},
},
diff --git a/finder/finder.go b/finder/finder.go
index 5413fa6..b4834b1 100644
--- a/finder/finder.go
+++ b/finder/finder.go
@@ -847,6 +847,7 @@
if err != nil {
return errors.New("No data to load from database\n")
}
+ defer reader.Close()
bufferedReader := bufio.NewReader(reader)
if !f.validateCacheHeader(bufferedReader) {
return errors.New("Cache header does not match")
diff --git a/finder/fs/test.go b/finder/fs/test.go
index cb2140e..ed981fd 100644
--- a/finder/fs/test.go
+++ b/finder/fs/test.go
@@ -74,6 +74,7 @@
if err != nil {
t.Fatalf(err.Error())
}
+ defer reader.Close()
bytes, err := ioutil.ReadAll(reader)
if err != nil {
t.Fatal(err.Error())
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index ccadc0f..89f8187 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -31,6 +31,7 @@
const (
Cc Lang = ""
Rust Lang = "rust"
+ Java Lang = "java"
)
var BoolDefault = proptools.BoolDefault
@@ -42,8 +43,9 @@
}
type FuzzPackager struct {
- Packages android.Paths
- FuzzTargets map[string]bool
+ Packages android.Paths
+ FuzzTargets map[string]bool
+ SharedLibInstallStrings []string
}
type FileToZip struct {
@@ -219,6 +221,9 @@
if lang == Rust {
zipFileName = "fuzz-rust-" + hostOrTarget + "-" + arch + ".zip"
}
+ if lang == Java {
+ zipFileName = "fuzz-java-" + hostOrTarget + "-" + arch + ".zip"
+ }
outputFile := android.PathForOutput(ctx, zipFileName)
s.Packages = append(s.Packages, outputFile)
@@ -251,3 +256,42 @@
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 7dbe6fa..8282426 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -67,13 +67,6 @@
ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
})
-
- android.RegisterBp2BuildMutator("genrule", GenruleBp2Build)
- android.RegisterBp2BuildMutator("cc_genrule", CcGenruleBp2Build)
-}
-
-func RegisterGenruleBp2BuildDeps(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("genrule_tool_deps", toolDepsMutator)
}
var (
@@ -110,8 +103,19 @@
type hostToolDependencyTag struct {
blueprint.BaseDependencyTag
+ android.LicenseAnnotationToolchainDependencyTag
label string
}
+
+func (t hostToolDependencyTag) AllowDisabledModuleDependency(target android.Module) bool {
+ // Allow depending on a disabled module if it's replaced by a prebuilt
+ // counterpart. We get the prebuilt through android.PrebuiltGetPreferred in
+ // GenerateAndroidBuildActions.
+ return target.IsReplacedByPrebuilt()
+}
+
+var _ android.AllowDisabledModuleDependency = (*hostToolDependencyTag)(nil)
+
type generatorProperties struct {
// The command to run on one or more input files. Cmd supports substitution of a few variables.
//
@@ -248,7 +252,7 @@
// 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 {
bazelCtx := ctx.Config().BazelContext
- filePaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
+ filePaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
if ok {
var bazelOutputFiles android.Paths
exportIncludeDirs := map[string]bool{}
@@ -304,6 +308,12 @@
switch tag := ctx.OtherModuleDependencyTag(module).(type) {
case hostToolDependencyTag:
tool := ctx.OtherModuleName(module)
+ if m, ok := module.(android.Module); ok {
+ // Necessary to retrieve any prebuilt replacement for the tool, since
+ // toolDepsMutator runs too late for the prebuilt mutators to have
+ // replaced the dependency.
+ module = android.PrebuiltGetPreferred(ctx, m)
+ }
switch t := module.(type) {
case android.HostToolProvider:
@@ -336,14 +346,9 @@
}
case bootstrap.GoBinaryTool:
// A GoBinaryTool provides the install path to a tool, which will be copied.
- if s, err := filepath.Rel(android.PathForOutput(ctx).String(), t.InstallPath()); err == nil {
- toolPath := android.PathForOutput(ctx, s)
- tools = append(tools, toolPath)
- addLocationLabel(tag.label, toolLocation{android.Paths{toolPath}})
- } else {
- ctx.ModuleErrorf("cannot find path for %q: %v", tool, err)
- return
- }
+ p := android.PathForGoBinary(ctx, t)
+ tools = append(tools, p)
+ addLocationLabel(tag.label, toolLocation{android.Paths{p}})
default:
ctx.ModuleErrorf("%q is not a host tool provider", tool)
return
@@ -837,38 +842,8 @@
Cmd string
}
-// CcGenruleBp2Build is for cc_genrule.
-func CcGenruleBp2Build(ctx android.TopDownMutatorContext) {
- m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build(ctx) {
- return
- }
-
- if ctx.ModuleType() != "cc_genrule" {
- // Not a cc_genrule.
- return
- }
-
- genruleBp2Build(ctx)
-}
-
-// GenruleBp2Build is used for genrule.
-func GenruleBp2Build(ctx android.TopDownMutatorContext) {
- m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build(ctx) {
- return
- }
-
- if ctx.ModuleType() != "genrule" {
- // Not a regular genrule.
- return
- }
-
- genruleBp2Build(ctx)
-}
-
-func genruleBp2Build(ctx android.TopDownMutatorContext) {
- m, _ := ctx.Module().(*Module)
+// ConvertWithBp2build converts a Soong module -> Bazel target.
+func (m *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
// Bazel only has the "tools" attribute.
tools_prop := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
tool_files_prop := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
@@ -887,7 +862,7 @@
cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
genDir := "$(GENDIR)"
- if ctx.ModuleType() == "cc_genrule" {
+ if t := ctx.ModuleType(); t == "cc_genrule" || t == "java_genrule" || t == "java_genrule_host" {
genDir = "$(RULEDIR)"
}
cmd = strings.Replace(cmd, "$(genDir)", genDir, -1)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 714d2f8..04c97fd 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -34,7 +34,9 @@
android.PrepareForTestWithFilegroup,
PrepareForTestWithGenRuleBuildComponents,
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ android.RegisterPrebuiltMutators(ctx)
ctx.RegisterModuleType("tool", toolFactory)
+ ctx.RegisterModuleType("prebuilt_tool", prebuiltToolFactory)
ctx.RegisterModuleType("output", outputProducerFactory)
ctx.RegisterModuleType("use_source", useSourceFactory)
}),
@@ -720,6 +722,69 @@
result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
}
+func TestPrebuiltTool(t *testing.T) {
+ testcases := []struct {
+ name string
+ bp string
+ expectedToolName string
+ }{
+ {
+ name: "source only",
+ bp: `
+ tool { name: "tool" }
+ `,
+ expectedToolName: "bin/tool",
+ },
+ {
+ name: "prebuilt only",
+ bp: `
+ prebuilt_tool { name: "tool" }
+ `,
+ expectedToolName: "prebuilt_bin/tool",
+ },
+ {
+ name: "source preferred",
+ bp: `
+ tool { name: "tool" }
+ prebuilt_tool { name: "tool" }
+ `,
+ expectedToolName: "bin/tool",
+ },
+ {
+ name: "prebuilt preferred",
+ bp: `
+ tool { name: "tool" }
+ prebuilt_tool { name: "tool", prefer: true }
+ `,
+ expectedToolName: "prebuilt_bin/prebuilt_tool",
+ },
+ {
+ name: "source disabled",
+ bp: `
+ tool { name: "tool", enabled: false }
+ prebuilt_tool { name: "tool" }
+ `,
+ expectedToolName: "prebuilt_bin/prebuilt_tool",
+ },
+ }
+
+ for _, test := range testcases {
+ t.Run(test.name, func(t *testing.T) {
+ result := prepareForGenRuleTest.RunTestWithBp(t, test.bp+`
+ genrule {
+ name: "gen",
+ tools: ["tool"],
+ out: ["foo"],
+ cmd: "$(location tool)",
+ }
+ `)
+ gen := result.Module("gen", "").(*Module)
+ expectedCmd := "__SBOX_SANDBOX_DIR__/tools/out/" + test.expectedToolName
+ android.AssertStringEquals(t, "command", expectedCmd, gen.rawCommands[0])
+ })
+ }
+}
+
func TestGenruleWithBazel(t *testing.T) {
bp := `
genrule {
@@ -764,7 +829,33 @@
return android.OptionalPathForPath(t.outputFile)
}
+type prebuiltTestTool struct {
+ android.ModuleBase
+ prebuilt android.Prebuilt
+ testTool
+}
+
+func (p *prebuiltTestTool) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *prebuiltTestTool) Prebuilt() *android.Prebuilt {
+ return &p.prebuilt
+}
+
+func (t *prebuiltTestTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "prebuilt_bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
+}
+
+func prebuiltToolFactory() android.Module {
+ module := &prebuiltTestTool{}
+ android.InitPrebuiltModuleWithoutSrcs(module)
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+ return module
+}
+
var _ android.HostToolProvider = (*testTool)(nil)
+var _ android.HostToolProvider = (*prebuiltTestTool)(nil)
type testOutputProducer struct {
android.ModuleBase
diff --git a/java/Android.bp b/java/Android.bp
index 9ffa123..c062941 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -40,9 +40,11 @@
"dex.go",
"dexpreopt.go",
"dexpreopt_bootjars.go",
+ "dexpreopt_check.go",
"dexpreopt_config.go",
"droiddoc.go",
"droidstubs.go",
+ "fuzz.go",
"gen.go",
"genrule.go",
"hiddenapi.go",
@@ -92,6 +94,7 @@
"platform_bootclasspath_test.go",
"platform_compat_config_test.go",
"plugin_test.go",
+ "prebuilt_apis_test.go",
"rro_test.go",
"sdk_test.go",
"sdk_library_test.go",
diff --git a/java/OWNERS b/java/OWNERS
index 5242712..5b71b1e 100644
--- a/java/OWNERS
+++ b/java/OWNERS
@@ -1 +1,4 @@
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/aar.go b/java/aar.go
index 13390db..8e10253 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -267,18 +267,29 @@
})
func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkContext,
- classLoaderContexts dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) {
+ classLoaderContexts dexpreopt.ClassLoaderContextMap, excludedLibs []string,
+ extraLinkFlags ...string) {
transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags :=
aaptLibs(ctx, sdkContext, classLoaderContexts)
+ // Exclude any libraries from the supplied list.
+ classLoaderContexts = classLoaderContexts.ExcludeLibs(excludedLibs)
+
// App manifest file
manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
- manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts,
- a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode,
- a.LoggingParent)
+ manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{
+ SdkContext: sdkContext,
+ ClassLoaderContexts: classLoaderContexts,
+ IsLibrary: a.isLibrary,
+ UseEmbeddedNativeLibs: a.useEmbeddedNativeLibs,
+ UsesNonSdkApis: a.usesNonSdkApis,
+ UseEmbeddedDex: a.useEmbeddedDex,
+ HasNoCode: a.hasNoCode,
+ LoggingParent: a.LoggingParent,
+ })
// Add additional manifest files to transitive manifests.
additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)
@@ -520,7 +531,7 @@
func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.aapt.isLibrary = true
a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
- a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts)
+ a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, nil)
a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
@@ -616,6 +627,7 @@
exportPackage android.WritablePath
extraAaptPackagesFile android.WritablePath
manifest android.WritablePath
+ assetsPackage android.WritablePath
exportedStaticPackages android.Paths
@@ -686,9 +698,8 @@
return android.Paths{a.manifest}
}
-// TODO(jungjw): Decide whether we want to implement this.
func (a *AARImport) ExportedAssets() android.OptionalPath {
- return android.OptionalPath{}
+ return android.OptionalPathForPath(a.assetsPackage)
}
// RRO enforcement is not available on aar_import since its RRO dirs are not
@@ -732,10 +743,11 @@
blueprint.RuleParams{
Command: `rm -rf $outDir && mkdir -p $outDir && ` +
`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out && ` +
+ `${config.Zip2ZipCmd} -i $in -o $assetsPackage 'assets/**/*' && ` +
`${config.MergeZipsCmd} $combinedClassesJar $$(ls $outDir/classes.jar 2> /dev/null) $$(ls $outDir/libs/*.jar 2> /dev/null)`,
- CommandDeps: []string{"${config.MergeZipsCmd}"},
+ CommandDeps: []string{"${config.MergeZipsCmd}", "${config.Zip2ZipCmd}"},
},
- "outDir", "combinedClassesJar")
+ "outDir", "combinedClassesJar", "assetsPackage")
func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if len(a.properties.Aars) != 1 {
@@ -761,15 +773,17 @@
a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar")
a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
+ a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip")
ctx.Build(pctx, android.BuildParams{
Rule: unzipAAR,
Input: a.aarPath,
- Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
+ Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage},
Description: "unzip AAR",
Args: map[string]string{
"outDir": extractedAARDir.String(),
"combinedClassesJar": a.classpathFile.String(),
+ "assetsPackage": a.assetsPackage.String(),
},
})
@@ -812,6 +826,19 @@
aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile,
linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil)
+ // Merge this import's assets with its dependencies' assets (if there are any).
+ if len(transitiveAssets) > 0 {
+ mergedAssets := android.PathForModuleOut(ctx, "merged-assets.zip")
+ inputZips := append(android.Paths{a.assetsPackage}, transitiveAssets...)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: mergeAssetsRule,
+ Inputs: inputZips,
+ Output: mergedAssets,
+ Description: "merge assets from dependencies and self",
+ })
+ a.assetsPackage = mergedAssets
+ }
+
ctx.SetProvider(JavaInfoProvider, JavaInfo{
HeaderJars: android.PathsIfNonNil(a.classpathFile),
ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 38065f1..7772b70 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "strconv"
"strings"
"github.com/google/blueprint"
@@ -27,13 +28,10 @@
var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
blueprint.RuleParams{
Command: `${config.ManifestFixerCmd} ` +
- `--minSdkVersion ${minSdkVersion} ` +
- `--targetSdkVersion ${targetSdkVersion} ` +
- `--raise-min-sdk-version ` +
`$args $in $out`,
CommandDeps: []string{"${config.ManifestFixerCmd}"},
},
- "minSdkVersion", "targetSdkVersion", "args")
+ "args")
var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
blueprint.RuleParams{
@@ -42,86 +40,124 @@
},
"args", "libs")
-// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
-func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext android.SdkContext,
- classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis,
- useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
+// targetSdkVersion for manifest_fixer
+// 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, sdkContext android.SdkContext) string {
+ targetSdkVersionSpec := sdkContext.TargetSdkVersion(ctx)
+ if ctx.Config().UnbundledBuildApps() && targetSdkVersionSpec.ApiLevel.IsPreview() {
+ return strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt())
+ }
+ targetSdkVersion, err := targetSdkVersionSpec.EffectiveVersionString(ctx)
+ if err != nil {
+ ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
+ }
+ return targetSdkVersion
+}
+type ManifestFixerParams struct {
+ SdkContext android.SdkContext
+ ClassLoaderContexts dexpreopt.ClassLoaderContextMap
+ IsLibrary bool
+ UseEmbeddedNativeLibs bool
+ UsesNonSdkApis bool
+ UseEmbeddedDex bool
+ HasNoCode bool
+ TestOnly bool
+ LoggingParent string
+}
+
+// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
+func ManifestFixer(ctx android.ModuleContext, manifest android.Path,
+ params ManifestFixerParams) android.Path {
var args []string
- if isLibrary {
+
+ if params.IsLibrary {
args = append(args, "--library")
- } else {
- minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
+ } else if params.SdkContext != nil {
+ minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
if minSdkVersion.FinalOrFutureInt() >= 23 {
- args = append(args, fmt.Sprintf("--extract-native-libs=%v", !useEmbeddedNativeLibs))
- } else if useEmbeddedNativeLibs {
+ 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)
}
}
- if usesNonSdkApis {
+ if params.UsesNonSdkApis {
args = append(args, "--uses-non-sdk-api")
}
- if useEmbeddedDex {
+ if params.UseEmbeddedDex {
args = append(args, "--use-embedded-dex")
}
- // manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added
- // explicitly via `uses_libs`/`optional_uses_libs`.
- requiredUsesLibs, optionalUsesLibs := classLoaderContexts.ImplicitUsesLibs()
- for _, usesLib := range requiredUsesLibs {
- args = append(args, "--uses-library", usesLib)
- }
- for _, usesLib := range optionalUsesLibs {
- args = append(args, "--optional-uses-library", usesLib)
+ if params.ClassLoaderContexts != nil {
+ // 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()
+
+ for _, usesLib := range requiredUsesLibs {
+ args = append(args, "--uses-library", usesLib)
+ }
+ for _, usesLib := range optionalUsesLibs {
+ args = append(args, "--optional-uses-library", usesLib)
+ }
}
- if hasNoCode {
+ if params.HasNoCode {
args = append(args, "--has-no-code")
}
- if loggingParent != "" {
- args = append(args, "--logging-parent", loggingParent)
- }
- var deps android.Paths
- targetSdkVersion, err := sdkContext.TargetSdkVersion(ctx).EffectiveVersionString(ctx)
- if err != nil {
- ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
- }
- if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
- targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
- deps = append(deps, ApiFingerprintPath(ctx))
+ if params.TestOnly {
+ args = append(args, "--test-only")
}
- minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
- if err != nil {
- ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ if params.LoggingParent != "" {
+ args = append(args, "--logging-parent", params.LoggingParent)
}
- if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
- minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
- deps = append(deps, ApiFingerprintPath(ctx))
+ var deps android.Paths
+ var argsMapper = make(map[string]string)
+
+ if params.SdkContext != nil {
+ targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params.SdkContext)
+ 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))
+ }
+
+ minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
+ if err != nil {
+ ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ }
+
+ if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
+ minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+ deps = append(deps, ApiFingerprintPath(ctx))
+ }
+
+ if err != nil {
+ ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ }
+ args = append(args, "--minSdkVersion ", minSdkVersion)
+ args = append(args, "--raise-min-sdk-version")
}
fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
- if err != nil {
- ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
- }
+ argsMapper["args"] = strings.Join(args, " ")
+
ctx.Build(pctx, android.BuildParams{
Rule: manifestFixerRule,
Description: "fix manifest",
Input: manifest,
Implicits: deps,
Output: fixedManifest,
- Args: map[string]string{
- "minSdkVersion": minSdkVersion,
- "targetSdkVersion": targetSdkVersion,
- "args": strings.Join(args, " "),
- },
+ Args: argsMapper,
})
return fixedManifest.WithoutRel()
diff --git a/java/android_resources.go b/java/android_resources.go
index 6864ebb..8c5908f 100644
--- a/java/android_resources.go
+++ b/java/android_resources.go
@@ -43,7 +43,7 @@
// androidResourceGlob returns the list of files in the given directory, using the standard
// exclusion patterns for Android resources.
-func androidResourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths {
+func androidResourceGlob(ctx android.EarlyModuleContext, dir android.Path) android.Paths {
return ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames)
}
diff --git a/java/androidmk.go b/java/androidmk.go
index 537159e..b930441 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -47,6 +47,7 @@
if library.dexJarFile.IsSet() {
entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile.Path())
}
+ entries.SetPath("LOCAL_SOONG_INSTALLED_MODULE", library.hostdexInstallFile)
entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
entries.SetString("LOCAL_MODULE_STEM", library.Stem()+"-hostdex")
@@ -60,6 +61,11 @@
func (library *Library) AndroidMkEntries() []android.AndroidMkEntries {
var entriesList []android.AndroidMkEntries
+ if library.Os() == android.Windows {
+ // Make does not support Windows Java modules
+ return nil
+ }
+
if library.hideApexVariantFromMake {
// For a java library built for an APEX, we don't need a Make module for itself. Otherwise, it
// will conflict with the platform variant because they have the same module name in the
@@ -176,7 +182,14 @@
}
func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries {
- if prebuilt.hideApexVariantFromMake || !prebuilt.ContainingSdk().Unversioned() {
+ if prebuilt.hideApexVariantFromMake {
+ // For a library imported from a prebuilt APEX, we don't need a Make module for itself, as we
+ // don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it
+ // is preopted.
+ dexpreoptEntries := prebuilt.dexpreopter.AndroidMkEntriesForApex()
+ return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
+ }
+ if !prebuilt.ContainingSdk().Unversioned() {
return []android.AndroidMkEntries{android.AndroidMkEntries{
Disabled: true,
}}
@@ -250,6 +263,10 @@
}
func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries {
+ if binary.Os() == android.Windows {
+ // Make does not support Windows Java modules
+ return nil
+ }
if !binary.isWrapperVariant {
return []android.AndroidMkEntries{android.AndroidMkEntries{
@@ -276,11 +293,6 @@
}}
} else {
outputFile := binary.wrapperFile
- // Have Make installation trigger Soong installation by using Soong's install path as
- // the output file.
- if binary.Host() {
- outputFile = binary.binaryFile
- }
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "EXECUTABLES",
@@ -421,8 +433,10 @@
if len(a.appProperties.Overrides) > 0 {
overridden = append(overridden, a.appProperties.Overrides...)
}
- if a.Name() != a.installApkName {
- overridden = append(overridden, a.Name())
+ // When APK name is overridden via PRODUCT_PACKAGE_NAME_OVERRIDES
+ // ensure that the original name is overridden.
+ if a.Stem() != a.installApkName {
+ overridden = append(overridden, a.Stem())
}
return overridden
}
@@ -680,7 +694,7 @@
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_CERTIFICATE", r.certificate.AndroidMkString())
- entries.SetPath("LOCAL_MODULE_PATH", r.installDir.ToMakePath())
+ entries.SetPath("LOCAL_MODULE_PATH", r.installDir)
entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", r.properties.Overrides...)
},
},
@@ -691,12 +705,12 @@
return []android.AndroidMkEntries{
android.AndroidMkEntries{
Class: "APPS",
- OutputFile: android.OptionalPathForPath(apkSet.packedOutput),
+ OutputFile: android.OptionalPathForPath(apkSet.primaryOutput),
Include: "$(BUILD_SYSTEM)/soong_android_app_set.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged())
- entries.SetString("LOCAL_APK_SET_INSTALL_FILE", apkSet.InstallFile())
+ entries.SetPath("LOCAL_APK_SET_INSTALL_FILE", apkSet.PackedAdditionalOutputs())
entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile)
entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...)
},
diff --git a/java/app.go b/java/app.go
index 6554d66..e4432ff 100755
--- a/java/app.go
+++ b/java/app.go
@@ -43,8 +43,6 @@
ctx.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
ctx.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
-
- android.RegisterBp2BuildMutator("android_app_certificate", AndroidAppCertificateBp2Build)
}
// AndroidManifest.xml merging
@@ -139,6 +137,7 @@
}
type AndroidApp struct {
+ android.BazelModuleBase
Library
aapt
android.OverridableModuleBase
@@ -291,7 +290,7 @@
if minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx); err == nil {
a.checkJniLibsSdkVersion(ctx, minSdkVersion)
- android.CheckMinSdkVersion(a, ctx, minSdkVersion)
+ android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps)
} else {
ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
}
@@ -426,7 +425,8 @@
a.aapt.splitNames = a.appProperties.Package_splits
a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent)
- a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, aaptLinkFlags...)
+ a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts,
+ a.usesLibraryProperties.Exclude_uses_libs, aaptLinkFlags...)
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
@@ -471,6 +471,7 @@
a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
a.dexpreopter.classLoaderContexts = a.classLoaderContexts
a.dexpreopter.manifestFile = a.mergedManifestFile
+ a.dexpreopter.preventInstall = a.appProperties.PreventInstall
if ctx.ModuleName() != "framework-res" {
a.Module.compile(ctx, a.aaptSrcJar)
@@ -485,7 +486,7 @@
a.jniLibs = jniLibs
if a.shouldEmbedJnis(ctx) {
jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
- a.installPathForJNISymbols = a.installPath(ctx).ToMakePath()
+ a.installPathForJNISymbols = a.installPath(ctx)
TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx))
for _, jni := range jniLibs {
if jni.coverageFile.Valid() {
@@ -621,7 +622,7 @@
a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
// Check if the install APK name needs to be overridden.
- a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
+ a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Stem())
if ctx.ModuleName() == "framework-res" {
// framework-res.apk is installed as system/framework/framework-res.apk
@@ -720,11 +721,15 @@
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
// Install the app package.
- if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() {
- ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile)
+ if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() &&
+ !a.appProperties.PreventInstall {
+
+ var extraInstalledPaths android.Paths
for _, extra := range a.extraOutputFiles {
- ctx.InstallFile(a.installDir, extra.Base(), extra)
+ installed := ctx.InstallFile(a.installDir, extra.Base(), extra)
+ extraInstalledPaths = append(extraInstalledPaths, installed)
}
+ ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...)
}
a.buildAppDependencyInfo(ctx)
@@ -938,6 +943,7 @@
android.InitDefaultableModule(module)
android.InitOverridableModule(module, &module.appProperties.Overrides)
android.InitApexModule(module)
+ android.InitBazelModule(module)
return module
}
@@ -1001,6 +1007,7 @@
command := rule.Command().BuiltTool("test_config_fixer").Input(testConfig).Output(fixedConfig)
fixNeeded := false
+ // Auto-generated test config uses `ModuleName` as the APK name. So fix it if it is not the case.
if ctx.ModuleName() != a.installApkName {
fixNeeded = true
command.FlagWithArg("--test-file-name ", a.installApkName+".apk")
@@ -1157,7 +1164,10 @@
// some of its properties.
func OverrideAndroidAppModuleFactory() android.Module {
m := &OverrideAndroidApp{}
- m.AddProperties(&overridableAppProperties{})
+ m.AddProperties(
+ &OverridableDeviceProperties{},
+ &overridableAppProperties{},
+ )
android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
android.InitOverrideModule(m)
@@ -1202,6 +1212,23 @@
// libraries, because SDK ones are automatically picked up by Soong. The <uses-library> name
// normally is the same as the module name, but there are exceptions.
Provides_uses_lib *string
+
+ // A list of shared library names to exclude from the classpath of the APK. Adding a library here
+ // will prevent it from being used when precompiling the APK and prevent it from being implicitly
+ // added to the APK's manifest's <uses-library> elements.
+ //
+ // Care must be taken when using this as it could result in runtime errors if the APK actually
+ // uses classes provided by the library and which are not provided in any other way.
+ //
+ // This is primarily intended for use by various CTS tests that check the runtime handling of the
+ // android.test.base shared library (and related libraries) but which depend on some common
+ // libraries that depend on the android.test.base library. Without this those tests will end up
+ // with a <uses-library android:name="android.test.base"/> in their manifest which would either
+ // render the tests worthless (as they would be testing the wrong behavior), or would break the
+ // test altogether by providing access to classes that the tests were not expecting. Those tests
+ // provide the android.test.base statically and use jarjar to rename them so they do not collide
+ // with the classes provided by the android.test.base library.
+ Exclude_uses_libs []string
}
// usesLibrary provides properties and helper functions for AndroidApp and AndroidAppImport to verify that the
@@ -1400,23 +1427,11 @@
Certificate string
}
-func AndroidAppCertificateBp2Build(ctx android.TopDownMutatorContext) {
- module, ok := ctx.Module().(*AndroidAppCertificate)
- if !ok {
- // Not an Android app certificate
- return
- }
- if !module.ConvertWithBp2build(ctx) {
- return
- }
- if ctx.ModuleType() != "android_app_certificate" {
- return
- }
-
- androidAppCertificateBp2BuildInternal(ctx, module)
+func (m *AndroidAppCertificate) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ androidAppCertificateBp2Build(ctx, m)
}
-func androidAppCertificateBp2BuildInternal(ctx android.TopDownMutatorContext, module *AndroidAppCertificate) {
+func androidAppCertificateBp2Build(ctx android.TopDownMutatorContext, module *AndroidAppCertificate) {
var certificate string
if module.properties.Certificate != nil {
certificate = *module.properties.Certificate
@@ -1433,3 +1448,38 @@
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
}
+
+type bazelAndroidAppAttributes struct {
+ *javaLibraryAttributes
+ Manifest bazel.Label
+ Custom_package *string
+ Resource_files bazel.LabelListAttribute
+}
+
+// ConvertWithBp2build is used to convert android_app to Bazel.
+func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ libAttrs := a.convertLibraryAttrsBp2Build(ctx)
+
+ 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...)
+ }
+
+ attrs := &bazelAndroidAppAttributes{
+ libAttrs,
+ android.BazelLabelForModuleSrcSingle(ctx, manifest),
+ // TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
+ a.overridableAppProperties.Package_name,
+ bazel.MakeLabelListAttribute(resourceFiles),
+ }
+ props := bazel.BazelTargetModuleProperties{Rule_class: "android_binary",
+ Bzl_load_location: "@rules_android//rules:rules.bzl"}
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
+
+}
diff --git a/java/app_set.go b/java/app_set.go
index 6b25638..694b167 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -55,10 +55,10 @@
android.DefaultableModuleBase
prebuilt android.Prebuilt
- properties AndroidAppSetProperties
- packedOutput android.WritablePath
- installFile string
- apkcertsFile android.ModuleOutPath
+ properties AndroidAppSetProperties
+ packedOutput android.WritablePath
+ primaryOutput android.WritablePath
+ apkcertsFile android.ModuleOutPath
}
func (as *AndroidAppSet) Name() string {
@@ -78,11 +78,11 @@
}
func (as *AndroidAppSet) OutputFile() android.Path {
- return as.packedOutput
+ return as.primaryOutput
}
-func (as *AndroidAppSet) InstallFile() string {
- return as.installFile
+func (as *AndroidAppSet) PackedAdditionalOutputs() android.Path {
+ return as.packedOutput
}
func (as *AndroidAppSet) APKCertsFile() android.Path {
@@ -114,11 +114,11 @@
func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+ as.primaryOutput = android.PathForModuleOut(ctx, as.BaseModuleName()+".apk")
as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
// We are assuming here that the install file in the APK
// set has `.apk` suffix. If it doesn't the build will fail.
// APK sets containing APEX files are handled elsewhere.
- as.installFile = as.BaseModuleName() + ".apk"
screenDensities := "all"
if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 {
screenDensities = strings.ToUpper(strings.Join(dpis, ","))
@@ -127,11 +127,11 @@
// TODO(asmundak): do we support device features
ctx.Build(pctx,
android.BuildParams{
- Rule: extractMatchingApks,
- Description: "Extract APKs from APK set",
- Output: as.packedOutput,
- ImplicitOutput: as.apkcertsFile,
- Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)},
+ Rule: extractMatchingApks,
+ Description: "Extract APKs from APK set",
+ Output: as.primaryOutput,
+ ImplicitOutputs: android.WritablePaths{as.packedOutput, as.apkcertsFile},
+ Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)},
Args: map[string]string{
"abis": strings.Join(SupportedAbis(ctx), ","),
"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
@@ -140,10 +140,21 @@
"stem": as.BaseModuleName(),
"apkcerts": as.apkcertsFile.String(),
"partition": as.PartitionTag(ctx.DeviceConfig()),
+ "zip": as.packedOutput.String(),
},
})
+
+ var installDir android.InstallPath
+ if as.Privileged() {
+ installDir = android.PathForModuleInstall(ctx, "priv-app", as.BaseModuleName())
+ } else {
+ installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName())
+ }
+ ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk", as.primaryOutput, as.packedOutput)
}
+func (as *AndroidAppSet) InstallBypassMake() bool { return true }
+
// android_app_set extracts a set of APKs based on the target device
// configuration and installs this set as "split APKs".
// The extracted set always contains an APK whose name is
diff --git a/java/app_set_test.go b/java/app_set_test.go
index adaf71b..03eb667 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -17,19 +17,20 @@
import (
"fmt"
"reflect"
+ "strings"
"testing"
"android/soong/android"
)
func TestAndroidAppSet(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
android_app_set {
name: "foo",
set: "prebuilts/apks/app.apks",
prerelease: true,
}`)
- module := ctx.ModuleForTests("foo", "android_common")
+ module := result.ModuleForTests("foo", "android_common")
const packedSplitApks = "foo.zip"
params := module.Output(packedSplitApks)
if params.Rule == nil {
@@ -41,9 +42,22 @@
if s := params.Args["partition"]; s != "system" {
t.Errorf("wrong partition value: '%s', expected 'system'", s)
}
- mkEntries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
+
+ android.AssertPathRelativeToTopEquals(t, "incorrect output path",
+ "out/soong/.intermediates/foo/android_common/foo.apk", params.Output)
+
+ android.AssertPathsRelativeToTopEquals(t, "incorrect implicit output paths",
+ []string{
+ "out/soong/.intermediates/foo/android_common/foo.zip",
+ "out/soong/.intermediates/foo/android_common/apkcerts.txt",
+ },
+ params.ImplicitOutputs.Paths())
+
+ mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
- expectedInstallFile := []string{"foo.apk"}
+ expectedInstallFile := []string{
+ strings.Replace(params.ImplicitOutputs[0].String(), android.OutSoongDir, result.Config.SoongOutDir(), 1),
+ }
if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',",
actualInstallFile, expectedInstallFile)
diff --git a/java/app_test.go b/java/app_test.go
index 07439fc..16bbec1 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -144,14 +144,14 @@
}
`)
- testJavaError(t, "platform_apis must be true when sdk_version is empty.", `
+ testJavaError(t, "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true", `
android_app {
name: "bar",
srcs: ["b.java"],
}
`)
- testJavaError(t, "platform_apis must be false when sdk_version is not empty.", `
+ testJavaError(t, "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.", `
android_app {
name: "bar",
srcs: ["b.java"],
@@ -1707,7 +1707,7 @@
},
},
{
- name: "overridden",
+ name: "overridden via PRODUCT_PACKAGE_NAME_OVERRIDES",
bp: `
android_app {
name: "foo",
@@ -1722,6 +1722,22 @@
"out/soong/target/product/test_device/system/app/bar/bar.apk",
},
},
+ {
+ name: "overridden via stem",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ stem: "bar",
+ }
+ `,
+ packageNameOverride: "",
+ expected: []string{
+ "out/soong/.intermediates/foo/android_common/bar.apk",
+ "out/soong/target/product/test_device/system/app/bar/bar.apk",
+ },
+ },
}
for _, test := range testCases {
@@ -1965,6 +1981,80 @@
}
}
+func TestOverrideAndroidAppStem(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ }
+ override_android_app {
+ name: "bar",
+ base: "foo",
+ }
+ override_android_app {
+ name: "baz",
+ base: "foo",
+ stem: "baz_stem",
+ }
+ android_app {
+ name: "foo2",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ stem: "foo2_stem",
+ }
+ override_android_app {
+ name: "bar2",
+ base: "foo2",
+ }
+ override_android_app {
+ name: "baz2",
+ base: "foo2",
+ stem: "baz2_stem",
+ }
+ `)
+ for _, expected := range []struct {
+ moduleName string
+ variantName string
+ apkPath string
+ }{
+ {
+ moduleName: "foo",
+ variantName: "android_common",
+ apkPath: "out/soong/target/product/test_device/system/app/foo/foo.apk",
+ },
+ {
+ moduleName: "foo",
+ variantName: "android_common_bar",
+ apkPath: "out/soong/target/product/test_device/system/app/bar/bar.apk",
+ },
+ {
+ moduleName: "foo",
+ variantName: "android_common_baz",
+ apkPath: "out/soong/target/product/test_device/system/app/baz_stem/baz_stem.apk",
+ },
+ {
+ moduleName: "foo2",
+ variantName: "android_common",
+ apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+ },
+ {
+ moduleName: "foo2",
+ variantName: "android_common_bar2",
+ // Note that this may cause the duplicate output error.
+ apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+ },
+ {
+ moduleName: "foo2",
+ variantName: "android_common_baz2",
+ apkPath: "out/soong/target/product/test_device/system/app/baz2_stem/baz2_stem.apk",
+ },
+ } {
+ variant := ctx.ModuleForTests(expected.moduleName, expected.variantName)
+ variant.Output(expected.apkPath)
+ }
+}
+
func TestOverrideAndroidAppDependency(t *testing.T) {
ctx, _ := testJava(t, `
android_app {
@@ -2168,10 +2258,33 @@
t.Errorf("test_config_fixer was not expected to run, but did: %q", params.RuleParams.Command)
}
}
-
}
}
+func TestInstrumentationTargetPrebuilt(t *testing.T) {
+ bp := `
+ android_app_import {
+ name: "foo",
+ apk: "foo.apk",
+ presigned: true,
+ }
+
+ android_test {
+ name: "bar",
+ srcs: ["a.java"],
+ instrumentation_for: "foo",
+ sdk_version: "current",
+ }
+ `
+
+ android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ ).ExtendWithErrorHandler(
+ android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ "instrumentation_for: dependency \"foo\" of type \"android_app_import\" does not provide JavaInfo so is unsuitable for use with this property")).
+ RunTestWithBp(t, bp)
+}
+
func TestStl(t *testing.T) {
ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
@@ -2399,7 +2512,7 @@
`--uses-library qux ` +
`--uses-library quuz ` +
`--uses-library runtime-library`
- android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs)
+ android.AssertStringDoesContain(t, "manifest_fixer args", actualManifestFixerArgs, expectManifestFixerArgs)
// Test that all libraries are verified (library order matters).
verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command
@@ -2873,3 +2986,76 @@
t.Errorf("App does not use library proguard config")
}
}
+
+func TestTargetSdkVersionManifestFixer(t *testing.T) {
+ platform_sdk_codename := "Tiramisu"
+ testCases := []struct {
+ name string
+ targetSdkVersionInBp string
+ targetSdkVersionExpected string
+ unbundledBuild bool
+ }{
+ {
+ name: "Non-Unbundled build: Android.bp has targetSdkVersion",
+ targetSdkVersionInBp: "30",
+ targetSdkVersionExpected: "30",
+ unbundledBuild: false,
+ },
+ {
+ name: "Unbundled build: Android.bp has targetSdkVersion",
+ targetSdkVersionInBp: "30",
+ targetSdkVersionExpected: "30",
+ unbundledBuild: true,
+ },
+ {
+ name: "Non-Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename",
+ targetSdkVersionInBp: platform_sdk_codename,
+ targetSdkVersionExpected: platform_sdk_codename,
+ unbundledBuild: false,
+ },
+ {
+ name: "Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename",
+ targetSdkVersionInBp: platform_sdk_codename,
+ targetSdkVersionExpected: "10000",
+ unbundledBuild: true,
+ },
+
+ {
+ name: "Non-Unbundled build: Android.bp has no targetSdkVersion",
+ targetSdkVersionExpected: platform_sdk_codename,
+ unbundledBuild: false,
+ },
+ {
+ name: "Unbundled build: Android.bp has no targetSdkVersion",
+ targetSdkVersionExpected: "10000",
+ unbundledBuild: true,
+ },
+ }
+ for _, testCase := range testCases {
+ bp := fmt.Sprintf(`
+ android_app {
+ name: "foo",
+ sdk_version: "current",
+ target_sdk_version: "%v",
+ }
+ `, testCase.targetSdkVersionInBp)
+ fixture := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ // explicitly set platform_sdk_codename to make the test deterministic
+ variables.Platform_sdk_codename = &platform_sdk_codename
+ variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+ // create a non-empty list if unbundledBuild==true
+ if testCase.unbundledBuild {
+ variables.Unbundled_build_apps = []string{"apex_a", "apex_b"}
+ }
+ }),
+ )
+
+ result := fixture.RunTestWithBp(t, bp)
+ foo := result.ModuleForTests("foo", "android_common")
+
+ manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+ android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected)
+ }
+}
diff --git a/java/base.go b/java/base.go
index ca34f2e..42d7733 100644
--- a/java/base.go
+++ b/java/base.go
@@ -184,16 +184,25 @@
// Properties that are specific to device modules. Host module factories should not add these when
// constructing a new module.
type DeviceProperties struct {
- // if not blank, set to the version of the sdk to compile against.
+ // If not blank, set to the version of the sdk to compile against.
// Defaults to compiling against the current platform.
+ // Values are of one of the following forms:
+ // 1) numerical API level or "current"
+ // 2) An SDK kind with an API level: "<sdk kind>_<API level>". See
+ // build/soong/android/sdk_version.go for the complete and up to date list of
+ // SDK kinds. If the SDK kind value is empty, it will be set to public.
Sdk_version *string
// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
- // Defaults to sdk_version if not set.
+ // Defaults to sdk_version if not set. See sdk_version for possible values.
Min_sdk_version *string
+ // if not blank, set the maximum version of the sdk that the compiled artifacts will run against.
+ // Defaults to empty string "". See sdk_version for possible values.
+ Max_sdk_version *string
+
// if not blank, set the targetSdkVersion in the AndroidManifest.xml.
- // Defaults to sdk_version if not set.
+ // Defaults to sdk_version if not set. See sdk_version for possible values.
Target_sdk_version *string
// Whether to compile against the platform APIs instead of an SDK.
@@ -236,9 +245,6 @@
// otherwise provides defaults libraries to add to the bootclasspath.
System_modules *string
- // set the name of the output
- Stem *string
-
IsSDKLibrary bool `blueprint:"mutated"`
// If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file.
@@ -250,6 +256,15 @@
SyspropPublicStub string `blueprint:"mutated"`
}
+// Device properties that can be overridden by overriding module (e.g. override_android_app)
+type OverridableDeviceProperties struct {
+ // set the name of the output. If not set, `name` is used.
+ // To override a module with this property set, overriding module might need to set this as well.
+ // Otherwise, both the overridden and the overriding modules will have the same output name, which
+ // can cause the duplicate output error.
+ Stem *string
+}
+
// Functionality common to Module and Import
//
// It is embedded in Module so its functionality can be used by methods in Module
@@ -363,6 +378,7 @@
android.DefaultableModuleBase
android.ApexModuleBase
android.SdkBase
+ android.BazelModuleBase
// Functionality common to Module and Import.
embeddableInModuleAndImport
@@ -371,6 +387,8 @@
protoProperties android.ProtoProperties
deviceProperties DeviceProperties
+ overridableDeviceProperties OverridableDeviceProperties
+
// jar file containing header classes including static library dependencies, suitable for
// inserting into the bootclasspath/classpath of another compile
headerJarFile android.Path
@@ -407,6 +425,9 @@
// installed file for binary dependency
installFile android.Path
+ // 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
@@ -452,6 +473,7 @@
sdkVersion android.SdkSpec
minSdkVersion android.SdkSpec
+ maxSdkVersion android.SdkSpec
}
func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -502,9 +524,9 @@
usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
sdkVersionSpecified := sc.SdkVersion(ctx).Specified()
if usePlatformAPI && sdkVersionSpecified {
- ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
+ ctx.PropertyErrorf("platform_apis", "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.")
} else if !usePlatformAPI && !sdkVersionSpecified {
- ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
+ ctx.PropertyErrorf("platform_apis", "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true")
}
}
@@ -522,6 +544,7 @@
j.addHostProperties()
j.AddProperties(
&j.deviceProperties,
+ &j.overridableDeviceProperties,
&j.dexer.dexProperties,
&j.dexpreoptProperties,
&j.linter.properties,
@@ -609,6 +632,13 @@
return j.SdkVersion(ctx)
}
+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) MinSdkVersionString() string {
return j.minSdkVersion.Raw
}
@@ -784,6 +814,9 @@
flags = append(flags, "--transaction_names")
}
+ aidlMinSdkVersion := j.MinSdkVersion(ctx).ApiLevel.String()
+ flags = append(flags, "--min_sdk_version="+aidlMinSdkVersion)
+
return strings.Join(flags, " "), deps
}
@@ -935,6 +968,7 @@
if flags.javaVersion.usesJavaModules() {
j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
}
+
srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
if hasSrcExt(srcFiles.Strings(), ".proto") {
flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
@@ -1055,7 +1089,7 @@
j.compiledSrcJars = srcJars
enableSharding := false
- var headerJarFileWithoutJarjar android.Path
+ var headerJarFileWithoutDepsOrJarjar android.Path
if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine {
if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 {
enableSharding = true
@@ -1065,7 +1099,7 @@
// allow for the use of annotation processors that do function correctly
// with sharding enabled. See: b/77284273.
}
- headerJarFileWithoutJarjar, j.headerJarFile =
+ headerJarFileWithoutDepsOrJarjar, j.headerJarFile =
j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinJars)
if ctx.Failed() {
return
@@ -1094,7 +1128,9 @@
}
if enableSharding {
- flags.classpath = append(flags.classpath, headerJarFileWithoutJarjar)
+ if headerJarFileWithoutDepsOrJarjar != nil {
+ flags.classpath = append(classpath{headerJarFileWithoutDepsOrJarjar}, flags.classpath...)
+ }
shardSize := int(*(j.properties.Javac_shard_size))
var shardSrcs []android.Paths
if len(uniqueSrcFiles) > 0 {
@@ -1497,7 +1533,7 @@
func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
deps deps, flags javaBuilderFlags, jarName string,
- extraJars android.Paths) (headerJar, jarjarHeaderJar android.Path) {
+ extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar android.Path) {
var jars android.Paths
if len(srcFiles) > 0 || len(srcJars) > 0 {
@@ -1508,6 +1544,7 @@
return nil, nil
}
jars = append(jars, turbineJar)
+ headerJar = turbineJar
}
jars = append(jars, extraJars...)
@@ -1521,20 +1558,19 @@
combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName)
TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{},
false, nil, []string{"META-INF/TRANSITIVE"})
- headerJar = combinedJar
- jarjarHeaderJar = combinedJar
+ jarjarAndDepsHeaderJar = combinedJar
if j.expandJarjarRules != nil {
// Transform classes.jar into classes-jarjar.jar
jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName)
- TransformJarJar(ctx, jarjarFile, headerJar, j.expandJarjarRules)
- jarjarHeaderJar = jarjarFile
+ TransformJarJar(ctx, jarjarFile, jarjarAndDepsHeaderJar, j.expandJarjarRules)
+ jarjarAndDepsHeaderJar = jarjarFile
if ctx.Failed() {
return nil, nil
}
}
- return headerJar, jarjarHeaderJar
+ return headerJar, jarjarAndDepsHeaderJar
}
func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
@@ -1618,8 +1654,7 @@
}
// Implements android.ApexModule
-func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
- sdkVersion android.ApiLevel) error {
+func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
sdkSpec := j.MinSdkVersion(ctx)
if !sdkSpec.Specified() {
return fmt.Errorf("min_sdk_version is not specified")
@@ -1634,7 +1669,7 @@
}
func (j *Module) Stem() string {
- return proptools.StringDefault(j.deviceProperties.Stem, j.Name())
+ return proptools.StringDefault(j.overridableDeviceProperties.Stem, j.Name())
}
func (j *Module) JacocoReportClassesFile() android.Path {
@@ -1899,6 +1934,9 @@
sm := module.(SystemModulesProvider)
outputDir, outputDeps := sm.OutputDirAndDeps()
deps.systemModules = &systemModules{outputDir, outputDeps}
+
+ case instrumentationForTag:
+ ctx.PropertyErrorf("instrumentation_for", "dependency %q of type %q does not provide JavaInfo so is unsuitable for use with this property", ctx.OtherModuleName(module), ctx.OtherModuleType(module))
}
}
@@ -1928,3 +1966,16 @@
}
var _ ModuleWithStem = (*Module)(nil)
+
+func (j *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ switch ctx.ModuleType() {
+ case "java_library", "java_library_host", "java_library_static":
+ if lib, ok := ctx.Module().(*Library); ok {
+ javaLibraryBp2Build(ctx, lib)
+ }
+ case "java_binary_host":
+ if binary, ok := ctx.Module().(*Binary); ok {
+ javaBinaryHostBp2Build(ctx, binary)
+ }
+ }
+}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index ced0048..5fe409e 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -139,6 +139,74 @@
BootclasspathFragmentsDepsProperties
}
+type SourceOnlyBootclasspathProperties struct {
+ Hidden_api struct {
+ // Contains prefixes of a package hierarchy that is provided solely by this
+ // bootclasspath_fragment.
+ //
+ // This affects the signature patterns file that is used to select the subset of monolithic
+ // hidden API flags. See split_packages property for more details.
+ Package_prefixes []string
+
+ // The list of split packages provided by this bootclasspath_fragment.
+ //
+ // A split package is one that contains classes which are provided by multiple
+ // bootclasspath_fragment modules.
+ //
+ // This defaults to "*" - which treats all packages as being split. A module that has no split
+ // packages must specify an empty list.
+ //
+ // This affects the signature patterns file that is generated by a bootclasspath_fragment and
+ // used to select the subset of monolithic hidden API flags against which the flags generated
+ // by the bootclasspath_fragment are compared.
+ //
+ // The signature patterns file selects the subset of monolithic hidden API flags using a number
+ // of patterns, i.e.:
+ // * The qualified name (including package) of an outermost class, e.g. java/lang/Character.
+ // This selects all the flags for all the members of this class and any nested classes.
+ // * A package wildcard, e.g. java/lang/*. This selects all the flags for all the members of all
+ // the classes in this package (but not in sub-packages).
+ // * A recursive package wildcard, e.g. java/**. This selects all the flags for all the members
+ // of all the classes in this package and sub-packages.
+ //
+ // The signature patterns file is constructed as follows:
+ // * All the signatures are retrieved from the all-flags.csv file.
+ // * The member and inner class names are removed.
+ // * If a class is in a split package then that is kept, otherwise the class part is removed
+ // and replaced with a wildcard, i.e. *.
+ // * If a package matches a package prefix then the package is removed.
+ // * All the package prefixes are added with a recursive wildcard appended to each, i.e. **.
+ // * The resulting patterns are sorted.
+ //
+ // So, by default (i.e. without specifying any package_prefixes or split_packages) the signature
+ // patterns is a list of class names, because there are no package packages and all packages are
+ // assumed to be split.
+ //
+ // If any split packages are specified then only those packages are treated as split and all
+ // other packages are treated as belonging solely to the bootclasspath_fragment and so they use
+ // wildcard package patterns.
+ //
+ // So, if an empty list of split packages is specified then the signature patterns file just
+ // includes a wildcard package pattern for every package provided by the bootclasspath_fragment.
+ //
+ // If split_packages are specified and a package that is split is not listed then it could lead
+ // to build failures as it will select monolithic flags that are generated by another
+ // bootclasspath_fragment to compare against the flags provided by this fragment. The latter
+ // will obviously not contain those flags and that can cause the comparison and build to fail.
+ //
+ // If any package prefixes are specified then any matching packages are removed from the
+ // signature patterns and replaced with a single recursive package pattern.
+ //
+ // It is not strictly necessary to specify either package_prefixes or split_packages as the
+ // defaults will produce a valid set of signature patterns. However, those patterns may include
+ // implementation details, e.g. names of implementation classes or packages, which will be
+ // exported to the sdk snapshot in the signature patterns file. That is something that should be
+ // avoided where possible. Specifying package_prefixes and split_packages allows those
+ // implementation details to be excluded from the snapshot.
+ Split_packages []string
+ }
+}
+
type BootclasspathFragmentModule struct {
android.ModuleBase
android.ApexModuleBase
@@ -147,8 +215,15 @@
properties bootclasspathFragmentProperties
+ sourceOnlyProperties SourceOnlyBootclasspathProperties
+
// 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
}
// commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
@@ -180,7 +255,7 @@
func bootclasspathFragmentFactory() android.Module {
m := &BootclasspathFragmentModule{}
- m.AddProperties(&m.properties)
+ m.AddProperties(&m.properties, &m.sourceOnlyProperties)
android.InitApexModule(m)
android.InitSdkAwareModule(m)
initClasspathFragment(m, BOOTCLASSPATH)
@@ -317,9 +392,19 @@
// 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
+
+ // Path to the image profile file on host (or empty, if profile is not generated).
+ profilePathOnHost android.Path
+
+ // Install path of the boot image profile if it needs to be installed in the APEX, or empty if not
+ // needed.
+ profileInstallPathInApex string
}
func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList {
@@ -333,6 +418,11 @@
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.
@@ -348,6 +438,14 @@
}
}
+func (i BootclasspathFragmentApexContentInfo) ProfilePathOnHost() android.Path {
+ return i.profilePathOnHost
+}
+
+func (i BootclasspathFragmentApexContentInfo) ProfileInstallPathInApex() string {
+ return i.profileInstallPathInApex
+}
+
func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
tag := ctx.OtherModuleDependencyTag(dep)
if IsBootclasspathFragmentContentDepTag(tag) {
@@ -465,6 +563,24 @@
// Copy the dex jars of this fragment's content modules to their predefined locations.
copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
}
+
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.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,
+ })
+ }
+ }
}
// A prebuilt fragment cannot contribute to an apex.
@@ -509,6 +625,13 @@
if imageConfig != nil {
info.modules = imageConfig.modules
+ global := dexpreopt.GetGlobalConfig(ctx)
+ if !global.DisableGenerateProfile {
+ info.profilePathOnHost = imageConfig.profilePathOnHost
+ info.profileInstallPathInApex = imageConfig.profileInstallPathInApex
+ }
+
+ info.shouldInstallBootImageInApex = imageConfig.shouldInstallInApex()
}
info.bootImageFilesByArch = bootImageFilesByArch
@@ -546,6 +669,10 @@
// 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()) {
unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents)
_, unknown = android.RemoveFromList("core-icu4j", unknown)
@@ -590,7 +717,7 @@
// TODO(b/192868581): Remove once the source and prebuilts provide a signature patterns file of
// their own.
if output.SignaturePatternsPath == nil {
- output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath)
+ output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath, []string{"*"}, nil)
}
// Initialize a HiddenAPIInfo structure.
@@ -659,7 +786,20 @@
func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
// Generate the rules to create the hidden API flags and update the supplied hiddenAPIInfo with the
// paths to the created files.
- return hiddenAPIRulesForBootclasspathFragment(ctx, contents, input)
+ output := hiddenAPIRulesForBootclasspathFragment(ctx, contents, input)
+
+ // If the module specifies split_packages or package_prefixes then use those to generate the
+ // signature patterns.
+ splitPackages := b.sourceOnlyProperties.Hidden_api.Split_packages
+ packagePrefixes := b.sourceOnlyProperties.Hidden_api.Package_prefixes
+ if splitPackages != nil || packagePrefixes != nil {
+ if splitPackages == nil {
+ splitPackages = []string{"*"}
+ }
+ output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath, splitPackages, packagePrefixes)
+ }
+
+ return output
}
// produceBootImageFiles builds the boot image files from the source if it is required.
@@ -708,6 +848,23 @@
return androidBootImageFilesByArch
}
+func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries {
+ var entriesList []android.AndroidMkEntries
+ 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
+}
+
// Collect information for opening IDE project files in java/jdeps.go.
func (b *BootclasspathFragmentModule) IDEInfo(dpInfo *android.IdeInfo) {
dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...)
@@ -774,13 +931,13 @@
All_flags_path android.OptionalPath `supported_build_releases:"S"`
// The path to the generated signature-patterns.csv file.
- Signature_patterns_path android.OptionalPath `supported_build_releases:"T+"`
+ Signature_patterns_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
// The path to the generated filtered-stub-flags.csv file.
- Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"T+"`
+ Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
// The path to the generated filtered-flags.csv file.
- Filtered_flags_path android.OptionalPath `supported_build_releases:"T+"`
+ Filtered_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
}
func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -916,7 +1073,7 @@
// At the moment this is basically just a bootclasspath_fragment module that can be used as a
// prebuilt. Eventually as more functionality is migrated into the bootclasspath_fragment module
// type from the various singletons then this will diverge.
-type prebuiltBootclasspathFragmentModule struct {
+type PrebuiltBootclasspathFragmentModule struct {
BootclasspathFragmentModule
prebuilt android.Prebuilt
@@ -924,16 +1081,16 @@
prebuiltProperties prebuiltBootclasspathFragmentProperties
}
-func (module *prebuiltBootclasspathFragmentModule) Prebuilt() *android.Prebuilt {
+func (module *PrebuiltBootclasspathFragmentModule) Prebuilt() *android.Prebuilt {
return &module.prebuilt
}
-func (module *prebuiltBootclasspathFragmentModule) Name() string {
+func (module *PrebuiltBootclasspathFragmentModule) Name() string {
return module.prebuilt.Name(module.ModuleBase.Name())
}
// produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified.
-func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
+func (module *PrebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
pathForOptionalSrc := func(src *string, defaultPath android.Path) android.Path {
if src == nil {
return defaultPath
@@ -974,7 +1131,7 @@
}
// produceBootImageFiles extracts the boot image files from the APEX if available.
-func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
+func (module *PrebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
return nil
}
@@ -984,37 +1141,53 @@
return nil // An error has been reported by FindDeapexerProviderForModule.
}
- files := bootImageFilesByArch{}
- for _, variant := range imageConfig.apexVariants() {
- arch := variant.target.Arch.ArchType
- for _, toPath := range variant.imagesDeps {
- apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
- // Get the path to the file that the deapexer extracted from the prebuilt apex file.
- fromPath := di.PrebuiltExportPath(apexRelativePath)
-
- // Return the toPath as the calling code expects the paths in the returned map to be the
- // paths predefined in the bootImageConfig.
- files[arch] = append(files[arch], toPath)
-
- // Copy the file to the predefined location.
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Input: fromPath,
- Output: toPath,
- })
- }
+ profile := (android.WritablePath)(nil)
+ if imageConfig.profileInstallPathInApex != "" {
+ profile = di.PrebuiltExportPath(imageConfig.profileInstallPathInApex)
}
- // Build the boot image files for the host variants. These are built from the dex files provided
- // by the contents of this module as prebuilt versions of the host boot image files are not
- // available, i.e. there is no host specific prebuilt apex containing them. This has to be built
- // without a profile as the prebuilt modules do not provide a profile.
- buildBootImageVariantsForBuildOs(ctx, imageConfig, nil)
+ // Build the boot image files for the host variants. These are always built from the dex files
+ // provided by the contents of this module as prebuilt versions of the host boot image files are
+ // not available, i.e. there is no host specific prebuilt apex containing them. This has to be
+ // built without a profile as the prebuilt modules do not provide a profile.
+ buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
- return files
+ if imageConfig.shouldInstallInApex() {
+ // If the boot image files for the android variants are in the prebuilt apex, we must use those
+ // rather than building new ones because those boot image files are going to be used on device.
+ files := bootImageFilesByArch{}
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, toPath := range variant.imagesDeps {
+ apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
+ // Get the path to the file that the deapexer extracted from the prebuilt apex file.
+ fromPath := di.PrebuiltExportPath(apexRelativePath)
+
+ // Return the toPath as the calling code expects the paths in the returned map to be the
+ // paths predefined in the bootImageConfig.
+ files[arch] = append(files[arch], toPath)
+
+ // Copy the file to the predefined location.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: fromPath,
+ Output: toPath,
+ })
+ }
+ }
+ return files
+ } else {
+ if profile == nil {
+ ctx.ModuleErrorf("Unable to produce boot image files: neither boot image files nor profiles exists in the prebuilt apex")
+ return nil
+ }
+ // Build boot image files for the android variants from the dex files provided by the contents
+ // of this module.
+ return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+ }
}
-var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
+var _ commonBootclasspathFragment = (*PrebuiltBootclasspathFragmentModule)(nil)
// createBootImageTag creates the tag to uniquely identify the boot image file among all of the
// files that a module requires from the prebuilt .apex file.
@@ -1028,16 +1201,22 @@
//
// If there is no image config associated with this fragment then it returns nil. Otherwise, it
// returns the files that are listed in the image config.
-func (module *prebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+func (module *PrebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
imageConfig := module.getImageConfig(ctx)
if imageConfig != nil {
- // Add the boot image files, e.g. .art, .oat and .vdex files.
files := []string{}
- for _, variant := range imageConfig.apexVariants() {
- arch := variant.target.Arch.ArchType
- for _, path := range variant.imagesDeps.Paths() {
- base := path.Base()
- files = append(files, apexRootRelativePathToBootImageFile(arch, base))
+ if imageConfig.profileInstallPathInApex != "" {
+ // Add the boot image profile.
+ files = append(files, imageConfig.profileInstallPathInApex)
+ }
+ if imageConfig.shouldInstallInApex() {
+ // Add the boot image files, e.g. .art, .oat and .vdex files.
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, path := range variant.imagesDeps.Paths() {
+ base := path.Base()
+ files = append(files, apexRootRelativePathToBootImageFile(arch, base))
+ }
}
}
return files
@@ -1049,10 +1228,10 @@
return filepath.Join("javalib", arch.String(), base)
}
-var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltBootclasspathFragmentModule)(nil)
+var _ android.RequiredFilesFromPrebuiltApex = (*PrebuiltBootclasspathFragmentModule)(nil)
func prebuiltBootclasspathFragmentFactory() android.Module {
- m := &prebuiltBootclasspathFragmentModule{}
+ m := &PrebuiltBootclasspathFragmentModule{}
m.AddProperties(&m.properties, &m.prebuiltProperties)
// This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs
// array.
diff --git a/java/builder.go b/java/builder.go
index ae124a3..e64a61f 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -120,14 +120,14 @@
"extractMatchingApks",
blueprint.RuleParams{
Command: `rm -rf "$out" && ` +
- `${config.ExtractApksCmd} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
+ `${config.ExtractApksCmd} -o "${out}" -zip "${zip}" -allow-prereleased=${allow-prereleased} ` +
`-sdk-version=${sdk-version} -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")
+ "abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition", "zip")
turbine, turbineRE = pctx.RemoteStaticRules("turbine",
blueprint.RuleParams{
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index f63d81d..ca27528 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -25,7 +25,7 @@
"android/soong/android"
)
-// Build rules and utilities to generate individual packages/modules/SdkExtensions/proto/classpaths.proto
+// Build rules and utilities to generate individual packages/modules/common/proto/classpaths.proto
// config files based on build configuration to embed into /system and /apex on a device.
//
// See `derive_classpath` service that reads the configs at runtime and defines *CLASSPATH variables
@@ -34,14 +34,15 @@
type classpathType int
const (
- // Matches definition in packages/modules/SdkExtensions/proto/classpaths.proto
+ // Matches definition in packages/modules/common/proto/classpaths.proto
BOOTCLASSPATH classpathType = iota
DEX2OATBOOTCLASSPATH
SYSTEMSERVERCLASSPATH
+ STANDALONE_SYSTEMSERVER_JARS
)
func (c classpathType) String() string {
- return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH"}[c]
+ return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"}[c]
}
type classpathFragmentProperties struct {
@@ -84,11 +85,10 @@
// Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto
type classpathJar struct {
- path string
- classpath classpathType
- // TODO(satayev): propagate min/max sdk versions for the jars
- minSdkVersion int32
- maxSdkVersion int32
+ path string
+ classpath classpathType
+ minSdkVersion string
+ maxSdkVersion string
}
// gatherPossibleApexModuleNamesAndStems returns a set of module and stem names from the
@@ -120,10 +120,32 @@
jars := make([]classpathJar, 0, len(paths)*len(classpaths))
for i := 0; i < len(paths); i++ {
for _, classpathType := range classpaths {
- jars = append(jars, classpathJar{
+ jar := classpathJar{
classpath: classpathType,
path: paths[i],
+ }
+ ctx.VisitDirectDepsIf(func(m android.Module) bool {
+ return m.Name() == configuredJars.Jar(i)
+ }, func(m android.Module) {
+ 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() {
+ jar.minSdkVersion = ctx.Config().LatestPreviewApiLevel().String()
+ } else {
+ jar.minSdkVersion = s.minSdkVersion.ApiLevel.String()
+ }
+ }
+ if s.maxSdkVersion.Specified() {
+ if s.maxSdkVersion.ApiLevel.IsCurrent() {
+ jar.maxSdkVersion = ctx.Config().LatestPreviewApiLevel().String()
+ } else {
+ jar.maxSdkVersion = s.maxSdkVersion.ApiLevel.String()
+ }
+ }
+ }
})
+ jars = append(jars, jar)
}
}
return jars
@@ -136,15 +158,15 @@
c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
- generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
- writeClasspathsJson(ctx, generatedJson, jars)
+ generatedTextproto := android.PathForModuleOut(ctx, outputFilename+".textproto")
+ writeClasspathsTextproto(ctx, generatedTextproto, jars)
rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
BuiltTool("conv_classpaths_proto").
Flag("encode").
- Flag("--format=json").
- FlagWithInput("--input=", generatedJson).
+ Flag("--format=textproto").
+ FlagWithInput("--input=", generatedTextproto).
FlagWithOutput("--output=", c.outputFilepath)
rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
@@ -159,24 +181,18 @@
ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo)
}
-func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
+func writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
var content strings.Builder
- fmt.Fprintf(&content, "{\n")
- fmt.Fprintf(&content, "\"jars\": [\n")
- for idx, jar := range jars {
- fmt.Fprintf(&content, "{\n")
- fmt.Fprintf(&content, "\"path\": \"%s\",\n", jar.path)
- fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath)
-
- if idx < len(jars)-1 {
- fmt.Fprintf(&content, "},\n")
- } else {
- fmt.Fprintf(&content, "}\n")
- }
+ for _, jar := range jars {
+ fmt.Fprintf(&content, "jars {\n")
+ fmt.Fprintf(&content, "path: \"%s\"\n", jar.path)
+ fmt.Fprintf(&content, "classpath: %s\n", jar.classpath)
+ fmt.Fprintf(&content, "min_sdk_version: \"%s\"\n", jar.minSdkVersion)
+ fmt.Fprintf(&content, "max_sdk_version: \"%s\"\n", jar.maxSdkVersion)
+ fmt.Fprintf(&content, "}\n")
}
- fmt.Fprintf(&content, "]\n")
- fmt.Fprintf(&content, "}\n")
+
android.WriteFileRule(ctx, output, content.String())
}
@@ -188,7 +204,7 @@
OutputFile: android.OptionalPathForPath(c.outputFilepath),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base())
},
},
diff --git a/java/config/config.go b/java/config/config.go
index 30c6f91..ea2f934 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -60,7 +60,7 @@
const (
JavaVmFlags = `-XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads`
- JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads`
+ JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1`
)
func init() {
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index b198c24..cf39746 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -28,6 +28,11 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+dist_targets = [
+ "sdk",
+ "win_sdk",
+]
+
java_library {
name: "core.current.stubs",
visibility: ["//visibility:public"],
@@ -40,15 +45,16 @@
system_modules: "none",
dist: {
- targets: [
- "sdk",
- "win_sdk",
- ],
+ targets: dist_targets,
},
}
// Distributed with the SDK for turning into system modules to compile apps
// against.
+//
+// Also, produces dist files that are used by the
+// prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk
+// directory.
java_library {
name: "core-current-stubs-for-system-modules",
visibility: ["//development/sdk"],
@@ -65,18 +71,22 @@
],
sdk_version: "none",
system_modules: "none",
- dist: {
- dest: "core-for-system-modules.jar",
- targets: [
- "sdk",
- "win_sdk",
- ],
- },
+ dists: [
+ {
+ // Legacy dist location for the public file.
+ dest: "core-for-system-modules.jar",
+ targets: dist_targets,
+ },
+ {
+ dest: "system-modules/public/core-for-system-modules.jar",
+ targets: dist_targets,
+ },
+ ],
}
// Used when compiling higher-level code against core.current.stubs.
java_system_modules {
- name: "core-current-stubs-system-modules",
+ name: "core-public-stubs-system-modules",
visibility: ["//visibility:public"],
libs: [
"core-current-stubs-for-system-modules",
@@ -103,10 +113,13 @@
visibility: ["//visibility:private"],
}
-// Used when compiling higher-level code with sdk_version "module_current"
-java_system_modules {
- name: "core-module-lib-stubs-system-modules",
- libs: [
+// Produces a dist file that is used by the
+// prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk
+// directory.
+java_library {
+ name: "core-module-lib-stubs-for-system-modules",
+ visibility: ["//visibility:private"],
+ static_libs: [
"core.module_lib.stubs",
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
@@ -117,6 +130,20 @@
// See http://b/123891440.
"core-generated-annotation-stubs",
],
+ sdk_version: "none",
+ system_modules: "none",
+ dist: {
+ dest: "system-modules/module-lib/core-for-system-modules.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",
+ ],
visibility: ["//visibility:public"],
}
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 39fb04a..4abdcc6 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -118,7 +118,7 @@
TransformJarsToJar(ctx, outputFile, "combine", d.implementationAndResourceJars,
android.OptionalPath{}, false, nil, nil)
d.combinedImplementationJar = outputFile
- } else {
+ } else if len(d.implementationAndResourceJars) == 1 {
d.combinedImplementationJar = d.implementationAndResourceJars[0]
}
@@ -127,7 +127,7 @@
TransformJarsToJar(ctx, outputFile, "turbine combine", d.headerJars,
android.OptionalPath{}, false, nil, []string{"META-INF/TRANSITIVE"})
d.combinedHeaderJar = outputFile
- } else {
+ } else if len(d.headerJars) == 1 {
d.combinedHeaderJar = d.headerJars[0]
}
@@ -174,7 +174,9 @@
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
OutputFile: android.OptionalPathForPath(d.combinedImplementationJar),
- Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ // Make does not support Windows Java modules
+ Disabled: d.Os() == android.Windows,
+ Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
diff --git a/java/dex.go b/java/dex.go
index 8045b5c..474694a 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -72,6 +72,9 @@
// This defaults to reasonable value based on module and should not be set.
// It exists only to support ART tests.
Uncompress_dex *bool
+
+ // Exclude kotlinc generate files: *.kotlin_module, *.kotlin_builtins. Defaults to false.
+ Exclude_kotlinc_generated_files *bool
}
type dexer struct {
@@ -94,7 +97,7 @@
`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $tmpJar && ` +
`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
- `${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
+ `${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`,
CommandDeps: []string{
"${config.D8Cmd}",
"${config.Zip2ZipCmd}",
@@ -116,7 +119,7 @@
ExecStrategy: "${config.RED8ExecStrategy}",
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
},
- }, []string{"outDir", "d8Flags", "zipFlags", "tmpJar"}, nil)
+ }, []string{"outDir", "d8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, nil)
var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
blueprint.RuleParams{
@@ -134,7 +137,7 @@
`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
`rm -rf ${outUsageDir} && ` +
`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
- `${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
+ `${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`,
CommandDeps: []string{
"${config.R8Cmd}",
"${config.Zip2ZipCmd}",
@@ -165,7 +168,7 @@
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
},
}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
- "r8Flags", "zipFlags", "tmpJar"}, []string{"implicits"})
+ "r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"})
func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) {
@@ -254,6 +257,15 @@
if BoolDefault(opt.Proguard_compatibility, true) {
r8Flags = append(r8Flags, "--force-proguard-compatibility")
+ } else {
+ // TODO(b/213833843): Allow configuration of the prefix via a build variable.
+ var sourceFilePrefix = "go/retraceme "
+ var sourceFileTemplate = "\"" + sourceFilePrefix + "%MAP_ID\""
+ // TODO(b/200967150): Also tag the source file in compat builds.
+ if Bool(opt.Optimize) || Bool(opt.Obfuscate) {
+ r8Flags = append(r8Flags, "--map-id-template", "%MAP_HASH")
+ r8Flags = append(r8Flags, "--source-file-template", sourceFileTemplate)
+ }
}
// TODO(ccross): Don't shrink app instrumentation tests by default.
@@ -298,6 +310,12 @@
commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion)
+ // Exclude kotlinc generated files when "exclude_kotlinc_generated_files" is set to true.
+ mergeZipsFlags := ""
+ if proptools.BoolDefault(d.dexProperties.Exclude_kotlinc_generated_files, false) {
+ mergeZipsFlags = "-stripFile META-INF/*.kotlin_module -stripFile **/*.kotlin_builtins"
+ }
+
useR8 := d.effectiveOptimizeEnabled()
if useR8 {
proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
@@ -311,14 +329,15 @@
r8Deps = append(r8Deps, commonDeps...)
rule := r8
args := map[string]string{
- "r8Flags": strings.Join(append(commonFlags, r8Flags...), " "),
- "zipFlags": zipFlags,
- "outDict": proguardDictionary.String(),
- "outUsageDir": proguardUsageDir.String(),
- "outUsage": proguardUsage.String(),
- "outUsageZip": proguardUsageZip.String(),
- "outDir": outDir.String(),
- "tmpJar": tmpJar.String(),
+ "r8Flags": strings.Join(append(commonFlags, r8Flags...), " "),
+ "zipFlags": zipFlags,
+ "outDict": proguardDictionary.String(),
+ "outUsageDir": proguardUsageDir.String(),
+ "outUsage": proguardUsage.String(),
+ "outUsageZip": proguardUsageZip.String(),
+ "outDir": outDir.String(),
+ "tmpJar": tmpJar.String(),
+ "mergeZipsFlags": mergeZipsFlags,
}
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
rule = r8RE
@@ -347,10 +366,11 @@
Input: classesJar,
Implicits: d8Deps,
Args: map[string]string{
- "d8Flags": strings.Join(append(commonFlags, d8Flags...), " "),
- "zipFlags": zipFlags,
- "outDir": outDir.String(),
- "tmpJar": tmpJar.String(),
+ "d8Flags": strings.Join(append(commonFlags, d8Flags...), " "),
+ "zipFlags": zipFlags,
+ "outDir": outDir.String(),
+ "tmpJar": tmpJar.String(),
+ "mergeZipsFlags": mergeZipsFlags,
},
})
}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index e9dc982..7c5f055 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -57,6 +57,25 @@
return "-dexpreopt-" + install.name
}
+// Returns Make entries for installing the file.
+//
+// This function uses a value receiver rather than a pointer receiver to ensure that the object is
+// safe to use in `android.AndroidMkExtraEntriesFunc`.
+func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
+ return android.AndroidMkEntries{
+ Class: "ETC",
+ SubName: install.SubModuleName(),
+ OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
+ entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
+ },
+ },
+ }
+}
+
type dexpreopter struct {
dexpreoptProperties DexpreoptProperties
@@ -66,6 +85,7 @@
isApp bool
isTest bool
isPresignedPrebuilt bool
+ preventInstall bool
manifestFile android.Path
statusFile android.WritablePath
@@ -114,6 +134,11 @@
return !apexInfo.IsForPlatform()
}
+func forPrebuiltApex(ctx android.BaseModuleContext) bool {
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ return apexInfo.ForPrebuiltApex
+}
+
func moduleName(ctx android.BaseModuleContext) string {
// Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not
// expected by dexpreopter.
@@ -121,13 +146,7 @@
}
func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
- global := dexpreopt.GetGlobalConfig(ctx)
-
- if global.DisablePreopt {
- return true
- }
-
- if inList(moduleName(ctx), global.DisablePreoptModules) {
+ if !ctx.Device() {
return true
}
@@ -139,31 +158,40 @@
return true
}
- if !ctx.Module().(DexpreopterInterface).IsInstallable() {
+ // If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be
+ // dexpreopted.
+ if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) {
return true
}
- if ctx.Host() {
- return true
- }
-
- 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 !global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) || ctx.Config().UnbundledBuild() {
- return true
- }
- } else {
- // Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
- if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
- return true
- }
- }
-
if !android.IsModulePreferred(ctx.Module()) {
return true
}
+ global := dexpreopt.GetGlobalConfig(ctx)
+
+ if global.DisablePreopt {
+ return true
+ }
+
+ if inList(moduleName(ctx), global.DisablePreoptModules) {
+ return true
+ }
+
+ isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
+ if isApexVariant(ctx) {
+ // Don't preopt APEX variant module unless the module is an APEX system server jar and we are
+ // building the entire system image.
+ if !isApexSystemServerJar || ctx.Config().UnbundledBuild() {
+ return true
+ }
+ } else {
+ // Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
+ if isApexSystemServerJar {
+ return true
+ }
+ }
+
// TODO: contains no java code
return false
@@ -190,8 +218,8 @@
func (d *dexpreopter) getInstallPath(
ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath {
global := dexpreopt.GetGlobalConfig(ctx)
- if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
- dexLocation := dexpreopt.GetSystemServerDexLocation(global, moduleName(ctx))
+ if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) {
+ dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx))
return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
}
if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) &&
@@ -228,8 +256,7 @@
return
}
- isSystemServerJar := global.SystemServerJars.ContainsJar(moduleName(ctx)) ||
- global.ApexSystemServerJars.ContainsJar(moduleName(ctx))
+ isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx))
bootImage := defaultBootImageConfig(ctx)
if global.UseArtImage {
@@ -335,17 +362,21 @@
dexpreoptRule.Build("dexpreopt", "dexpreopt")
- if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
- // 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.
- for _, install := range dexpreoptRule.Installs() {
- // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
- installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
- installBase := filepath.Base(install.To)
- arch := filepath.Base(installDir)
- installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+ isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
+
+ for _, install := range dexpreoptRule.Installs() {
+ // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
+ installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
+ installBase := filepath.Base(install.To)
+ arch := filepath.Base(installDir)
+ installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+
+ if isApexSystemServerJar {
+ // APEX variants of java libraries are hidden from Make, so their dexpreopt
+ // outputs need special handling. Currently, for APEX variants of java
+ // libraries, only those in the system server classpath are handled here.
+ // Preopting of boot classpath jars in the ART APEX are handled in
+ // java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
// The installs will be handled by Make as sub-modules of the java library.
d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
name: arch + "-" + installBase,
@@ -354,10 +385,12 @@
installDirOnDevice: installPath,
installFileOnDevice: installBase,
})
+ } else if !d.preventInstall {
+ ctx.InstallFile(installPath, installBase, install.From)
}
- } else {
- // The installs will be handled by Make as LOCAL_SOONG_BUILT_INSTALLED of the java library
- // module.
+ }
+
+ if !isApexSystemServerJar {
d.builtInstalled = dexpreoptRule.Installs().String()
}
}
@@ -369,19 +402,7 @@
func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
var entries []android.AndroidMkEntries
for _, install := range d.builtInstalledForApex {
- install := install
- entries = append(entries, android.AndroidMkEntries{
- Class: "ETC",
- SubName: install.SubModuleName(),
- OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
- ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.ToMakePath().String())
- entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
- entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
- },
- },
- })
+ entries = append(entries, install.ToMakeEntries())
}
return entries
}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 284a19a..cad9c33 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -256,6 +256,10 @@
// Subdirectory where the image files on device are installed.
installDirOnDevice string
+ // Install path of the boot image profile if it needs to be installed in the APEX, or empty if not
+ // needed.
+ profileInstallPathInApex string
+
// A list of (location, jar) pairs for the Java modules in this image.
modules android.ConfiguredJarList
@@ -272,6 +276,9 @@
// Rules which should be used in make to install the outputs.
profileInstalls android.RuleBuilderInstalls
+ // Path to the image profile file on host (or empty, if profile is not generated).
+ profilePathOnHost android.Path
+
// Target-dependent fields.
variants []*bootImageVariant
}
@@ -306,10 +313,13 @@
// This is only set for a variant of an image that extends another image.
primaryImagesDeps android.Paths
- // Rules which should be used in make to install the outputs.
+ // Rules which should be used in make to install the outputs on host.
installs android.RuleBuilderInstalls
vdexInstalls android.RuleBuilderInstalls
unstrippedInstalls android.RuleBuilderInstalls
+
+ // Rules which should be used in make to install the outputs on device.
+ deviceInstalls android.RuleBuilderInstalls
}
// Get target-specific boot image variant for the given boot image config and target.
@@ -381,6 +391,11 @@
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
@@ -499,8 +514,18 @@
dst := dstBootJarsByModule[name]
if src == nil {
+ // A dex boot jar should be provided by the source java module. It needs to be installable or
+ // have compile_dex=true - cf. assignments to java.Module.dexJarFile.
+ //
+ // However, the source java module may be either replaced or overridden (using prefer:true) by
+ // a prebuilt java module with the same name. In that case the dex boot jar needs to be
+ // provided by the corresponding prebuilt APEX module. That APEX is the one that refers
+ // through a exported_(boot|systemserver)classpath_fragments property to a
+ // prebuilt_(boot|systemserver)classpath_fragment module, which in turn lists the prebuilt
+ // java module in the contents property. If that chain is broken then this dependency will
+ // fail.
if !ctx.Config().AllowMissingDependencies() {
- ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+ ctx.ModuleErrorf("module %s does not provide a dex boot jar (see comment next to this message in Soong for details)", name)
} else {
ctx.AddMissingDependencies([]string{name})
}
@@ -625,7 +650,6 @@
Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx)
if profile != nil {
- cmd.FlagWithArg("--compiler-filter=", "speed-profile")
cmd.FlagWithInput("--profile-file=", profile)
}
@@ -694,6 +718,7 @@
var vdexInstalls android.RuleBuilderInstalls
var unstrippedInstalls android.RuleBuilderInstalls
+ var deviceInstalls android.RuleBuilderInstalls
for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
cmd.ImplicitOutput(artOrOat)
@@ -719,12 +744,21 @@
android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
}
+ if image.installDirOnHost != image.installDirOnDevice && !image.shouldInstallInApex() && !ctx.Config().UnbundledBuild() {
+ installDirOnDevice := filepath.Join("/", image.installDirOnDevice, arch.String())
+ for _, file := range image.moduleFiles(ctx, outputDir, ".art", ".oat", ".vdex") {
+ deviceInstalls = append(deviceInstalls,
+ android.RuleBuilderInstall{file, filepath.Join(installDirOnDevice, file.Base())})
+ }
+ }
+
rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
// save output and installed files for makevars
image.installs = rule.Installs()
image.vdexInstalls = vdexInstalls
image.unstrippedInstalls = unstrippedInstalls
+ image.deviceInstalls = deviceInstalls
}
const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
@@ -770,11 +804,14 @@
FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
FlagWithOutput("--reference-profile-file=", profile)
- rule.Install(profile, "/system/etc/boot-image.prof")
+ if image == defaultBootImageConfig(ctx) {
+ rule.Install(profile, "/system/etc/boot-image.prof")
+ image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+ }
rule.Build("bootJarsProfile", "profile boot jars")
- image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+ image.profilePathOnHost = profile
return profile
}
diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go
new file mode 100644
index 0000000..83c088c
--- /dev/null
+++ b/java/dexpreopt_check.go
@@ -0,0 +1,96 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "strings"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+
+ "github.com/google/blueprint/pathtools"
+)
+
+func init() {
+ RegisterDexpreoptCheckBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterDexpreoptCheckBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory)
+}
+
+// A build-time check to verify if all compilation artifacts of system server jars are installed
+// into the system image. When the check fails, it means that dexpreopting is not working for some
+// system server jars and needs to be fixed.
+// This singleton module generates a list of the paths to the artifacts based on
+// PRODUCT_SYSTEM_SERVER_JARS and PRODUCT_APEX_SYSTEM_SERVER_JARS, and passes it to Make via a
+// variable. Make will then do the actual check.
+// Currently, it only checks artifacts of modules defined in Soong. Artifacts of modules defined in
+// Makefile are generated by a script generated by dexpreopt_gen, and their existence is unknown to
+// Make and Ninja.
+type dexpreoptSystemserverCheck struct {
+ android.SingletonModuleBase
+
+ // Mapping from the module name to the install paths to the compilation artifacts.
+ artifactsByModuleName map[string][]string
+
+ // The install paths to the compilation artifacts.
+ artifacts []string
+}
+
+func dexpreoptSystemserverCheckFactory() android.SingletonModule {
+ m := &dexpreoptSystemserverCheck{}
+ m.artifactsByModuleName = make(map[string][]string)
+ android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ return m
+}
+
+func getInstallPath(ctx android.ModuleContext, location string) android.InstallPath {
+ return android.PathForModuleInPartitionInstall(
+ ctx, "", strings.TrimPrefix(location, "/"))
+}
+
+func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ global := dexpreopt.GetGlobalConfig(ctx)
+ targets := ctx.Config().Targets[android.Android]
+
+ // The check should be skipped on unbundled builds because system server jars are not preopted on
+ // unbundled builds since the artifacts are installed into the system image, not the APEXes.
+ if global.DisablePreopt || len(targets) == 0 || ctx.Config().UnbundledBuild() {
+ return
+ }
+
+ systemServerJars := global.AllSystemServerJars(ctx)
+ for _, jar := range systemServerJars.CopyOfJars() {
+ dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, jar)
+ odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType)
+ odexPath := getInstallPath(ctx, odexLocation)
+ vdexPath := getInstallPath(ctx, pathtools.ReplaceExtension(odexLocation, "vdex"))
+ m.artifactsByModuleName[jar] = []string{odexPath.String(), vdexPath.String()}
+ }
+}
+
+func (m *dexpreoptSystemserverCheck) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+ // Only keep modules defined in Soong.
+ ctx.VisitAllModules(func(module android.Module) {
+ if artifacts, ok := m.artifactsByModuleName[module.Name()]; ok {
+ m.artifacts = append(m.artifacts, artifacts...)
+ }
+ })
+}
+
+func (m *dexpreoptSystemserverCheck) MakeVars(ctx android.MakeVarsContext) {
+ ctx.Strict("DEXPREOPT_SYSTEMSERVER_ARTIFACTS", strings.Join(m.artifacts, " "))
+}
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 415a1d4..21e1d12 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -41,37 +41,32 @@
var (
bootImageConfigKey = android.NewOnceKey("bootImageConfig")
+ bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw")
artBootImageName = "art"
frameworkBootImageName = "boot"
)
-// Construct the global boot image configs.
-func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
- return ctx.Config().Once(bootImageConfigKey, func() interface{} {
-
+func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig {
+ return ctx.Config().Once(bootImageConfigRawKey, func() interface{} {
global := dexpreopt.GetGlobalConfig(ctx)
- targets := dexpreoptTargets(ctx)
- deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
artModules := global.ArtApexJars
frameworkModules := global.BootJars.RemoveList(artModules)
- artDirOnHost := "apex/art_boot_images/javalib"
- artDirOnDevice := "apex/com.android.art/javalib"
- 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: artDirOnHost,
- installDirOnDevice: artDirOnDevice,
- modules: artModules,
+ name: artBootImageName,
+ stem: "boot",
+ installDirOnHost: "apex/art_boot_images/javalib",
+ installDirOnDevice: "system/framework",
+ profileInstallPathInApex: "etc/boot-image.prof",
+ modules: artModules,
}
// 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,
@@ -81,10 +76,22 @@
modules: frameworkModules,
}
- configs := map[string]*bootImageConfig{
+ return map[string]*bootImageConfig{
artBootImageName: &artCfg,
frameworkBootImageName: &frameworkCfg,
}
+ }).(map[string]*bootImageConfig)
+}
+
+// Construct the global boot image configs.
+func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
+ return ctx.Config().Once(bootImageConfigKey, func() interface{} {
+ targets := dexpreoptTargets(ctx)
+ deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
+
+ configs := genBootImageConfigRaw(ctx)
+ artCfg := configs[artBootImageName]
+ frameworkCfg := configs[frameworkBootImageName]
// common to all configs
for _, c := range configs {
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 869a598..c84a15c 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -769,8 +769,8 @@
d.Javadoc.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
- jsilver := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jsilver.jar")
- doclava := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "doclava.jar")
+ jsilver := ctx.Config().HostJavaToolPath(ctx, "jsilver.jar")
+ doclava := ctx.Config().HostJavaToolPath(ctx, "doclava.jar")
outDir := android.PathForModuleOut(ctx, "out")
srcJarDir := android.PathForModuleOut(ctx, "srcjars")
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 7fd88fc..f9dcfd6 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "path/filepath"
"strings"
"github.com/google/blueprint/proptools"
@@ -25,6 +26,9 @@
"android/soong/remoteexec"
)
+// The values allowed for Droidstubs' Api_levels_sdk_type
+var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib"}
+
func init() {
RegisterStubsBuildComponents(android.InitRegistrationContext)
}
@@ -134,7 +138,7 @@
// 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' and 'system' for now; defaults to public.
+ // the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system' and 'module-lib' for now; defaults to public.
Api_levels_sdk_type *string
// the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
@@ -330,7 +334,11 @@
// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
FlagWithArg("--hide ", "SuperfluousPrefix").
- FlagWithArg("--hide ", "AnnotationExtraction")
+ FlagWithArg("--hide ", "AnnotationExtraction").
+ // (b/217545629)
+ FlagWithArg("--hide ", "ChangedThrows").
+ // (b/217552813)
+ FlagWithArg("--hide ", "ChangedAbstract")
}
}
@@ -404,19 +412,24 @@
// When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
// an actual file present on disk (in the order the patterns were passed). For system APIs for
// privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
- // for older releases.
- if sdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public"); sdkType != "public" {
- if sdkType != "system" {
- ctx.PropertyErrorf("api_levels_sdk_type", "only 'public' and 'system' are supported")
- }
- // If building non public stubs, add all sdkType patterns first...
- for _, dir := range dirs {
- cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkType, filename))
- }
+ // 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 "module-lib":
+ sdkDirs = []string{"module-lib", "system", "public"}
+ case "system":
+ sdkDirs = []string{"system", "public"}
+ case "public":
+ sdkDirs = []string{"public"}
+ default:
+ ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes)
+ return
}
- for _, dir := range dirs {
- // ... and fallback to public ones, for Metalava to use if needed.
- cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, "public", filename))
+
+ for _, sdkDir := range sdkDirs {
+ for _, dir := range dirs {
+ cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename))
+ }
}
}
@@ -797,7 +810,7 @@
properties PrebuiltStubsSourcesProperties
- stubsSrcJar android.ModuleOutPath
+ stubsSrcJar android.Path
}
func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
@@ -814,35 +827,39 @@
}
func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
-
if len(p.properties.Srcs) != 1 {
- ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs))
+ ctx.PropertyErrorf("srcs", "must only specify one directory path or srcjar, contains %d paths", len(p.properties.Srcs))
return
}
- localSrcDir := p.properties.Srcs[0]
- // Although PathForModuleSrc can return nil if either the path doesn't exist or
- // the path components are invalid it won't in this case because no components
- // are specified and the module directory must exist in order to get this far.
- srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir)
+ src := p.properties.Srcs[0]
+ if filepath.Ext(src) == ".srcjar" {
+ // This is a srcjar. We can use it directly.
+ p.stubsSrcJar = android.PathForModuleSrc(ctx, src)
+ } else {
+ outPath := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
- // Glob the contents of the directory just in case the directory does not exist.
- srcGlob := localSrcDir + "/**/*"
- srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
+ // This is a directory. Glob the contents just in case the directory does not exist.
+ srcGlob := src + "/**/*"
+ srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("soong_zip").
- Flag("-write_if_changed").
- Flag("-jar").
- FlagWithOutput("-o ", p.stubsSrcJar).
- FlagWithArg("-C ", srcDir.String()).
- FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths)
+ // Although PathForModuleSrc can return nil if either the path doesn't exist or
+ // the path components are invalid it won't in this case because no components
+ // are specified and the module directory must exist in order to get this far.
+ srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, src)
- rule.Restat()
-
- rule.Build("zip src", "Create srcjar from prebuilt source")
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("soong_zip").
+ Flag("-write_if_changed").
+ Flag("-jar").
+ FlagWithOutput("-o ", outPath).
+ FlagWithArg("-C ", srcDir.String()).
+ FlagWithRspFileInputList("-r ", outPath.ReplaceExtension(ctx, "rsp"), srcPaths)
+ rule.Restat()
+ rule.Build("zip src", "Create srcjar from prebuilt source")
+ p.stubsSrcJar = outPath
+ }
}
func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 60d0bea..10d99f3 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -15,6 +15,7 @@
package java
import (
+ "fmt"
"reflect"
"regexp"
"strings"
@@ -82,8 +83,10 @@
}
}
-func TestSystemDroidstubs(t *testing.T) {
- ctx, _ := testJavaWithFS(t, `
+// runs a test for droidstubs with a customizable sdkType argument and returns
+// the list of jar patterns that is passed as `--android-jar-pattern`
+func getAndroidJarPatternsForDroidstubs(t *testing.T, sdkType string) []string {
+ ctx, _ := testJavaWithFS(t, fmt.Sprintf(`
droiddoc_exported_dir {
name: "some-exported-dir",
path: "somedir",
@@ -102,9 +105,9 @@
"some-other-exported-dir",
],
api_levels_annotations_enabled: true,
- api_levels_sdk_type: "system",
+ api_levels_sdk_type: "%s",
}
- `,
+ `, sdkType),
map[string][]byte{
"foo-doc/a.java": nil,
})
@@ -113,13 +116,40 @@
manifest := m.Output("metalava.sbox.textproto")
cmd := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`)
- matches := r.FindAllString(cmd, -1)
+ return r.FindAllString(cmd, -1)
+}
+
+func TestPublicDroidstubs(t *testing.T) {
+ patterns := getAndroidJarPatternsForDroidstubs(t, "public")
+
+ android.AssertArrayString(t, "order of patterns", []string{
+ "--android-jar-pattern somedir/%/public/android.jar",
+ "--android-jar-pattern someotherdir/%/public/android.jar",
+ }, patterns)
+}
+
+func TestSystemDroidstubs(t *testing.T) {
+ patterns := getAndroidJarPatternsForDroidstubs(t, "system")
+
android.AssertArrayString(t, "order of patterns", []string{
"--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",
- }, matches)
+ }, patterns)
+}
+
+func TestModuleLibDroidstubs(t *testing.T) {
+ patterns := getAndroidJarPatternsForDroidstubs(t, "module-lib")
+
+ android.AssertArrayString(t, "order of patterns", []string{
+ "--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) {
diff --git a/java/fuzz.go b/java/fuzz.go
new file mode 100644
index 0000000..257f343
--- /dev/null
+++ b/java/fuzz.go
@@ -0,0 +1,159 @@
+// 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 java
+
+import (
+ "github.com/google/blueprint/proptools"
+ "sort"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/fuzz"
+)
+
+func init() {
+ RegisterJavaFuzzBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("java_fuzz_host", FuzzFactory)
+ ctx.RegisterSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory)
+}
+
+type JavaFuzzLibrary struct {
+ Library
+ fuzzPackagedModule fuzz.FuzzPackagedModule
+}
+
+func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.Library.GenerateAndroidBuildActions(ctx)
+
+ if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
+ j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
+ }
+ 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.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
+
+ // java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants.
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ disableLinuxBionic := struct {
+ Target struct {
+ Linux_bionic struct {
+ Enabled *bool
+ }
+ }
+ }{}
+ disableLinuxBionic.Target.Linux_bionic.Enabled = proptools.BoolPtr(false)
+ ctx.AppendProperties(&disableLinuxBionic)
+ })
+
+ module.initModuleAndImport(module)
+ android.InitSdkAwareModule(module)
+ InitJavaModule(module, android.HostSupported)
+ return module
+}
+
+// Responsible for generating rules that package fuzz targets into
+// their architecture & target/host specific zip file.
+type javaFuzzPackager struct {
+ fuzz.FuzzPackager
+}
+
+func javaFuzzPackagingFactory() android.Singleton {
+ return &javaFuzzPackager{}
+}
+
+func (s *javaFuzzPackager) 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)
+
+ ctx.VisitAllModules(func(module android.Module) {
+ // Discard non-fuzz targets.
+ javaModule, ok := module.(*JavaFuzzLibrary)
+ if !ok {
+ return
+ }
+
+ fuzzModuleValidator := fuzz.FuzzModule{
+ javaModule.ModuleBase,
+ javaModule.DefaultableModuleBase,
+ javaModule.ApexModuleBase,
+ }
+
+ if ok := fuzz.IsValid(fuzzModuleValidator); !ok || *javaModule.Module.properties.Installable {
+ return
+ }
+
+ hostOrTargetString := "target"
+ if javaModule.Host() {
+ hostOrTargetString = "host"
+ }
+ archString := javaModule.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)
+
+ // Add .jar
+ files = append(files, fuzz.FileToZip{javaModule.outputFile, ""})
+
+ archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaModule.fuzzPackagedModule, files, builder, archDir, archString, "host", archOs, archDirs)
+ if !ok {
+ return
+ }
+
+ })
+ s.CreateFuzzPackage(ctx, archDirs, fuzz.Java, pctx)
+}
+
+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
new file mode 100644
index 0000000..cf063eb
--- /dev/null
+++ b/java/fuzz_test.go
@@ -0,0 +1,65 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+ "path/filepath"
+ "testing"
+)
+
+var prepForJavaFuzzTest = android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterJavaFuzzBuildComponents),
+)
+
+func TestJavaFuzz(t *testing.T) {
+ result := prepForJavaFuzzTest.RunTestWithBp(t, `
+ java_fuzz_host {
+ name: "foo",
+ srcs: ["a.java"],
+ libs: ["bar"],
+ static_libs: ["baz"],
+ }
+
+ java_library_host {
+ name: "bar",
+ srcs: ["b.java"],
+ }
+
+ java_library_host {
+ name: "baz",
+ srcs: ["c.java"],
+ }`)
+
+ osCommonTarget := result.Config.BuildOSCommonTarget.String()
+ javac := result.ModuleForTests("foo", osCommonTarget).Rule("javac")
+ combineJar := result.ModuleForTests("foo", osCommonTarget).Description("for javac")
+
+ if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
+ t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
+ }
+
+ baz := result.ModuleForTests("baz", osCommonTarget).Rule("javac").Output.String()
+ barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac", "bar.jar")
+ bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac", "baz.jar")
+
+ android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barOut)
+ android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazOut)
+
+ if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
+ t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
+ }
+}
diff --git a/java/genrule.go b/java/genrule.go
index e0a9c8f..5047c41 100644
--- a/java/genrule.go
+++ b/java/genrule.go
@@ -24,8 +24,8 @@
}
func RegisterGenRuleBuildComponents(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("java_genrule", genRuleFactory)
- ctx.RegisterModuleType("java_genrule_host", genRuleFactoryHost)
+ ctx.RegisterModuleType("java_genrule", GenRuleFactory)
+ ctx.RegisterModuleType("java_genrule_host", GenRuleFactoryHost)
}
// java_genrule is a genrule that can depend on other java_* objects.
@@ -33,7 +33,7 @@
// By default a java_genrule has a single variant that will run against the device variant of its dependencies and
// produce an output that can be used as an input to a device java rule.
//
-// Specifying `host_supported: true` will produce two variants, one that uses device dependencie sand one that uses
+// Specifying `host_supported: true` will produce two variants, one that uses device dependencies and one that uses
// host dependencies. Each variant will run the command.
//
// Use a java_genrule instead of a genrule when it needs to depend on or be depended on by other java modules, unless
@@ -44,7 +44,7 @@
// Use a java_genrule to package generated java resources:
//
// java_genrule {
-// name: "generated_resources",
+// name: "generated_resources",
// tools: [
// "generator",
// "soong_zip",
@@ -60,10 +60,12 @@
// srcs: ["src/**/*.java"],
// static_libs: ["generated_resources"],
// }
-func genRuleFactory() android.Module {
+func GenRuleFactory() android.Module {
module := genrule.NewGenRule()
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitBazelModule(module)
return module
}
@@ -72,10 +74,12 @@
//
// A java_genrule_host has a single variant that will run against the host variant of its dependencies and
// produce an output that can be used as an input to a host java rule.
-func genRuleFactoryHost() android.Module {
+func GenRuleFactoryHost() android.Module {
module := genrule.NewGenRule()
android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitBazelModule(module)
return module
}
diff --git a/java/genrule_test.go b/java/genrule_test.go
new file mode 100644
index 0000000..1c294b2
--- /dev/null
+++ b/java/genrule_test.go
@@ -0,0 +1,118 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func testGenruleContext(config android.Config) *android.TestContext {
+ ctx := android.NewTestArchContext(config)
+ ctx.RegisterModuleType("java_genrule", GenRuleFactory)
+ ctx.Register()
+
+ return ctx
+}
+
+func TestGenruleCmd(t *testing.T) {
+ fs := map[string][]byte{
+ "tool": nil,
+ "foo": nil,
+ }
+ bp := `
+ java_genrule {
+ name: "gen",
+ tool_files: ["tool"],
+ cmd: "$(location tool) $(in) $(out)",
+ srcs: ["foo"],
+ out: ["out"],
+ }
+ `
+ config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
+
+ ctx := testGenruleContext(config)
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ if errs == nil {
+ _, errs = ctx.PrepareBuildActions(config)
+ }
+ if errs != nil {
+ t.Fatal(errs)
+ }
+
+ gen := ctx.ModuleForTests("gen", "android_common").Output("out")
+ expected := []string{"foo"}
+ if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
+ t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings())
+ }
+}
+
+func TestJarGenrules(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+
+ java_genrule {
+ name: "jargen",
+ tool_files: ["b.java"],
+ cmd: "$(location b.java) $(in) $(out)",
+ out: ["jargen.jar"],
+ srcs: [":foo"],
+ }
+
+ java_library {
+ name: "bar",
+ static_libs: ["jargen"],
+ srcs: ["c.java"],
+ }
+
+ java_library {
+ name: "baz",
+ libs: ["jargen"],
+ srcs: ["c.java"],
+ }
+ `)
+
+ foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar")
+ jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar")
+ bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar")
+ baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
+ barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
+
+ if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected jargen inputs [%q], got %q", w, g)
+ }
+
+ if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) {
+ t.Errorf("bar classpath %v does not contain %q", bar.Args["classpath"], jargen.Output.String())
+ }
+
+ if !strings.Contains(baz.Args["classpath"], jargen.Output.String()) {
+ t.Errorf("baz classpath %v does not contain %q", baz.Args["classpath"], jargen.Output.String())
+ }
+
+ if len(barCombined.Inputs) != 2 ||
+ barCombined.Inputs[0].String() != bar.Output.String() ||
+ barCombined.Inputs[1].String() != jargen.Output.String() {
+ t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+ barCombined.Inputs.Strings(), bar.Output.String(), jargen.Output.String())
+ }
+}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 7c8be1e..3af5f1c 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -297,6 +297,7 @@
type hiddenApiAnnotationsDependencyTag struct {
blueprint.BaseDependencyTag
+ android.LicenseAnnotationSharedDependencyTag
}
// Tag used to mark dependencies on java_library instances that contains Java source files whose
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index f1e30f3..0cc960d 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -943,13 +943,22 @@
// buildRuleSignaturePatternsFile creates a rule to generate a file containing the set of signature
// patterns that will select a subset of the monolithic flags.
-func buildRuleSignaturePatternsFile(ctx android.ModuleContext, flagsPath android.Path) android.Path {
+func buildRuleSignaturePatternsFile(ctx android.ModuleContext, flagsPath android.Path, splitPackages []string, packagePrefixes []string) android.Path {
patternsFile := android.PathForModuleOut(ctx, "modular-hiddenapi", "signature-patterns.csv")
// Create a rule to validate the output from the following rule.
rule := android.NewRuleBuilder(pctx, ctx)
+
+ // Quote any * in the packages to avoid them being expanded by the shell.
+ quotedSplitPackages := []string{}
+ for _, pkg := range splitPackages {
+ quotedSplitPackages = append(quotedSplitPackages, strings.ReplaceAll(pkg, "*", "\\*"))
+ }
+
rule.Command().
BuiltTool("signature_patterns").
FlagWithInput("--flags ", flagsPath).
+ FlagForEachArg("--split-package ", quotedSplitPackages).
+ FlagForEachArg("--package-prefix ", packagePrefixes).
FlagWithOutput("--output ", patternsFile)
rule.Build("hiddenAPISignaturePatterns", "hidden API signature patterns")
diff --git a/java/java.go b/java/java.go
index 287bcfa..e55f045 100644
--- a/java/java.go
+++ b/java/java.go
@@ -21,6 +21,9 @@
import (
"fmt"
"path/filepath"
+ "strings"
+
+ "android/soong/bazel"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -268,6 +271,12 @@
type dependencyTag struct {
blueprint.BaseDependencyTag
name string
+
+ // True if the dependency is relinked at runtime.
+ runtimeLinked bool
+
+ // True if the dependency is a toolchain, for example an annotation processor.
+ toolchain bool
}
// installDependencyTag is a dependency tag that is annotated to cause the installed files of the
@@ -278,6 +287,17 @@
name string
}
+func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
+ if d.runtimeLinked {
+ return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
+ } else if d.toolchain {
+ return []android.LicenseAnnotation{android.LicenseAnnotationToolchain}
+ }
+ return nil
+}
+
+var _ android.LicenseAnnotationsDependencyTag = dependencyTag{}
+
type usesLibraryDependencyTag struct {
dependencyTag
@@ -294,10 +314,13 @@
func makeUsesLibraryDependencyTag(sdkVersion int, optional bool, implicit bool) usesLibraryDependencyTag {
return usesLibraryDependencyTag{
- dependencyTag: dependencyTag{name: fmt.Sprintf("uses-library-%d", sdkVersion)},
- sdkVersion: sdkVersion,
- optional: optional,
- implicit: implicit,
+ dependencyTag: dependencyTag{
+ name: fmt.Sprintf("uses-library-%d", sdkVersion),
+ runtimeLinked: true,
+ },
+ sdkVersion: sdkVersion,
+ optional: optional,
+ implicit: implicit,
}
}
@@ -307,23 +330,24 @@
var (
dataNativeBinsTag = dependencyTag{name: "dataNativeBins"}
+ dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"}
staticLibTag = dependencyTag{name: "staticlib"}
- libTag = dependencyTag{name: "javalib"}
- java9LibTag = dependencyTag{name: "java9lib"}
- pluginTag = dependencyTag{name: "plugin"}
- errorpronePluginTag = dependencyTag{name: "errorprone-plugin"}
- exportedPluginTag = dependencyTag{name: "exported-plugin"}
- bootClasspathTag = dependencyTag{name: "bootclasspath"}
- systemModulesTag = dependencyTag{name: "system modules"}
+ libTag = dependencyTag{name: "javalib", runtimeLinked: true}
+ java9LibTag = dependencyTag{name: "java9lib", runtimeLinked: true}
+ pluginTag = dependencyTag{name: "plugin", toolchain: true}
+ errorpronePluginTag = dependencyTag{name: "errorprone-plugin", toolchain: true}
+ exportedPluginTag = dependencyTag{name: "exported-plugin", toolchain: true}
+ bootClasspathTag = dependencyTag{name: "bootclasspath", runtimeLinked: true}
+ systemModulesTag = dependencyTag{name: "system modules", runtimeLinked: true}
frameworkResTag = dependencyTag{name: "framework-res"}
- kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
- kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"}
- kotlinPluginTag = dependencyTag{name: "kotlin-plugin"}
+ kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib", runtimeLinked: true}
+ kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations", runtimeLinked: true}
+ kotlinPluginTag = dependencyTag{name: "kotlin-plugin", toolchain: true}
proguardRaiseTag = dependencyTag{name: "proguard-raise"}
certificateTag = dependencyTag{name: "certificate"}
instrumentationForTag = dependencyTag{name: "instrumentation_for"}
- extraLintCheckTag = dependencyTag{name: "extra-lint-check"}
- jniLibTag = dependencyTag{name: "jnilib"}
+ extraLintCheckTag = dependencyTag{name: "extra-lint-check", toolchain: true}
+ jniLibTag = dependencyTag{name: "jnilib", runtimeLinked: true}
syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
jniInstallTag = installDependencyTag{name: "jni install"}
binaryInstallTag = installDependencyTag{name: "binary install"}
@@ -433,7 +457,7 @@
} else if ctx.Device() {
return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
} else {
- return JAVA_VERSION_9
+ return JAVA_VERSION_11
}
}
@@ -445,6 +469,7 @@
JAVA_VERSION_7 = 7
JAVA_VERSION_8 = 8
JAVA_VERSION_9 = 9
+ JAVA_VERSION_11 = 11
)
func (v javaVersion) String() string {
@@ -457,6 +482,8 @@
return "1.8"
case JAVA_VERSION_9:
return "1.9"
+ case JAVA_VERSION_11:
+ return "11"
default:
return "unsupported"
}
@@ -477,8 +504,10 @@
return JAVA_VERSION_8
case "1.9", "9":
return JAVA_VERSION_9
- case "10", "11":
- ctx.PropertyErrorf("java_version", "Java language levels above 9 are not supported")
+ case "11":
+ return JAVA_VERSION_11
+ case "10":
+ ctx.PropertyErrorf("java_version", "Java language levels 10 is not supported")
return JAVA_VERSION_UNSUPPORTED
default:
ctx.PropertyErrorf("java_version", "Unrecognized Java language level")
@@ -532,9 +561,18 @@
return false
}
+// Sets `dexer.dexProperties.Uncompress_dex` to the proper value.
+func setUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter, dexer *dexer) {
+ if dexer.dexProperties.Uncompress_dex == nil {
+ // If the value was not force-set by the user, use reasonable default based on the module.
+ dexer.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, dexpreopter))
+ }
+}
+
func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.sdkVersion = j.SdkVersion(ctx)
j.minSdkVersion = j.MinSdkVersion(ctx)
+ j.maxSdkVersion = j.MaxSdkVersion(ctx)
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if !apexInfo.IsForPlatform() {
@@ -545,10 +583,7 @@
j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
- if j.dexProperties.Uncompress_dex == nil {
- // If the value was not force-set by the user, use reasonable default based on the module.
- j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
- }
+ setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
j.classLoaderContexts = j.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
j.compile(ctx, nil)
@@ -562,12 +597,23 @@
if j.InstallMixin != nil {
extraInstallDeps = j.InstallMixin(ctx, j.outputFile)
}
- j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
- j.Stem()+".jar", j.outputFile, extraInstallDeps...)
- }
-
- if ctx.Windows() {
- j.HideFromMake()
+ hostDexNeeded := Bool(j.deviceProperties.Hostdex) && !ctx.Host()
+ if hostDexNeeded {
+ j.hostdexInstallFile = ctx.InstallFile(
+ android.PathForHostDexInstall(ctx, "framework"),
+ j.Stem()+"-hostdex.jar", j.outputFile)
+ }
+ 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")
+ }
+ j.installFile = ctx.InstallFile(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...)
}
}
@@ -711,6 +757,7 @@
android.InitApexModule(module)
android.InitSdkAwareModule(module)
+ android.InitBazelModule(module)
InitJavaModule(module, android.HostAndDeviceSupported)
return module
}
@@ -733,6 +780,7 @@
android.InitApexModule(module)
android.InitSdkAwareModule(module)
+ android.InitBazelModule(module)
InitJavaModule(module, android.HostSupported)
return module
}
@@ -789,6 +837,9 @@
type hostTestProperties struct {
// list of native binary modules that should be installed alongside the test
Data_native_bins []string `android:"arch_variant"`
+
+ // list of device binary modules that should be installed alongside the test
+ Data_device_bins []string `android:"arch_variant"`
}
type testHelperLibraryProperties struct {
@@ -841,6 +892,20 @@
dexJarFile android.Path
}
+func (j *Test) InstallInTestcases() bool {
+ // Host java tests install into $(HOST_OUT_JAVA_LIBRARIES), and then are copied into
+ // testcases by base_rules.mk.
+ return !j.Host()
+}
+
+func (j *TestHelperLibrary) InstallInTestcases() bool {
+ return true
+}
+
+func (j *JavaTestImport) InstallInTestcases() bool {
+ return true
+}
+
func (j *TestHost) DepsMutator(ctx android.BottomUpMutatorContext) {
if len(j.testHostProperties.Data_native_bins) > 0 {
for _, target := range ctx.MultiTargets() {
@@ -848,6 +913,11 @@
}
}
+ if len(j.testHostProperties.Data_device_bins) > 0 {
+ deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
+ ctx.AddFarVariationDependencies(deviceVariations, dataDeviceBinsTag, j.testHostProperties.Data_device_bins...)
+ }
+
if len(j.testProperties.Jni_libs) > 0 {
for _, target := range ctx.MultiTargets() {
sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
@@ -862,14 +932,35 @@
j.extraResources = append(j.extraResources, p)
}
+func (j *TestHost) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ var configs []tradefed.Config
+ if len(j.testHostProperties.Data_device_bins) > 0 {
+ // add Tradefed configuration to push device bins to device for testing
+ remoteDir := filepath.Join("/data/local/tests/unrestricted/", j.Name())
+ options := []tradefed.Option{{Name: "cleanup", Value: "true"}}
+ for _, bin := range j.testHostProperties.Data_device_bins {
+ fullPath := filepath.Join(remoteDir, bin)
+ options = append(options, tradefed.Option{Name: "push-file", Key: bin, Value: fullPath})
+ }
+ configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.PushFilePreparer", options})
+ }
+
+ j.Test.generateAndroidBuildActionsWithConfig(ctx, configs)
+}
+
func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.generateAndroidBuildActionsWithConfig(ctx, nil)
+}
+
+func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, configs []tradefed.Config) {
if j.testProperties.Test_options.Unit_test == nil && ctx.Host() {
// TODO(b/): Clean temporary heuristic to avoid unexpected onboarding.
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, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test)
+ j.testProperties.Test_suites, configs, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test)
j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
@@ -879,6 +970,10 @@
j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
})
+ ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
+ j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
+ })
+
ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) {
sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo)
if sharedLibInfo.SharedLibrary != nil {
@@ -911,7 +1006,7 @@
func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.prebuiltTestProperties.Test_config, nil,
- j.prebuiltTestProperties.Test_suites, nil, nil)
+ j.prebuiltTestProperties.Test_suites, nil, nil, nil)
j.Import.GenerateAndroidBuildActions(ctx)
}
@@ -1135,10 +1230,6 @@
j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
ctx.ModuleName()+ext, j.wrapperFile)
}
-
- if ctx.Windows() {
- j.HideFromMake()
- }
}
func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -1173,6 +1264,8 @@
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst)
android.InitDefaultableModule(module)
+ android.InitBazelModule(module)
+
return module
}
@@ -1190,6 +1283,7 @@
android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst)
android.InitDefaultableModule(module)
+ android.InitBazelModule(module)
return module
}
@@ -1239,6 +1333,7 @@
android.ModuleBase
android.DefaultableModuleBase
android.ApexModuleBase
+ android.BazelModuleBase
prebuilt android.Prebuilt
android.SdkBase
@@ -1379,8 +1474,17 @@
})
if Bool(j.properties.Installable) {
- ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
- jarName, outputFile)
+ 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.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
@@ -1401,16 +1505,13 @@
installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName()))
j.dexJarInstallFile = installPath
- // Initialize the hiddenapi structure.
- j.initHiddenAPI(ctx, dexJarFile, outputFile, nil)
-
j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, installPath)
- if j.dexProperties.Uncompress_dex == nil {
- // If the value was not force-set by the user, use reasonable default based on the module.
- j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
- }
+ setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
j.dexpreopt(ctx, dexOutputPath)
+
+ // Initialize the hiddenapi structure.
+ j.initHiddenAPI(ctx, dexJarFile, outputFile, j.dexProperties.Uncompress_dex)
} else {
// This should never happen as a variant for a prebuilt_apex is only created if the
// prebuilt_apex has been configured to export the java library dex file.
@@ -1430,10 +1531,7 @@
j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
ctx, android.PathForModuleInstall(ctx, "framework", jarName))
- if j.dexProperties.Uncompress_dex == nil {
- // If the value was not force-set by the user, use reasonable default based on the module.
- j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
- }
+ setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
var dexOutputFile android.OutputPath
@@ -1549,9 +1647,6 @@
var _ android.IDECustomizedModuleName = (*Import)(nil)
// Collect information for opening IDE project files in java/jdeps.go.
-const (
- removedPrefix = "prebuilt_"
-)
func (j *Import) IDEInfo(dpInfo *android.IdeInfo) {
dpInfo.Jars = append(dpInfo.Jars, j.PrebuiltSrcs()...)
@@ -1594,6 +1689,7 @@
android.InitPrebuiltModule(module, &module.properties.Jars)
android.InitApexModule(module)
android.InitSdkAwareModule(module)
+ android.InitBazelModule(module)
InitJavaModule(module, android.HostAndDeviceSupported)
return module
}
@@ -1806,6 +1902,7 @@
module.AddProperties(
&CommonProperties{},
&DeviceProperties{},
+ &OverridableDeviceProperties{},
&DexProperties{},
&DexpreoptProperties{},
&android.ProtoProperties{},
@@ -1906,3 +2003,139 @@
clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
}
}
+
+type javaLibraryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Javacopts bazel.StringListAttribute
+}
+
+func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) *javaLibraryAttributes {
+ //TODO(b/209577426): Support multiple arch variants
+ srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs))
+ attrs := &javaLibraryAttributes{
+ Srcs: srcs,
+ }
+
+ if m.properties.Javacflags != nil {
+ attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
+ }
+
+ var deps bazel.LabelList
+ if m.properties.Libs != nil {
+ deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Libs))
+ }
+ if m.properties.Static_libs != nil {
+ //TODO(b/217236083) handle static libs similarly to Soong
+ deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Static_libs))
+ }
+ attrs.Deps = bazel.MakeLabelListAttribute(deps)
+
+ return attrs
+}
+
+func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
+ attrs := m.convertLibraryAttrsBp2Build(ctx)
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "java_library",
+ Bzl_load_location: "//build/bazel/rules/java:library.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+}
+
+type javaBinaryHostAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Main_class string
+ Jvm_flags bazel.StringListAttribute
+ Javacopts bazel.StringListAttribute
+}
+
+// JavaBinaryHostBp2Build is for java_binary_host bp2build.
+func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) {
+ mainClass := ""
+ if m.binaryProperties.Main_class != nil {
+ mainClass = *m.binaryProperties.Main_class
+ }
+ if m.properties.Manifest != nil {
+ mainClassInManifest, err := android.GetMainClassInManifest(ctx.Config(), android.PathForModuleSrc(ctx, *m.properties.Manifest).String())
+ if err != nil {
+ return
+ }
+ mainClass = mainClassInManifest
+ }
+ srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs))
+ attrs := &javaBinaryHostAttributes{
+ Srcs: srcs,
+ Main_class: mainClass,
+ }
+
+ if m.properties.Javacflags != nil {
+ attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
+ }
+
+ // Attribute deps
+ deps := []string{}
+ if m.properties.Static_libs != nil {
+ deps = append(deps, m.properties.Static_libs...)
+ }
+ if m.binaryProperties.Jni_libs != nil {
+ deps = append(deps, m.binaryProperties.Jni_libs...)
+ }
+ if len(deps) > 0 {
+ attrs.Deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, deps))
+ }
+
+ // Attribute jvm_flags
+ if m.binaryProperties.Jni_libs != nil {
+ jniLibPackages := map[string]bool{}
+ for _, jniLibLabel := range android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs).Includes {
+ jniLibPackage := jniLibLabel.Label
+ indexOfColon := strings.Index(jniLibLabel.Label, ":")
+ if indexOfColon > 0 {
+ // JNI lib from other package
+ jniLibPackage = jniLibLabel.Label[2:indexOfColon]
+ } else if indexOfColon == 0 {
+ // JNI lib in the same package of java_binary
+ packageOfCurrentModule := m.GetBazelLabel(ctx, m)
+ jniLibPackage = packageOfCurrentModule[2:strings.Index(packageOfCurrentModule, ":")]
+ }
+ if _, inMap := jniLibPackages[jniLibPackage]; !inMap {
+ jniLibPackages[jniLibPackage] = true
+ }
+ }
+ jniLibPaths := []string{}
+ for jniLibPackage, _ := range jniLibPackages {
+ // 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, ":")})
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "java_binary",
+ }
+
+ // Create the BazelTargetModule.
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+}
+
+type bazelJavaImportAttributes struct {
+ Jars bazel.LabelListAttribute
+}
+
+// java_import bp2Build converter.
+func (i *Import) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ //TODO(b/209577426): Support multiple arch variants
+ jars := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, i.properties.Jars, []string(nil)))
+
+ attrs := &bazelJavaImportAttributes{
+ Jars: jars,
+ }
+ props := bazel.BazelTargetModuleProperties{Rule_class: "java_import"}
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: android.RemoveOptionalPrebuiltPrefix(i.Name())}, attrs)
+
+}
diff --git a/java/java_test.go b/java/java_test.go
index bc9b409..21c76b6 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -988,69 +988,15 @@
}
`)
- barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+ barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
for i := 0; i < 3; i++ {
barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i))
- if !strings.Contains(barJavac.Args["classpath"], barHeaderJar) {
- t.Errorf("bar javac classpath %v does not contain %q", barJavac.Args["classpath"], barHeaderJar)
+ if !strings.HasPrefix(barJavac.Args["classpath"], "-classpath "+barHeaderJar+":") {
+ t.Errorf("bar javac classpath %v does start with %q", barJavac.Args["classpath"], barHeaderJar)
}
}
}
-func TestJarGenrules(t *testing.T) {
- ctx, _ := testJava(t, `
- java_library {
- name: "foo",
- srcs: ["a.java"],
- }
-
- java_genrule {
- name: "jargen",
- tool_files: ["b.java"],
- cmd: "$(location b.java) $(in) $(out)",
- out: ["jargen.jar"],
- srcs: [":foo"],
- }
-
- java_library {
- name: "bar",
- static_libs: ["jargen"],
- srcs: ["c.java"],
- }
-
- java_library {
- name: "baz",
- libs: ["jargen"],
- srcs: ["c.java"],
- }
- `)
-
- foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar")
- jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar")
- bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar")
- baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
- barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
-
- if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) {
- t.Errorf("expected jargen inputs [%q], got %q", w, g)
- }
-
- if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) {
- t.Errorf("bar classpath %v does not contain %q", bar.Args["classpath"], jargen.Output.String())
- }
-
- if !strings.Contains(baz.Args["classpath"], jargen.Output.String()) {
- t.Errorf("baz classpath %v does not contain %q", baz.Args["classpath"], jargen.Output.String())
- }
-
- if len(barCombined.Inputs) != 2 ||
- barCombined.Inputs[0].String() != bar.Output.String() ||
- barCombined.Inputs[1].String() != jargen.Output.String() {
- t.Errorf("bar combined jar inputs %v is not [%q, %q]",
- barCombined.Inputs.Strings(), bar.Output.String(), jargen.Output.String())
- }
-}
-
func TestExcludeFileGroupInSrcs(t *testing.T) {
ctx, _ := testJava(t, `
java_library {
@@ -1357,6 +1303,36 @@
}
}
+func TestAidlFlagsWithMinSdkVersion(t *testing.T) {
+ fixture := android.GroupFixturePreparers(
+ prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}}))
+
+ for _, tc := range []struct {
+ name string
+ sdkVersion string
+ expected string
+ }{
+ {"default is current", "", "current"},
+ {"use sdk_version", `sdk_version: "14"`, "14"},
+ {"system_current", `sdk_version: "system_current"`, "current"},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := fixture.RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ srcs: ["aidl/foo/IFoo.aidl"],
+ `+tc.sdkVersion+`
+ }
+ `)
+ aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+ expectedAidlFlag := "--min_sdk_version=" + tc.expected
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+ })
+ }
+}
+
func TestDataNativeBinaries(t *testing.T) {
ctx, _ := testJava(t, `
java_test_host {
@@ -1484,3 +1460,64 @@
t.Errorf("expected errorprone to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
}
}
+
+func TestDataDeviceBinsBuildsDeviceBinary(t *testing.T) {
+ bp := `
+ java_test_host {
+ name: "foo",
+ srcs: ["test.java"],
+ data_device_bins: ["bar"],
+ }
+
+ cc_binary {
+ name: "bar",
+ }
+ `
+
+ ctx := android.GroupFixturePreparers(
+ PrepareForIntegrationTestWithJava,
+ ).RunTestWithBp(t, bp)
+
+ buildOS := ctx.Config.BuildOS.String()
+ fooVariant := ctx.ModuleForTests("foo", buildOS+"_common")
+ barVariant := ctx.ModuleForTests("bar", "android_arm64_armv8-a")
+ fooMod := fooVariant.Module().(*TestHost)
+
+ relocated := barVariant.Output("bar")
+ expectedInput := "out/soong/.intermediates/bar/android_arm64_armv8-a/unstripped/bar"
+ android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
+
+ entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, fooMod)[0]
+ expectedData := []string{
+ "out/soong/.intermediates/bar/android_arm64_armv8-a/bar:bar",
+ }
+ actualData := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", ctx.Config, expectedData, actualData)
+}
+
+func TestDataDeviceBinsAutogenTradefedConfig(t *testing.T) {
+ bp := `
+ java_test_host {
+ name: "foo",
+ srcs: ["test.java"],
+ data_device_bins: ["bar"],
+ }
+
+ cc_binary {
+ name: "bar",
+ }
+ `
+
+ ctx := android.GroupFixturePreparers(
+ PrepareForIntegrationTestWithJava,
+ ).RunTestWithBp(t, bp)
+
+ buildOS := ctx.Config.BuildOS.String()
+ fooModule := ctx.ModuleForTests("foo", buildOS+"_common")
+ expectedAutogenConfig := `<option name="push-file" key="bar" value="/data/local/tests/unrestricted/foo/bar" />`
+
+ autogen := fooModule.Rule("autogen")
+ if !strings.Contains(autogen.Args["extraConfigs"], expectedAutogenConfig) {
+ t.Errorf("foo extraConfigs %v does not contain %q", autogen.Args["extraConfigs"], expectedAutogenConfig)
+ }
+}
diff --git a/java/jdeps.go b/java/jdeps.go
index 0ab2e42..eff9a31 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -40,16 +40,11 @@
var _ android.SingletonMakeVarsProvider = (*jdepsGeneratorSingleton)(nil)
const (
- // Environment variables used to modify behavior of this singleton.
- envVariableCollectJavaDeps = "SOONG_COLLECT_JAVA_DEPS"
- jdepsJsonFileName = "module_bp_java_deps.json"
+ jdepsJsonFileName = "module_bp_java_deps.json"
)
func (j *jdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- if !ctx.Config().IsEnvTrue(envVariableCollectJavaDeps) {
- return
- }
-
+ // (b/204397180) Generate module_bp_java_deps.json by default.
moduleInfos := make(map[string]android.IdeInfo)
ctx.VisitAllModules(func(module android.Module) {
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 7749310..e3396c1 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -20,6 +20,8 @@
)
var legacyCorePlatformApiModules = []string{
+ "AAECarSystemUI",
+ "AAECarSystemUI-tests",
"ArcSettings",
"ahat-test-dump",
"android.car",
@@ -30,28 +32,33 @@
"api-stubs-docs",
"art_cts_jvmti_test_library",
"art-gtest-jars-MyClassNatives",
+ "BackupEncryption",
"BackupFrameworksServicesRoboTests",
"backuplib",
"BandwidthEnforcementTest",
"BlockedNumberProvider",
"BluetoothInstrumentationTests",
+ "BluetoothMidiLib",
"BluetoothMidiService",
- "CarDeveloperOptions",
+ "BTTestApp",
+ "CallEnhancement",
+ "CapCtrlInterface",
"CarService",
"CarServiceTest",
- "car-apps-common",
"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",
- "CtsIkeTestCases",
- "CtsAppExitTestCases",
"CtsLibcoreWycheproofBCTestCases",
"CtsMediaTestCases",
"CtsNetTestCases",
@@ -64,8 +71,10 @@
"DeviceInfo",
"DiagnosticTools",
"DisplayCutoutEmulationEmu01Overlay",
+ "DocumentsUIGoogleTests",
"DocumentsUIPerfTests",
"DocumentsUITests",
+ "DocumentsUIUnitTests",
"DownloadProvider",
"DownloadProviderTests",
"DownloadProviderUi",
@@ -75,10 +84,12 @@
"ethernet-service",
"EthernetServiceTests",
"ExternalStorageProvider",
- "ExtServices",
- "ExtServices-core",
- "framework-all",
+ "face-V1-0-javalib",
+ "FloralClocks",
+ "framework-jobscheduler",
"framework-minus-apex",
+ "framework-minus-apex-intdefs",
+ "FrameworkOverlayG6QU3",
"FrameworksCoreTests",
"FrameworksIkeTests",
"FrameworksNetCommonTests",
@@ -87,29 +98,50 @@
"FrameworksServicesTests",
"FrameworksMockingServicesTests",
"FrameworksUtilTests",
- "FrameworksWifiTests",
+ "GtsIncrementalInstallTestCases",
+ "GtsIncrementalInstallTriggerApp",
+ "GtsInstallerV2TestCases",
+ "HelloOslo",
"hid",
"hidl_test_java_java",
"hwbinder",
- "ims",
+ "imssettings",
+ "izat.lib.glue",
"KeyChain",
- "ksoap2",
+ "LocalSettingsLib",
"LocalTransport",
"lockagent",
"mediaframeworktest",
- "MediaProvider",
+ "mediatek-ims-base",
"MmsService",
- "MtpDocumentsProvider",
+ "ModemTestMode",
+ "MtkCapCtrl",
+ "MtpService",
"MultiDisplayProvider",
+ "my.tests.snapdragonsdktest",
+ "NetworkSetting",
"NetworkStackIntegrationTestsLib",
"NetworkStackNextIntegrationTests",
"NetworkStackNextTests",
"NetworkStackTests",
"NetworkStackTestsLib",
- "NfcNci",
+ "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",
@@ -123,21 +155,50 @@
"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",
- "TelephonyProvider",
"TelephonyProviderTests",
"TeleService",
"testables",
@@ -147,12 +208,16 @@
"time_zone_distro_installer-tests",
"time_zone_distro-tests",
"time_zone_updater",
+ "TMobilePlanProvider",
"TvProvider",
"uiautomator-stubs-docs",
+ "uimgbamanagerlibrary",
"UsbHostExternalManagementTestApp",
"UserDictionaryProvider",
+ "UxPerformance",
"WallpaperBackup",
- "wifi-service",
+ "WallpaperBackupAgentTests",
+ "WfdCommon",
}
var legacyCorePlatformApiLookup = make(map[string]struct{})
diff --git a/java/lint.go b/java/lint.go
index fe3218e..7845c33 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -377,6 +377,7 @@
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")
depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
@@ -447,6 +448,8 @@
cmd.FlagWithInput("--baseline ", lintBaseline.Path())
}
+ cmd.FlagWithOutput("--write-reference-baseline ", baseline)
+
cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 9fec08a..1e27238 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -68,7 +68,6 @@
func platformBootclasspathFactory() android.SingletonModule {
m := &platformBootclasspathModule{}
m.AddProperties(&m.properties)
- // TODO(satayev): split apex jars into separate configs.
initClasspathFragment(m, BOOTCLASSPATH)
android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
return m
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 0d8ebac..f442ddf 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -115,7 +115,7 @@
Include: "$(BUILD_PREBUILT)",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
},
},
diff --git a/java/plugin.go b/java/plugin.go
index 297ac2c..4b174b9 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -14,7 +14,10 @@
package java
-import "android/soong/android"
+import (
+ "android/soong/android"
+ "android/soong/bazel"
+)
func init() {
registerJavaPluginBuildComponents(android.InitRegistrationContext)
@@ -24,7 +27,6 @@
ctx.RegisterModuleType("java_plugin", PluginFactory)
}
-// A java_plugin module describes a host java library that will be used by javac as an annotation processor.
func PluginFactory() android.Module {
module := &Plugin{}
@@ -32,9 +34,13 @@
module.AddProperties(&module.pluginProperties)
InitJavaModule(module, android.HostSupported)
+
+ android.InitBazelModule(module)
+
return module
}
+// Plugin describes a java_plugin module, a host java library that will be used by javac as an annotation processor.
type Plugin struct {
Library
@@ -50,3 +56,29 @@
// parallelism and cause more recompilation for modules that depend on modules that use this plugin.
Generates_api *bool
}
+
+type pluginAttributes struct {
+ *javaLibraryAttributes
+ Processor_class *string
+ Target_compatible_with bazel.LabelListAttribute
+}
+
+// ConvertWithBp2build is used to convert android_app to Bazel.
+func (p *Plugin) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ libAttrs := p.convertLibraryAttrsBp2Build(ctx)
+ attrs := &pluginAttributes{
+ libAttrs,
+ nil,
+ bazel.LabelListAttribute{},
+ }
+
+ if p.pluginProperties.Processor_class != nil {
+ attrs.Processor_class = p.pluginProperties.Processor_class
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "java_plugin",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: p.Name()}, attrs)
+}
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c33e6c2..c67e2bd 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -167,30 +167,24 @@
localPath := strings.TrimPrefix(f, mydir)
module, apiver, scope := parseJarPath(localPath)
createImport(mctx, module, scope, apiver, localPath, sdkVersion, compileDex)
+
+ if module == "core-for-system-modules" {
+ createSystemModules(mctx, apiver, scope)
+ }
}
}
-func createSystemModules(mctx android.LoadHookContext, apiver string) {
+func createSystemModules(mctx android.LoadHookContext, apiver string, scope string) {
props := struct {
Name *string
Libs []string
}{}
- props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", "public", apiver))
- props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", "public", apiver))
+ props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, apiver))
+ props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, apiver))
mctx.CreateModule(systemModulesImportFactory, &props)
}
-func prebuiltSdkSystemModules(mctx android.LoadHookContext, p *prebuiltApis) {
- for _, apiver := range p.properties.Api_dirs {
- jar := android.ExistentPathForSource(mctx,
- mctx.ModuleDir(), apiver, "public", "core-for-system-modules.jar")
- if jar.Valid() {
- createSystemModules(mctx, apiver)
- }
- }
-}
-
func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
mydir := mctx.ModuleDir() + "/"
// <apiver>/<scope>/api/<module>.txt
@@ -273,7 +267,6 @@
if p, ok := mctx.Module().(*prebuiltApis); ok {
prebuiltApiFiles(mctx, p)
prebuiltSdkStubs(mctx, p)
- prebuiltSdkSystemModules(mctx, p)
}
}
diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go
new file mode 100644
index 0000000..79f4225
--- /dev/null
+++ b/java/prebuilt_apis_test.go
@@ -0,0 +1,56 @@
+// 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 java
+
+import (
+ "sort"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+func TestPrebuiltApis_SystemModulesCreation(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ FixtureWithPrebuiltApis(map[string][]string{
+ "31": {},
+ "32": {},
+ "current": {},
+ }),
+ ).RunTest(t)
+
+ sdkSystemModules := []string{}
+ result.VisitAllModules(func(module blueprint.Module) {
+ name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+ if strings.HasPrefix(name, "sdk_") && strings.HasSuffix(name, "_system_modules") {
+ sdkSystemModules = append(sdkSystemModules, name)
+ }
+ })
+ sort.Strings(sdkSystemModules)
+ expected := []string{
+ // 31 only has public system modules.
+ "sdk_public_31_system_modules",
+
+ // 32 and current both have public and module-lib system modules.
+ "sdk_public_32_system_modules",
+ "sdk_module-lib_32_system_modules",
+ "sdk_public_current_system_modules",
+ "sdk_module-lib_current_system_modules",
+ }
+ sort.Strings(expected)
+ android.AssertArrayString(t, "sdk system modules", expected, sdkSystemModules)
+}
diff --git a/java/proto.go b/java/proto.go
index 8731822..8d23803 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -24,7 +24,7 @@
func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android.ProtoFlags) android.Paths {
// Shard proto files into groups of 100 to avoid having to recompile all of them if one changes and to avoid
// hitting command line length limits.
- shards := android.ShardPaths(protoFiles, 100)
+ shards := android.ShardPaths(protoFiles, 50)
srcJarFiles := make(android.Paths, 0, len(shards))
@@ -73,13 +73,15 @@
}
func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
+ const unspecifiedProtobufPluginType = ""
if String(p.Proto.Plugin) == "" {
switch String(p.Proto.Type) {
+ case "stream": // does not require additional dependencies
case "micro":
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro")
case "nano":
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano")
- case "lite", "":
+ case "lite", unspecifiedProtobufPluginType:
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
case "full":
if ctx.Host() || ctx.BazelConversionMode() {
@@ -102,6 +104,9 @@
if String(p.Proto.Plugin) == "" {
var typeToPlugin string
switch String(p.Proto.Type) {
+ case "stream":
+ flags.proto.OutTypeFlag = "--javastream_out"
+ typeToPlugin = "javastream"
case "micro":
flags.proto.OutTypeFlag = "--javamicro_out"
typeToPlugin = "javamicro"
diff --git a/java/proto_test.go b/java/proto_test.go
new file mode 100644
index 0000000..d1cb714
--- /dev/null
+++ b/java/proto_test.go
@@ -0,0 +1,53 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+const protoModules = `
+java_library_static {
+ name: "libprotobuf-java-lite",
+}
+`
+
+func TestProtoStream(t *testing.T) {
+ bp := `
+ java_library {
+ name: "java-stream-protos",
+ proto: {
+ type: "stream",
+ },
+ srcs: [
+ "a.proto",
+ "b.proto",
+ ],
+ }
+ `
+
+ ctx := android.GroupFixturePreparers(
+ PrepareForIntegrationTestWithJava,
+ ).RunTestWithBp(t, protoModules+bp)
+
+ proto0 := ctx.ModuleForTests("java-stream-protos", "android_common").Output("proto/proto0.srcjar")
+
+ if cmd := proto0.RuleParams.Command; !strings.Contains(cmd, "--javastream_out=") {
+ t.Errorf("expected '--javastream_out' in %q", cmd)
+ }
+}
diff --git a/java/robolectric.go b/java/robolectric.go
index a3603ad..f719521 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -212,13 +212,7 @@
installDeps = append(installDeps, installedData)
}
- installed := ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
-
- if r.ExportedToMake() {
- // Soong handles installation here, but Make is usually what creates the phony rule that atest
- // uses to build the module. Create it here for now.
- ctx.Phony(ctx.ModuleName(), installed)
- }
+ r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
}
func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath,
@@ -282,6 +276,10 @@
func (r *robolectricTest) AndroidMkEntries() []android.AndroidMkEntries {
entriesList := r.Library.AndroidMkEntries()
entries := &entriesList[0]
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+ })
entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
func(w io.Writer, name, prefix, moduleDir string) {
@@ -354,7 +352,6 @@
return module
}
-func (r *robolectricTest) InstallBypassMake() bool { return true }
func (r *robolectricTest) InstallInTestcases() bool { return true }
func (r *robolectricTest) InstallForceOS() (*android.OsType, *android.ArchType) {
return &r.forceOSType, &r.forceArchType
@@ -432,7 +429,6 @@
}
}
-func (r *robolectricRuntimes) InstallBypassMake() bool { return true }
func (r *robolectricRuntimes) InstallInTestcases() bool { return true }
func (r *robolectricRuntimes) InstallForceOS() (*android.OsType, *android.ArchType) {
return &r.forceOSType, &r.forceArchType
diff --git a/java/rro.go b/java/rro.go
index 0b4d091..be84aff 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -139,7 +139,7 @@
aaptLinkFlags = append(aaptLinkFlags,
"--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
}
- r.aapt.buildActions(ctx, r, nil, aaptLinkFlags...)
+ r.aapt.buildActions(ctx, r, nil, nil, aaptLinkFlags...)
// Sign the built package
_, certificates := collectAppDeps(ctx, r, false, false)
diff --git a/java/rro_test.go b/java/rro_test.go
index 27abbe4..be0d7ba 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -62,6 +62,7 @@
result := android.GroupFixturePreparers(
PrepareForTestWithJavaDefaultModules,
PrepareForTestWithOverlayBuildComponents,
+ android.FixtureModifyConfig(android.SetKatiEnabledForTests),
fs.AddToFixture(),
).RunTestWithBp(t, bp)
@@ -127,7 +128,10 @@
}
func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
- ctx, config := testJava(t, `
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyConfig(android.SetKatiEnabledForTests),
+ ).RunTestWithBp(t, `
java_defaults {
name: "rro_defaults",
theme: "default_theme",
@@ -148,7 +152,7 @@
//
// RRO module with defaults
//
- m := ctx.ModuleForTests("foo_with_defaults", "android_common")
+ m := result.ModuleForTests("foo_with_defaults", "android_common")
// Check AAPT2 link flags.
aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
@@ -159,14 +163,14 @@
}
// Check device location.
- path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ path := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
expectedPath := []string{shared.JoinPath("out/target/product/test_device/product/overlay/default_theme")}
- android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
//
// RRO module without defaults
//
- m = ctx.ModuleForTests("foo_barebones", "android_common")
+ m = result.ModuleForTests("foo_barebones", "android_common")
// Check AAPT2 link flags.
aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
@@ -176,9 +180,9 @@
}
// Check device location.
- path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ path = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
- android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
}
func TestOverrideRuntimeResourceOverlay(t *testing.T) {
diff --git a/java/sdk.go b/java/sdk.go
index d1b899e..0dddd40 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -55,11 +55,37 @@
return JAVA_VERSION_7
} else if sdk.FinalOrFutureInt() <= 29 {
return JAVA_VERSION_8
- } else {
+ } else if sdk.FinalOrFutureInt() <= 31 {
return JAVA_VERSION_9
+ } else {
+ return JAVA_VERSION_11
}
}
+// systemModuleKind returns the kind of system modules to use for the supplied combination of sdk
+// kind and API level.
+func systemModuleKind(sdkKind android.SdkKind, apiLevel android.ApiLevel) android.SdkKind {
+ systemModuleKind := sdkKind
+ if apiLevel.LessThanOrEqualTo(android.LastWithoutModuleLibCoreSystemModules) {
+ // API levels less than or equal to 31 did not provide a core-for-system-modules.jar
+ // specifically for the module-lib API. So, always use the public system modules for them.
+ systemModuleKind = android.SdkPublic
+ } else if systemModuleKind == android.SdkCore {
+ // Core is by definition what is included in the system module for the public API so should
+ // just use its system modules.
+ systemModuleKind = android.SdkPublic
+ } else if systemModuleKind == android.SdkSystem || systemModuleKind == android.SdkTest {
+ // The core system and test APIs are currently the same as the public API so they should use
+ // its system modules.
+ systemModuleKind = android.SdkPublic
+ } else if systemModuleKind == android.SdkSystemServer {
+ // The core system server API is the same as the core module-lib API.
+ systemModuleKind = android.SdkModule
+ }
+
+ return systemModuleKind
+}
+
func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep {
sdkVersion := sdkContext.SdkVersion(ctx)
if !sdkVersion.Valid() {
@@ -105,7 +131,8 @@
var systemModules string
if defaultJavaLanguageVersion(ctx, sdkVersion).usesJavaModules() {
- systemModules = "sdk_public_" + sdkVersion.ApiLevel.String() + "_system_modules"
+ systemModuleKind := systemModuleKind(sdkVersion.Kind, sdkVersion.ApiLevel)
+ systemModules = fmt.Sprintf("sdk_%s_%s_system_modules", systemModuleKind, sdkVersion.ApiLevel)
}
return sdkDep{
@@ -116,13 +143,15 @@
}
}
- toModule := func(modules []string, res string, aidl android.Path) sdkDep {
+ 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)
return sdkDep{
useModule: true,
- bootclasspath: append(modules, config.DefaultLambdaStubsLibrary),
- systemModules: "core-current-stubs-system-modules",
- java9Classpath: modules,
- frameworkResModule: res,
+ bootclasspath: []string{module, config.DefaultLambdaStubsLibrary},
+ systemModules: fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind),
+ java9Classpath: []string{module},
+ frameworkResModule: "framework-res",
aidl: android.OptionalPathForPath(aidl),
}
}
@@ -161,38 +190,24 @@
noFrameworksLibs: true,
}
case android.SdkPublic:
- return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
+ return toModule("android_stubs_current", sdkFrameworkAidlPath(ctx))
case android.SdkSystem:
- return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
+ return toModule("android_system_stubs_current", sdkFrameworkAidlPath(ctx))
case android.SdkTest:
- return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
+ return toModule("android_test_stubs_current", sdkFrameworkAidlPath(ctx))
case android.SdkCore:
return sdkDep{
useModule: true,
bootclasspath: []string{"core.current.stubs", config.DefaultLambdaStubsLibrary},
- systemModules: "core-current-stubs-system-modules",
+ systemModules: "core-public-stubs-system-modules",
noFrameworksLibs: true,
}
case android.SdkModule:
// TODO(146757305): provide .apk and .aidl that have more APIs for modules
- return sdkDep{
- useModule: true,
- bootclasspath: []string{"android_module_lib_stubs_current", config.DefaultLambdaStubsLibrary},
- systemModules: "core-module-lib-stubs-system-modules",
- java9Classpath: []string{"android_module_lib_stubs_current"},
- frameworkResModule: "framework-res",
- aidl: android.OptionalPathForPath(nonUpdatableFrameworkAidlPath(ctx)),
- }
+ return toModule("android_module_lib_stubs_current", nonUpdatableFrameworkAidlPath(ctx))
case android.SdkSystemServer:
// TODO(146757305): provide .apk and .aidl that have more APIs for modules
- return sdkDep{
- useModule: true,
- bootclasspath: []string{"android_system_server_stubs_current", config.DefaultLambdaStubsLibrary},
- systemModules: "core-module-lib-stubs-system-modules",
- java9Classpath: []string{"android_system_server_stubs_current"},
- frameworkResModule: "framework-res",
- aidl: android.OptionalPathForPath(sdkFrameworkAidlPath(ctx)),
- }
+ return toModule("android_system_server_stubs_current", sdkFrameworkAidlPath(ctx))
default:
panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw))
}
@@ -370,7 +385,7 @@
"frameworks-base-api-current.txt",
"frameworks-base-api-system-current.txt",
"frameworks-base-api-module-lib-current.txt",
- "services-system-server-current.txt",
+ "frameworks-base-api-system-server-current.txt",
}
count := 0
ctx.VisitAllModules(func(module android.Module) {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 273efec..e794a48 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -32,25 +32,7 @@
)
const (
- sdkXmlFileSuffix = ".xml"
- permissionsTemplate = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n` +
- `<!-- Copyright (C) 2018 The Android Open Source Project\n` +
- `\n` +
- ` Licensed under the Apache License, Version 2.0 (the \"License\");\n` +
- ` you may not use this file except in compliance with the License.\n` +
- ` You may obtain a copy of the License at\n` +
- `\n` +
- ` http://www.apache.org/licenses/LICENSE-2.0\n` +
- `\n` +
- ` Unless required by applicable law or agreed to in writing, software\n` +
- ` distributed under the License is distributed on an \"AS IS\" BASIS,\n` +
- ` WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n` +
- ` See the License for the specific language governing permissions and\n` +
- ` limitations under the License.\n` +
- `-->\n` +
- `<permissions>\n` +
- ` <library name=\"%s\" file=\"%s\"/>\n` +
- `</permissions>\n`
+ sdkXmlFileSuffix = ".xml"
)
// A tag to associated a dependency with a specific api scope.
@@ -361,13 +343,14 @@
// The sdk_version to use for building the stubs.
//
// If not specified then it will use an sdk_version determined as follows:
+ //
// 1) If the sdk_version specified on the java_sdk_library is none then this
- // will be none. This is used for java_sdk_library instances that are used
- // to create stubs that contribute to the core_current sdk version.
- // 2) Otherwise, it is assumed that this library extends but does not contribute
- // directly to a specific sdk_version and so this uses the sdk_version appropriate
- // for the api scope. e.g. public will use sdk_version: current, system will use
- // sdk_version: system_current, etc.
+ // will be none. This is used for java_sdk_library instances that are used
+ // to create stubs that contribute to the core_current sdk version.
+ // 2) Otherwise, it is assumed that this library extends but does not
+ // contribute directly to a specific sdk_version and so this uses the
+ // sdk_version appropriate for the api scope. e.g. public will use
+ // sdk_version: current, system will use sdk_version: system_current, etc.
//
// This does not affect the sdk_version used for either generating the stubs source
// or the API file. They both have to use the same sdk_version as is used for
@@ -636,6 +619,33 @@
// Files containing information about supported java doc tags.
Doctag_files []string `android:"path"`
+
+ // Signals that this shared library is part of the bootclasspath starting
+ // on the version indicated in this attribute.
+ //
+ // This will make platforms at this level and above to ignore
+ // <uses-library> tags with this library name because the library is already
+ // available
+ On_bootclasspath_since *string
+
+ // Signals that this shared library was part of the bootclasspath before
+ // (but not including) the version indicated in this attribute.
+ //
+ // The system will automatically add a <uses-library> tag with this library to
+ // apps that target any SDK less than the version indicated in this attribute.
+ On_bootclasspath_before *string
+
+ // Indicates that PackageManager should ignore this shared library if the
+ // platform is below the version indicated in this attribute.
+ //
+ // This means that the device won't recognise this library as installed.
+ Min_device_sdk *string
+
+ // Indicates that PackageManager should ignore this shared library if the
+ // platform is above the version indicated in this attribute.
+ //
+ // This means that the device won't recognise this library as installed.
+ Max_device_sdk *string
}
// commonSdkLibraryAndImportModule defines the interface that must be provided by a module that
@@ -1118,6 +1128,22 @@
return generatedScopes
}
+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) {
+ ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+ isExternal := !module.depIsInSameApex(ctx, child)
+ if am, ok := child.(android.ApexModule); ok {
+ if !do(ctx, parent, am, isExternal) {
+ return false
+ }
+ }
+ return !isExternal
+ })
+ })
+}
+
type sdkLibraryComponentTag struct {
blueprint.BaseDependencyTag
name string
@@ -1195,14 +1221,23 @@
func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
paths, err := module.commonOutputFiles(tag)
- if paths == nil && err == nil {
- return module.Library.OutputFiles(tag)
- } else {
+ if paths != nil || err != nil {
return paths, err
}
+ if module.requiresRuntimeImplementationLibrary() {
+ return module.Library.OutputFiles(tag)
+ }
+ if tag == "" {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if proptools.String(module.deviceProperties.Min_sdk_version) != "" {
+ module.CheckMinSdkVersion(ctx)
+ }
+
module.generateCommonBuildActions(ctx)
// Only build an implementation library if required.
@@ -1579,14 +1614,29 @@
// Creates the xml file that publicizes the runtime library
func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
+ moduleMinApiLevel := module.Library.MinSdkVersion(mctx).ApiLevel
+ var moduleMinApiLevelStr = moduleMinApiLevel.String()
+ if moduleMinApiLevel == android.NoneApiLevel {
+ moduleMinApiLevelStr = "current"
+ }
props := struct {
- Name *string
- Lib_name *string
- Apex_available []string
+ Name *string
+ Lib_name *string
+ Apex_available []string
+ On_bootclasspath_since *string
+ On_bootclasspath_before *string
+ Min_device_sdk *string
+ Max_device_sdk *string
+ Sdk_library_min_api_level *string
}{
- Name: proptools.StringPtr(module.xmlPermissionsModuleName()),
- Lib_name: proptools.StringPtr(module.BaseModuleName()),
- Apex_available: module.ApexProperties.Apex_available,
+ Name: proptools.StringPtr(module.xmlPermissionsModuleName()),
+ Lib_name: proptools.StringPtr(module.BaseModuleName()),
+ Apex_available: module.ApexProperties.Apex_available,
+ On_bootclasspath_since: module.commonSdkLibraryProperties.On_bootclasspath_since,
+ On_bootclasspath_before: module.commonSdkLibraryProperties.On_bootclasspath_before,
+ Min_device_sdk: module.commonSdkLibraryProperties.Min_device_sdk,
+ Max_device_sdk: module.commonSdkLibraryProperties.Max_device_sdk,
+ Sdk_library_min_api_level: &moduleMinApiLevelStr,
}
mctx.CreateModule(sdkLibraryXmlFactory, &props)
@@ -2382,6 +2432,38 @@
type sdkLibraryXmlProperties struct {
// canonical name of the lib
Lib_name *string
+
+ // Signals that this shared library is part of the bootclasspath starting
+ // on the version indicated in this attribute.
+ //
+ // This will make platforms at this level and above to ignore
+ // <uses-library> tags with this library name because the library is already
+ // available
+ On_bootclasspath_since *string
+
+ // Signals that this shared library was part of the bootclasspath before
+ // (but not including) the version indicated in this attribute.
+ //
+ // The system will automatically add a <uses-library> tag with this library to
+ // apps that target any SDK less than the version indicated in this attribute.
+ On_bootclasspath_before *string
+
+ // Indicates that PackageManager should ignore this shared library if the
+ // platform is below the version indicated in this attribute.
+ //
+ // This means that the device won't recognise this library as installed.
+ Min_device_sdk *string
+
+ // Indicates that PackageManager should ignore this shared library if the
+ // platform is above the version indicated in this attribute.
+ //
+ // This means that the device won't recognise this library as installed.
+ Max_device_sdk *string
+
+ // The SdkLibrary's min api level as a string
+ //
+ // This value comes from the ApiLevel of the MinSdkVersion property.
+ Sdk_library_min_api_level *string
}
// java_sdk_library_xml builds the permission xml file for a java_sdk_library.
@@ -2458,11 +2540,87 @@
return "/" + partition + "/framework/" + implName + ".jar"
}
+func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string {
+ if value == nil {
+ return ""
+ }
+ apiLevel, err := android.ApiLevelFromUser(ctx, *value)
+ if err != nil {
+ // attributes in bp files have underscores but in the xml have dashes.
+ ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
+ return ""
+ }
+ if apiLevel.IsCurrent() {
+ // passing "current" would always mean a future release, never the current (or the current in
+ // progress) which means some conditions would never be triggered.
+ ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
+ `"current" is not an allowed value for this attribute`)
+ return ""
+ }
+ return formattedOptionalAttribute(attrName, value)
+}
+
+// formats an attribute for the xml permissions file if the value is not null
+// returns empty string otherwise
+func formattedOptionalAttribute(attrName string, value *string) string {
+ if value == nil {
+ return ""
+ }
+ return fmt.Sprintf(` %s=\"%s\"\n`, attrName, *value)
+}
+
+func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string {
+ libName := proptools.String(module.properties.Lib_name)
+ libNameAttr := formattedOptionalAttribute("name", &libName)
+ filePath := module.implPath(ctx)
+ filePathAttr := formattedOptionalAttribute("file", &filePath)
+ implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since)
+ implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
+ minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
+ maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
+ // <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that).
+ // similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
+ var libraryTag string
+ if module.properties.Min_device_sdk != nil {
+ libraryTag = ` <apex-library\n`
+ } else {
+ libraryTag = ` <library\n`
+ }
+
+ return strings.Join([]string{
+ `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n`,
+ `<!-- Copyright (C) 2018 The Android Open Source Project\n`,
+ `\n`,
+ ` Licensed under the Apache License, Version 2.0 (the \"License\");\n`,
+ ` you may not use this file except in compliance with the License.\n`,
+ ` You may obtain a copy of the License at\n`,
+ `\n`,
+ ` http://www.apache.org/licenses/LICENSE-2.0\n`,
+ `\n`,
+ ` Unless required by applicable law or agreed to in writing, software\n`,
+ ` distributed under the License is distributed on an \"AS IS\" BASIS,\n`,
+ ` WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n`,
+ ` See the License for the specific language governing permissions and\n`,
+ ` limitations under the License.\n`,
+ `-->\n`,
+ `<permissions>\n`,
+ libraryTag,
+ libNameAttr,
+ filePathAttr,
+ implicitFromAttr,
+ implicitUntilAttr,
+ minSdkAttr,
+ maxSdkAttr,
+ ` />\n`,
+ `</permissions>\n`}, "")
+}
+
func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
module.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
libName := proptools.String(module.properties.Lib_name)
- xmlContent := fmt.Sprintf(permissionsTemplate, libName, module.implPath(ctx))
+ module.selfValidate(ctx)
+ xmlContent := module.permissionsContents(ctx)
module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
rule := android.NewRuleBuilder(pctx, ctx)
@@ -2477,24 +2635,99 @@
func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
if module.hideApexVariantFromMake {
- return []android.AndroidMkEntries{android.AndroidMkEntries{
+ return []android.AndroidMkEntries{{
Disabled: true,
}}
}
- return []android.AndroidMkEntries{android.AndroidMkEntries{
+ return []android.AndroidMkEntries{{
Class: "ETC",
OutputFile: android.OptionalPathForPath(module.outputFilePath),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_TAGS", "optional")
- entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
},
},
}}
}
+func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) {
+ module.validateAtLeastTAttributes(ctx)
+ module.validateMinAndMaxDeviceSdk(ctx)
+ module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx)
+ module.validateOnBootclasspathBeforeRequirements(ctx)
+}
+
+func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) {
+ t := android.ApiLevelOrPanic(ctx, "Tiramisu")
+ module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk")
+ module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk")
+ module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before")
+ module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since")
+}
+
+func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) {
+ if attr != nil {
+ if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil {
+ // we will inform the user of invalid inputs when we try to write the
+ // permissions xml file so we don't need to do it here
+ if t.GreaterThan(level) {
+ ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T")
+ }
+ }
+ }
+}
+
+func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) {
+ if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil {
+ min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
+ max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
+ if minErr == nil && maxErr == nil {
+ // we will inform the user of invalid inputs when we try to write the
+ // permissions xml file so we don't need to do it here
+ if min.GreaterThan(max) {
+ ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk")
+ }
+ }
+ }
+}
+
+func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) {
+ moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
+ if module.properties.Min_device_sdk != nil {
+ api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
+ if err == nil {
+ if moduleMinApi.GreaterThan(api) {
+ ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
+ }
+ }
+ }
+ if module.properties.Max_device_sdk != nil {
+ api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
+ if err == nil {
+ if moduleMinApi.GreaterThan(api) {
+ ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
+ }
+ }
+ }
+}
+
+func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) {
+ moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
+ if module.properties.On_bootclasspath_before != nil {
+ t := android.ApiLevelOrPanic(ctx, "Tiramisu")
+ // if we use the attribute, then we need to do this validation
+ if moduleMinApi.LessThan(t) {
+ // if minAPi is < T, then we need to have min_device_sdk (which only accepts T+)
+ if module.properties.Min_device_sdk == nil {
+ ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T")
+ }
+ }
+ }
+}
+
type sdkLibrarySdkMemberType struct {
android.SdkMemberTypeBase
}
@@ -2527,7 +2760,7 @@
android.SdkMemberPropertiesBase
// Scope to per scope properties.
- Scopes map[*apiScope]scopeProperties
+ Scopes map[*apiScope]*scopeProperties
// The Java stubs source files.
Stub_srcs []string
@@ -2546,6 +2779,33 @@
Doctag_paths android.Paths
Permitted_packages []string
+
+ // Signals that this shared library is part of the bootclasspath starting
+ // on the version indicated in this attribute.
+ //
+ // This will make platforms at this level and above to ignore
+ // <uses-library> tags with this library name because the library is already
+ // available
+ On_bootclasspath_since *string
+
+ // Signals that this shared library was part of the bootclasspath before
+ // (but not including) the version indicated in this attribute.
+ //
+ // The system will automatically add a <uses-library> tag with this library to
+ // apps that target any SDK less than the version indicated in this attribute.
+ On_bootclasspath_before *string
+
+ // Indicates that PackageManager should ignore this shared library if the
+ // platform is below the version indicated in this attribute.
+ //
+ // This means that the device won't recognise this library as installed.
+ Min_device_sdk *string
+
+ // Indicates that PackageManager should ignore this shared library if the
+ // platform is above the version indicated in this attribute.
+ //
+ // This means that the device won't recognise this library as installed.
+ Max_device_sdk *string
}
type scopeProperties struct {
@@ -2553,14 +2813,14 @@
StubsSrcJar android.Path
CurrentApiFile android.Path
RemovedApiFile android.Path
- AnnotationsZip android.Path
+ AnnotationsZip android.Path `supported_build_releases:"Tiramisu+"`
SdkVersion string
}
func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
sdk := variant.(*SdkLibrary)
- s.Scopes = make(map[*apiScope]scopeProperties)
+ s.Scopes = make(map[*apiScope]*scopeProperties)
for _, apiScope := range allApiScopes {
paths := sdk.findScopePaths(apiScope)
if paths == nil {
@@ -2583,7 +2843,7 @@
if paths.annotationsZip.Valid() {
properties.AnnotationsZip = paths.annotationsZip.Path()
}
- s.Scopes[apiScope] = properties
+ s.Scopes[apiScope] = &properties
}
}
@@ -2592,6 +2852,10 @@
s.Compile_dex = sdk.dexProperties.Compile_dex
s.Doctag_paths = sdk.doctagPaths
s.Permitted_packages = sdk.PermittedPackagesForUpdatableBootJars()
+ s.On_bootclasspath_since = sdk.commonSdkLibraryProperties.On_bootclasspath_since
+ 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
}
func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index be23536..3500c84 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -15,12 +15,13 @@
package java
import (
- "android/soong/android"
"fmt"
"path/filepath"
"regexp"
"testing"
+ "android/soong/android"
+
"github.com/google/blueprint/proptools"
)
@@ -47,6 +48,7 @@
name: "bar",
srcs: ["a.java", "b.java"],
api_packages: ["bar"],
+ exclude_kotlinc_generated_files: true,
}
java_library {
name: "baz",
@@ -107,7 +109,7 @@
libs: ["foo"],
sdk_version: "module_30",
}
- `)
+ `)
// check the existence of the internal modules
foo := result.ModuleForTests("foo", "android_common")
@@ -160,6 +162,194 @@
android.AssertDeepEquals(t, "qux exports (required)", []string{"fred", "quuz", "foo", "bar"}, requiredSdkLibs)
android.AssertDeepEquals(t, "qux exports (optional)", []string{}, optionalSdkLibs)
}
+
+ fooDexJar := result.ModuleForTests("foo", "android_common").Rule("d8")
+ // tests if kotlinc generated files are NOT excluded from output of foo.
+ android.AssertStringDoesNotContain(t, "foo dex", fooDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
+
+ barDexJar := result.ModuleForTests("bar", "android_common").Rule("d8")
+ // tests if kotlinc generated files are excluded from output of bar.
+ android.AssertStringDoesContain(t, "bar dex", barDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithPrebuiltApis(map[string][]string{
+ "28": {"foo"},
+ "29": {"foo"},
+ "30": {"foo", "fooUpdatable", "fooUpdatableErr"},
+ }),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W", "X"}
+ }),
+ ).RunTestWithBp(t,
+ `
+ java_sdk_library {
+ name: "fooUpdatable",
+ srcs: ["a.java", "b.java"],
+ api_packages: ["foo"],
+ on_bootclasspath_since: "U",
+ on_bootclasspath_before: "V",
+ min_device_sdk: "W",
+ max_device_sdk: "X",
+ min_sdk_version: "S",
+ }
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java", "b.java"],
+ api_packages: ["foo"],
+ }
+`)
+
+ // test that updatability attributes are passed on correctly
+ fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml")
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"U\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"V\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"W\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"X\"`)
+
+ // double check that updatability attributes are not written if they don't exist in the bp file
+ // the permissions file for the foo library defined above
+ fooPermissions := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml")
+ android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-since`)
+ android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-before`)
+ android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `min-device-sdk`)
+ android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `max-device-sdk`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_Validation_ValidVersion(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithPrebuiltApis(map[string][]string{
+ "30": {"fooUpdatable", "fooUpdatableErr"},
+ }),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(
+ []string{
+ `on_bootclasspath_since: "aaa" could not be parsed as an integer and is not a recognized codename`,
+ `on_bootclasspath_before: "bbc" could not be parsed as an integer and is not a recognized codename`,
+ `min_device_sdk: "ccc" could not be parsed as an integer and is not a recognized codename`,
+ `max_device_sdk: "current" is not an allowed value for this attribute`,
+ })).RunTestWithBp(t,
+ `
+ java_sdk_library {
+ name: "fooUpdatableErr",
+ srcs: ["a.java", "b.java"],
+ api_packages: ["foo"],
+ on_bootclasspath_since: "aaa",
+ on_bootclasspath_before: "bbc",
+ min_device_sdk: "ccc",
+ max_device_sdk: "current",
+ }
+`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_Validation_AtLeastTAttributes(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithPrebuiltApis(map[string][]string{
+ "28": {"foo"},
+ }),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(
+ []string{
+ "on_bootclasspath_since: Attribute value needs to be at least T",
+ "on_bootclasspath_before: Attribute value needs to be at least T",
+ "min_device_sdk: Attribute value needs to be at least T",
+ "max_device_sdk: Attribute value needs to be at least T",
+ },
+ )).RunTestWithBp(t,
+ `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java", "b.java"],
+ api_packages: ["foo"],
+ on_bootclasspath_since: "S",
+ on_bootclasspath_before: "S",
+ min_device_sdk: "S",
+ max_device_sdk: "S",
+ min_sdk_version: "S",
+ }
+`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdk(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithPrebuiltApis(map[string][]string{
+ "28": {"foo"},
+ }),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"}
+ }),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(
+ []string{
+ "min_device_sdk can't be greater than max_device_sdk",
+ },
+ )).RunTestWithBp(t,
+ `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java", "b.java"],
+ api_packages: ["foo"],
+ min_device_sdk: "V",
+ max_device_sdk: "U",
+ min_sdk_version: "S",
+ }
+`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdkAndModuleMinSdk(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithPrebuiltApis(map[string][]string{
+ "28": {"foo"},
+ }),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"}
+ }),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(
+ []string{
+ regexp.QuoteMeta("min_device_sdk: Can't be less than module's min sdk (V)"),
+ regexp.QuoteMeta("max_device_sdk: Can't be less than module's min sdk (V)"),
+ },
+ )).RunTestWithBp(t,
+ `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java", "b.java"],
+ api_packages: ["foo"],
+ min_device_sdk: "U",
+ max_device_sdk: "U",
+ min_sdk_version: "V",
+ }
+`)
+}
+
+func TestJavaSdkLibrary_UpdatableLibrary_usesNewTag(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithPrebuiltApis(map[string][]string{
+ "30": {"foo"},
+ }),
+ ).RunTestWithBp(t,
+ `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java", "b.java"],
+ min_device_sdk: "Tiramisu",
+ min_sdk_version: "S",
+ }
+`)
+ // test that updatability attributes are passed on correctly
+ fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml")
+ android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<apex-library`)
+ android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<library`)
}
func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
@@ -960,3 +1150,87 @@
})
}
}
+
+func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) {
+ preparer := android.GroupFixturePreparers(
+ PrepareForTestWithJavaBuildComponents,
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ )
+
+ preparer.RunTestWithBp(t, `
+ java_sdk_library {
+ name: "sdklib",
+ srcs: ["a.java"],
+ static_libs: ["util"],
+ min_sdk_version: "30",
+ unsafe_ignore_missing_latest_api: true,
+ }
+
+ java_library {
+ name: "util",
+ srcs: ["a.java"],
+ min_sdk_version: "30",
+ }
+ `)
+
+ preparer.
+ RunTestWithBp(t, `
+ java_sdk_library {
+ name: "sdklib",
+ srcs: ["a.java"],
+ libs: ["util"],
+ impl_only_libs: ["util"],
+ stub_only_libs: ["util"],
+ stub_only_static_libs: ["util"],
+ min_sdk_version: "30",
+ unsafe_ignore_missing_latest_api: true,
+ }
+
+ java_library {
+ name: "util",
+ srcs: ["a.java"],
+ }
+ `)
+
+ preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "util".*should support min_sdk_version\(30\)`)).
+ RunTestWithBp(t, `
+ java_sdk_library {
+ name: "sdklib",
+ srcs: ["a.java"],
+ static_libs: ["util"],
+ min_sdk_version: "30",
+ unsafe_ignore_missing_latest_api: true,
+ }
+
+ java_library {
+ name: "util",
+ srcs: ["a.java"],
+ min_sdk_version: "31",
+ }
+ `)
+
+ preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "another_util".*should support min_sdk_version\(30\)`)).
+ RunTestWithBp(t, `
+ java_sdk_library {
+ name: "sdklib",
+ srcs: ["a.java"],
+ static_libs: ["util"],
+ min_sdk_version: "30",
+ unsafe_ignore_missing_latest_api: true,
+ }
+
+ java_library {
+ name: "util",
+ srcs: ["a.java"],
+ static_libs: ["another_util"],
+ min_sdk_version: "30",
+ }
+
+ java_library {
+ name: "another_util",
+ srcs: ["a.java"],
+ min_sdk_version: "31",
+ }
+ `)
+}
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 6d62130..9e8ba6e 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -25,27 +25,36 @@
"android/soong/java/config"
)
+type classpathTestCase struct {
+ name string
+ unbundled bool
+ moduleType string
+ host android.OsClass
+ properties string
+
+ // for java 8
+ bootclasspath []string
+ java8classpath []string
+
+ // for java 9
+ system string
+ java9classpath []string
+
+ forces8 bool // if set, javac will always be called with java 8 arguments
+
+ aidl string
+
+ // Indicates how this test case is affected by the setting of Always_use_prebuilt_sdks.
+ //
+ // If this is nil then the test case is unaffected by the setting of Always_use_prebuilt_sdks.
+ // Otherwise, the test case can only be used when
+ // Always_use_prebuilt_sdks=*forAlwaysUsePrebuiltSdks.
+ forAlwaysUsePrebuiltSdks *bool
+}
+
func TestClasspath(t *testing.T) {
const frameworkAidl = "-I" + defaultJavaDir + "/framework/aidl"
- var classpathTestcases = []struct {
- name string
- unbundled bool
- moduleType string
- host android.OsClass
- properties string
-
- // for java 8
- bootclasspath []string
- java8classpath []string
-
- // for java 9
- system string
- java9classpath []string
-
- forces8 bool // if set, javac will always be called with java 8 arguments
-
- aidl string
- }{
+ var classpathTestcases = []classpathTestCase{
{
name: "default",
bootclasspath: config.StableCorePlatformBootclasspathLibraries,
@@ -91,25 +100,52 @@
aidl: "-pprebuilts/sdk/30/public/framework.aidl",
},
{
+ // Test case only applies when Always_use_prebuilt_sdks=false (the default).
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
name: "current",
properties: `sdk_version: "current",`,
bootclasspath: []string{"android_stubs_current", "core-lambda-stubs"},
- system: "core-current-stubs-system-modules",
+ system: "core-public-stubs-system-modules",
java9classpath: []string{"android_stubs_current"},
aidl: "-pout/soong/framework.aidl",
},
{
+ // Test case only applies when Always_use_prebuilt_sdks=true.
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+ name: "current",
+ properties: `sdk_version: "current",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_public_current_system_modules",
+ java8classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/current/public/framework.aidl",
+ },
+ {
+ // Test case only applies when Always_use_prebuilt_sdks=false (the default).
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
name: "system_current",
properties: `sdk_version: "system_current",`,
bootclasspath: []string{"android_system_stubs_current", "core-lambda-stubs"},
- system: "core-current-stubs-system-modules",
+ system: "core-public-stubs-system-modules",
java9classpath: []string{"android_system_stubs_current"},
aidl: "-pout/soong/framework.aidl",
},
{
+ // Test case only applies when Always_use_prebuilt_sdks=true.
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+ name: "system_current",
+ properties: `sdk_version: "system_current",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_public_current_system_modules",
+ java8classpath: []string{"prebuilts/sdk/current/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/current/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/current/public/framework.aidl",
+ },
+ {
name: "system_29",
properties: `sdk_version: "system_29",`,
bootclasspath: []string{`""`},
@@ -118,7 +154,6 @@
aidl: "-pprebuilts/sdk/29/public/framework.aidl",
},
{
-
name: "system_30",
properties: `sdk_version: "system_30",`,
bootclasspath: []string{`""`},
@@ -128,20 +163,57 @@
aidl: "-pprebuilts/sdk/30/public/framework.aidl",
},
{
+ // Test case only applies when Always_use_prebuilt_sdks=false (the default).
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
name: "test_current",
properties: `sdk_version: "test_current",`,
bootclasspath: []string{"android_test_stubs_current", "core-lambda-stubs"},
- system: "core-current-stubs-system-modules",
+ system: "core-public-stubs-system-modules",
java9classpath: []string{"android_test_stubs_current"},
aidl: "-pout/soong/framework.aidl",
},
{
+ // Test case only applies when Always_use_prebuilt_sdks=true.
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+ name: "test_current",
+ properties: `sdk_version: "test_current",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_public_current_system_modules",
+ java8classpath: []string{"prebuilts/sdk/current/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/current/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/current/public/framework.aidl",
+ },
+ {
+ name: "test_30",
+ properties: `sdk_version: "test_30",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_public_30_system_modules",
+ java8classpath: []string{"prebuilts/sdk/30/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/30/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/30/public/framework.aidl",
+ },
+ {
+ // Test case only applies when Always_use_prebuilt_sdks=false (the default).
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
name: "core_current",
properties: `sdk_version: "core_current",`,
bootclasspath: []string{"core.current.stubs", "core-lambda-stubs"},
- system: "core-current-stubs-system-modules",
+ system: "core-public-stubs-system-modules",
+ },
+ {
+ // Test case only applies when Always_use_prebuilt_sdks=true.
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+ name: "core_current",
+ properties: `sdk_version: "core_current",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_public_current_system_modules",
+ java8classpath: []string{"prebuilts/sdk/current/core/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/current/core/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/current/public/framework.aidl",
},
{
@@ -214,8 +286,10 @@
java9classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
aidl: "-pprebuilts/sdk/current/public/framework.aidl",
},
-
{
+ // Test case only applies when Always_use_prebuilt_sdks=false (the default).
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
+
name: "module_current",
properties: `sdk_version: "module_current",`,
bootclasspath: []string{"android_module_lib_stubs_current", "core-lambda-stubs"},
@@ -224,6 +298,48 @@
aidl: "-pout/soong/framework_non_updatable.aidl",
},
{
+ // Test case only applies when Always_use_prebuilt_sdks=true.
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+ name: "module_current",
+ properties: `sdk_version: "module_current",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_module-lib_current_system_modules",
+ java8classpath: []string{"prebuilts/sdk/current/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/current/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/current/public/framework.aidl",
+ },
+ {
+ name: "module_30",
+ properties: `sdk_version: "module_30",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_public_30_system_modules",
+ java8classpath: []string{"prebuilts/sdk/30/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/30/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/30/public/framework.aidl",
+ },
+ {
+ name: "module_31",
+ properties: `sdk_version: "module_31",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_public_31_system_modules",
+ java8classpath: []string{"prebuilts/sdk/31/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/31/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/31/public/framework.aidl",
+ },
+ {
+ name: "module_32",
+ properties: `sdk_version: "module_32",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_module-lib_32_system_modules",
+ java8classpath: []string{"prebuilts/sdk/32/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/32/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/32/public/framework.aidl",
+ },
+ {
+ // Test case only applies when Always_use_prebuilt_sdks=false (the default).
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
+
name: "system_server_current",
properties: `sdk_version: "system_server_current",`,
bootclasspath: []string{"android_system_server_stubs_current", "core-lambda-stubs"},
@@ -231,9 +347,62 @@
java9classpath: []string{"android_system_server_stubs_current"},
aidl: "-pout/soong/framework.aidl",
},
+ {
+ // Test case only applies when Always_use_prebuilt_sdks=true.
+ forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+ name: "system_server_current",
+ properties: `sdk_version: "system_server_current",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_module-lib_current_system_modules",
+ java8classpath: []string{"prebuilts/sdk/current/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/current/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/current/public/framework.aidl",
+ },
+ {
+ name: "system_server_30",
+ properties: `sdk_version: "system_server_30",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_public_30_system_modules",
+ java8classpath: []string{"prebuilts/sdk/30/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/30/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/30/public/framework.aidl",
+ },
+ {
+ name: "system_server_31",
+ properties: `sdk_version: "system_server_31",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_public_31_system_modules",
+ java8classpath: []string{"prebuilts/sdk/31/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/31/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/31/public/framework.aidl",
+ },
+ {
+ name: "system_server_32",
+ properties: `sdk_version: "system_server_32",`,
+ bootclasspath: []string{`""`},
+ system: "sdk_module-lib_32_system_modules",
+ java8classpath: []string{"prebuilts/sdk/32/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ java9classpath: []string{"prebuilts/sdk/32/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/32/public/framework.aidl",
+ },
}
+ t.Run("basic", func(t *testing.T) {
+ testClasspathTestCases(t, classpathTestcases, false)
+ })
+
+ t.Run("Always_use_prebuilt_sdks=true", func(t *testing.T) {
+ testClasspathTestCases(t, classpathTestcases, true)
+ })
+}
+
+func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks bool) {
for _, testcase := range classpathTestcases {
+ if testcase.forAlwaysUsePrebuiltSdks != nil && *testcase.forAlwaysUsePrebuiltSdks != alwaysUsePrebuiltSdks {
+ continue
+ }
+
t.Run(testcase.name, func(t *testing.T) {
moduleType := "java_library"
if testcase.moduleType != "" {
@@ -299,7 +468,9 @@
system = "--system=none"
} else if testcase.system != "" {
dir := ""
- if strings.HasPrefix(testcase.system, "sdk_public_") {
+ // If the system modules name starts with sdk_ then it is a prebuilt module and so comes
+ // from the prebuilt directory.
+ if strings.HasPrefix(testcase.system, "sdk_") {
dir = "prebuilts/sdk"
} else {
dir = defaultJavaDir
@@ -351,11 +522,20 @@
android.AssertPathsRelativeToTopEquals(t, "implicits", deps, javac.Implicits)
}
+ preparer := android.NullFixturePreparer
+ if alwaysUsePrebuiltSdks {
+ preparer = android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
+ })
+ }
+
fixtureFactory := android.GroupFixturePreparers(
prepareForJavaTest,
FixtureWithPrebuiltApis(map[string][]string{
"29": {},
"30": {},
+ "31": {},
+ "32": {},
"current": {},
}),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -369,6 +549,7 @@
env["ANDROID_JAVA8_HOME"] = "jdk8"
}
}),
+ preparer,
)
// Test with legacy javac -source 1.8 -target 1.8
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index f209f4a..fa61ea6 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -58,6 +58,10 @@
func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
configuredJars := p.configuredJars(ctx)
classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, p.classpathType)
+ standaloneConfiguredJars := p.standaloneConfiguredJars(ctx)
+ standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS)
+ configuredJars = configuredJars.AppendList(&standaloneConfiguredJars)
+ classpathJars = append(classpathJars, standaloneClasspathJars...)
p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
}
@@ -66,6 +70,10 @@
return dexpreopt.GetGlobalConfig(ctx).SystemServerJars
}
+func (p *platformSystemServerClasspathModule) standaloneConfiguredJars(ctx android.ModuleContext) android.ConfiguredJarList {
+ return dexpreopt.GetGlobalConfig(ctx).StandaloneSystemServerJars
+}
+
type SystemServerClasspathModule struct {
android.ModuleBase
android.ApexModuleBase
@@ -84,10 +92,15 @@
}
type systemServerClasspathFragmentProperties struct {
- // The contents of this systemserverclasspath_fragment, could be either java_library, or java_sdk_library.
+ // List of system_server classpath jars, could be either java_library, or java_sdk_library.
//
// The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH.
Contents []string
+
+ // List of jars that system_server loads dynamically using separate classloaders.
+ //
+ // The order does not matter.
+ Standalone_contents []string
}
func systemServerClasspathFactory() android.Module {
@@ -101,12 +114,16 @@
}
func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- if len(s.properties.Contents) == 0 {
- ctx.PropertyErrorf("contents", "empty contents are not allowed")
+ if len(s.properties.Contents) == 0 && len(s.properties.Standalone_contents) == 0 {
+ ctx.PropertyErrorf("contents", "Either contents or standalone_contents needs to be non-empty")
}
configuredJars := s.configuredJars(ctx)
classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, s.classpathType)
+ standaloneConfiguredJars := s.standaloneConfiguredJars(ctx)
+ standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS)
+ configuredJars = configuredJars.AppendList(&standaloneConfiguredJars)
+ classpathJars = append(classpathJars, standaloneClasspathJars...)
s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
// Collect the module directory for IDE info in java/jdeps.go.
@@ -120,15 +137,42 @@
jars, unknown := global.ApexSystemServerJars.Filter(possibleUpdatableModules)
// TODO(satayev): remove geotz ssc_fragment, since geotz is not part of SSCP anymore.
_, unknown = android.RemoveFromList("geotz", unknown)
+ // This module only exists in car products.
+ // So ignore it even if it is not in PRODUCT_APEX_SYSTEM_SERVER_JARS.
+ // TODO(b/203233647): Add better mechanism to make it optional.
+ _, unknown = android.RemoveFromList("car-frameworks-service-module", unknown)
- // For non test apexes, make sure that all contents are actually declared in make.
- if global.ApexSystemServerJars.Len() > 0 && len(unknown) > 0 && !android.IsModuleInVersionedSdk(ctx.Module()) {
+ // This module is optional, so it is not present in all products.
+ // (See PRODUCT_ISOLATED_COMPILATION_ENABLED.)
+ // So ignore it even if it is not in PRODUCT_APEX_SYSTEM_SERVER_JARS.
+ // TODO(b/203233647): Add better mechanism to make it optional.
+ _, unknown = android.RemoveFromList("service-compos", unknown)
+
+ // TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths
+ // config. However, any test specific jars would not be present in ApexSystemServerJars. Instead,
+ // we should check if we are creating a config for apex_test via ApexInfo and amend the values.
+ // 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()) {
+ // 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)
}
return jars
}
+func (s *SystemServerClasspathModule) standaloneConfiguredJars(ctx android.ModuleContext) android.ConfiguredJarList {
+ global := dexpreopt.GetGlobalConfig(ctx)
+
+ possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Standalone_contents, systemServerClasspathFragmentContentDepTag)
+ jars, _ := global.ApexStandaloneSystemServerJars.Filter(possibleUpdatableModules)
+
+ // TODO(jiakaiz): add a check to ensure that the contents are declared in make.
+
+ return jars
+}
+
type systemServerClasspathFragmentContentDependencyTag struct {
blueprint.BaseDependencyTag
}
@@ -176,8 +220,11 @@
func (s *SystemServerClasspathModule) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
module := ctx.Module()
_, isSourceModule := module.(*SystemServerClasspathModule)
+ var deps []string
+ deps = append(deps, s.properties.Contents...)
+ deps = append(deps, s.properties.Standalone_contents...)
- for _, name := range s.properties.Contents {
+ for _, name := range deps {
// A systemserverclasspath_fragment must depend only on other source modules, while the
// prebuilt_systemserverclasspath_fragment_fragment must only depend on other prebuilt modules.
if !isSourceModule {
@@ -190,6 +237,7 @@
// Collect information for opening IDE project files in java/jdeps.go.
func (s *SystemServerClasspathModule) IDEInfo(dpInfo *android.IdeInfo) {
dpInfo.Deps = append(dpInfo.Deps, s.properties.Contents...)
+ dpInfo.Deps = append(dpInfo.Deps, s.properties.Standalone_contents...)
dpInfo.Paths = append(dpInfo.Paths, s.modulePaths...)
}
@@ -217,14 +265,22 @@
type systemServerClasspathFragmentSdkMemberProperties struct {
android.SdkMemberPropertiesBase
- // Contents of the systemserverclasspath fragment
+ // List of system_server classpath jars, could be either java_library, or java_sdk_library.
+ //
+ // The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH.
Contents []string
+
+ // List of jars that system_server loads dynamically using separate classloaders.
+ //
+ // The order does not matter.
+ Standalone_contents []string
}
func (s *systemServerClasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
module := variant.(*SystemServerClasspathModule)
s.Contents = module.properties.Contents
+ s.Standalone_contents = module.properties.Standalone_contents
}
func (s *systemServerClasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -234,6 +290,10 @@
if len(s.Contents) > 0 {
propertySet.AddPropertyWithTag("contents", s.Contents, requiredMemberDependency)
}
+
+ if len(s.Standalone_contents) > 0 {
+ propertySet.AddPropertyWithTag("standalone_contents", s.Standalone_contents, requiredMemberDependency)
+ }
}
var _ android.SdkMemberType = (*systemServerClasspathFragmentMemberType)(nil)
diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go
index 9ad50dd..ba328e7 100644
--- a/java/systemserver_classpath_fragment_test.go
+++ b/java/systemserver_classpath_fragment_test.go
@@ -99,7 +99,7 @@
func TestSystemServerClasspathFragmentWithoutContents(t *testing.T) {
prepareForTestWithSystemServerClasspath.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
- `\Qempty contents are not allowed\E`)).
+ `\QEither contents or standalone_contents needs to be non-empty\E`)).
RunTestWithBp(t, `
systemserverclasspath_fragment {
name: "systemserverclasspath-fragment",
diff --git a/java/testing.go b/java/testing.go
index 99d55a0..6c49bc8 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -159,8 +159,7 @@
`, strings.Join(android.SortedStringKeys(release2Modules), `", "`))
for release, modules := range release2Modules {
- libs := append([]string{"android", "core-for-system-modules"}, modules...)
- mockFS.Merge(prebuiltApisFilesForLibs([]string{release}, libs))
+ mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
}
return android.GroupFixturePreparers(
android.FixtureAddTextFile(path, bp),
@@ -168,19 +167,32 @@
)
}
-func prebuiltApisFilesForLibs(apiLevels []string, sdkLibs []string) map[string][]byte {
+func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[string][]byte {
+ libs := append([]string{"android"}, modules...)
+
fs := make(map[string][]byte)
for _, level := range apiLevels {
- for _, lib := range sdkLibs {
- for _, scope := range []string{"public", "system", "module-lib", "system-server", "test"} {
- fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, scope, lib)] = nil
+ apiLevel := android.ApiLevelForTest(level)
+ for _, sdkKind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkModule, android.SdkSystemServer, android.SdkTest} {
+ // A core-for-system-modules file must only be created for the sdk kind that supports it.
+ if sdkKind == systemModuleKind(sdkKind, apiLevel) {
+ fs[fmt.Sprintf("prebuilts/sdk/%s/%s/core-for-system-modules.jar", level, sdkKind)] = nil
+ }
+
+ for _, lib := range libs {
+ // Create a jar file for every library.
+ fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, sdkKind, lib)] = nil
+
// No finalized API files for "current"
if level != "current" {
- fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, scope, lib)] = nil
- fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, scope, lib)] = nil
+ fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, sdkKind, lib)] = nil
+ fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, sdkKind, lib)] = nil
}
}
}
+ if level == "current" {
+ fs["prebuilts/sdk/current/core/android.jar"] = nil
+ }
fs[fmt.Sprintf("prebuilts/sdk/%s/public/framework.aidl", level)] = nil
}
return fs
@@ -332,7 +344,7 @@
}`
systemModules := []string{
- "core-current-stubs-system-modules",
+ "core-public-stubs-system-modules",
"core-module-lib-stubs-system-modules",
"legacy-core-platform-api-stubs-system-modules",
"stable-core-platform-api-stubs-system-modules",
@@ -494,3 +506,19 @@
}
}
}
+
+// Applies the given modifier on the boot image config with the given name.
+func FixtureModifyBootImageConfig(name string, configModifier func(*bootImageConfig)) android.FixturePreparer {
+ return android.FixtureModifyConfig(func(androidConfig android.Config) {
+ pathCtx := android.PathContextForTesting(androidConfig)
+ config := genBootImageConfigRaw(pathCtx)
+ configModifier(config[name])
+ })
+}
+
+// Sets the value of `installDirOnDevice` 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
+ })
+}
diff --git a/licenses/Android.bp b/licenses/Android.bp
index a983b5b..8db001f 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -18,6 +18,11 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+filegroup {
+ name: "Apache-2.0-License-Text",
+ srcs: ["LICENSE"],
+}
+
license {
name: "Android-Apache-2.0",
package_name: "Android",
@@ -492,36 +497,36 @@
license_kind {
name: "SPDX-license-identifier-CC-BY-ND",
- conditions: ["restricted"],
+ conditions: ["by_exception_only"],
}
license_kind {
name: "SPDX-license-identifier-CC-BY-ND-1.0",
- conditions: ["restricted"],
+ conditions: ["by_exception_only"],
url: "https://spdx.org/licenses/CC-BY-ND-1.0.html",
}
license_kind {
name: "SPDX-license-identifier-CC-BY-ND-2.0",
- conditions: ["restricted"],
+ conditions: ["by_exception_only"],
url: "https://spdx.org/licenses/CC-BY-ND-2.0.html",
}
license_kind {
name: "SPDX-license-identifier-CC-BY-ND-2.5",
- conditions: ["restricted"],
+ conditions: ["by_exception_only"],
url: "https://spdx.org/licenses/CC-BY-ND-2.5.html",
}
license_kind {
name: "SPDX-license-identifier-CC-BY-ND-3.0",
- conditions: ["restricted"],
+ conditions: ["by_exception_only"],
url: "https://spdx.org/licenses/CC-BY-ND-3.0.html",
}
license_kind {
name: "SPDX-license-identifier-CC-BY-ND-4.0",
- conditions: ["restricted"],
+ conditions: ["by_exception_only"],
url: "https://spdx.org/licenses/CC-BY-ND-4.0.html",
}
@@ -562,7 +567,10 @@
license_kind {
name: "SPDX-license-identifier-CC-BY-SA-ND",
- conditions: ["restricted"],
+ conditions: [
+ "restricted",
+ "by_exception_only",
+ ],
}
license_kind {
diff --git a/licenses/LICENSE b/licenses/LICENSE
index dae0406..d645695 100644
--- a/licenses/LICENSE
+++ b/licenses/LICENSE
@@ -1,16 +1,4 @@
- Copyright (c) The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 8d0ad7c..dbc112e 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -155,7 +155,7 @@
OutputFile: android.OptionalPathForPath(l.outputFilePath),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base())
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable)
entries.SetString("LINKER_CONFIG_PATH_"+l.Name(), l.OutputFile().String())
diff --git a/linkerconfig/proto/linker_config.proto b/linkerconfig/proto/linker_config.proto
index fec66c8..dccf311 100644
--- a/linkerconfig/proto/linker_config.proto
+++ b/linkerconfig/proto/linker_config.proto
@@ -34,4 +34,16 @@
// Required libs from the module
repeated string requireLibs = 4;
+
+ message Contribution {
+ // Target namespace where this module contributes the search paths.
+ string namespace = 1;
+ // Search paths (inc. permitted paths) that this module contributes.
+ // Paths should be related to the current module and can use "${LIB}" variable which is
+ // expanded to "lib" or "lib64".
+ // e.g. ${LIB}/subdir
+ repeated string paths = 2;
+ }
+ // APEX can contribute search paths to specified namespaces.
+ repeated Contribution contributions = 5;
}
diff --git a/mk2rbc/Android.bp b/mk2rbc/Android.bp
index b18bfc7..4fa3eb6 100644
--- a/mk2rbc/Android.bp
+++ b/mk2rbc/Android.bp
@@ -38,7 +38,6 @@
"soong_variables.go",
"types.go",
"variable.go",
- "version_defaults.go",
],
deps: ["androidmk-parser"],
}
diff --git a/mk2rbc/cmd/mk2rbc.go b/mk2rbc/cmd/mk2rbc.go
index d8e7018..e84eacd 100644
--- a/mk2rbc/cmd/mk2rbc.go
+++ b/mk2rbc/cmd/mk2rbc.go
@@ -40,27 +40,27 @@
)
var (
- rootDir = flag.String("root", ".", "the value of // for load paths")
// TODO(asmundak): remove this option once there is a consensus on suffix
suffix = flag.String("suffix", ".rbc", "generated files' suffix")
dryRun = flag.Bool("dry_run", false, "dry run")
recurse = flag.Bool("convert_dependents", false, "convert all dependent files")
mode = flag.String("mode", "", `"backup" to back up existing files, "write" to overwrite them`)
- warn = flag.Bool("warnings", false, "warn about partially failed conversions")
- verbose = flag.Bool("v", false, "print summary")
errstat = flag.Bool("error_stat", false, "print error statistics")
traceVar = flag.String("trace", "", "comma-separated list of variables to trace")
// TODO(asmundak): this option is for debugging
allInSource = flag.Bool("all", false, "convert all product config makefiles in the tree under //")
outputTop = flag.String("outdir", "", "write output files into this directory hierarchy")
- launcher = flag.String("launcher", "", "generated launcher path. If set, the non-flag argument is _product_name_")
+ launcher = flag.String("launcher", "", "generated launcher path.")
+ boardlauncher = flag.String("boardlauncher", "", "generated board configuration launcher path.")
printProductConfigMap = flag.Bool("print_product_config_map", false, "print product config map and exit")
cpuProfile = flag.String("cpu_profile", "", "write cpu profile to file")
traceCalls = flag.Bool("trace_calls", false, "trace function calls")
+ inputVariables = flag.String("input_variables", "", "starlark file containing product config and global variables")
+ makefileList = flag.String("makefile_list", "", "path to a list of all makefiles in the source tree, generated by soong's finder. If not provided, mk2rbc will find the makefiles itself (more slowly than if this flag was provided)")
)
func init() {
- // Poor man's flag aliasing: works, but the usage string is ugly and
+ // Simplistic flag aliasing: works, but the usage string is ugly and
// both flag and its alias can be present on the command line
flagAlias := func(target string, alias string) {
if f := flag.Lookup(target); f != nil {
@@ -70,29 +70,29 @@
quit("cannot alias unknown flag " + target)
}
flagAlias("suffix", "s")
- flagAlias("root", "d")
flagAlias("dry_run", "n")
flagAlias("convert_dependents", "r")
- flagAlias("warnings", "w")
flagAlias("error_stat", "e")
}
var backupSuffix string
var tracedVariables []string
-var errorLogger = errorsByType{data: make(map[string]datum)}
-var makefileFinder = &LinuxMakefileFinder{}
-var versionDefaultsMk = filepath.Join("build", "make", "core", "version_defaults.mk")
+var errorLogger = errorSink{data: make(map[string]datum)}
+var makefileFinder mk2rbc.MakefileFinder
func main() {
flag.Usage = func() {
cmd := filepath.Base(os.Args[0])
fmt.Fprintf(flag.CommandLine.Output(),
- "Usage: %[1]s flags file...\n"+
- "or: %[1]s flags --launcher=PATH PRODUCT\n", cmd)
+ "Usage: %[1]s flags file...\n", cmd)
flag.PrintDefaults()
}
flag.Parse()
+ if _, err := os.Stat("build/soong/mk2rbc"); err != nil {
+ quit("Must be run from the root of the android tree. (build/soong/mk2rbc does not exist)")
+ }
+
// Delouse
if *suffix == ".mk" {
quit("cannot use .mk as generated file suffix")
@@ -136,6 +136,16 @@
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
+
+ if *makefileList != "" {
+ makefileFinder = &FileListMakefileFinder{
+ cachedMakefiles: nil,
+ filePath: *makefileList,
+ }
+ } else {
+ makefileFinder = &FindCommandMakefileFinder{}
+ }
+
// Find out global variables
getConfigVariables()
getSoongVariables()
@@ -170,43 +180,47 @@
if len(files) != 1 {
quit(fmt.Errorf("a launcher can be generated only for a single product"))
}
- versionDefaults, err := generateVersionDefaults()
- if err != nil {
- quit(err)
+ if *inputVariables == "" {
+ quit(fmt.Errorf("the product launcher requires an input variables file"))
}
- versionDefaultsPath := outputFilePath(versionDefaultsMk)
- err = writeGenerated(versionDefaultsPath, versionDefaults)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s:%s", files[0], err)
- ok = false
+ if !convertOne(*inputVariables) {
+ quit(fmt.Errorf("the product launcher input variables file failed to convert"))
}
- err = writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(files[0]), versionDefaultsPath,
+ err := writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(files[0]), outputFilePath(*inputVariables),
mk2rbc.MakePath2ModuleName(files[0])))
if err != nil {
- fmt.Fprintf(os.Stderr, "%s:%s", files[0], err)
+ fmt.Fprintf(os.Stderr, "%s: %s", files[0], err)
+ ok = false
+ }
+ }
+ if *boardlauncher != "" {
+ if len(files) != 1 {
+ quit(fmt.Errorf("a launcher can be generated only for a single product"))
+ }
+ if *inputVariables == "" {
+ quit(fmt.Errorf("the board launcher requires an input variables file"))
+ }
+ if !convertOne(*inputVariables) {
+ quit(fmt.Errorf("the board launcher input variables file failed to convert"))
+ }
+ err := writeGenerated(*boardlauncher, mk2rbc.BoardLauncher(
+ outputFilePath(files[0]), outputFilePath(*inputVariables)))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: %s", files[0], err)
ok = false
}
}
- printStats()
if *errstat {
errorLogger.printStatistics()
+ printStats()
}
if !ok {
os.Exit(1)
}
}
-func generateVersionDefaults() (string, error) {
- versionSettings, err := mk2rbc.ParseVersionDefaults(filepath.Join(*rootDir, versionDefaultsMk))
- if err != nil {
- return "", err
- }
- return mk2rbc.VersionDefaults(versionSettings), nil
-
-}
-
func quit(s interface{}) {
fmt.Fprintln(os.Stderr, s)
os.Exit(2)
@@ -216,17 +230,16 @@
const androidProductsMk = "AndroidProducts.mk"
// Build the list of AndroidProducts.mk files: it's
// build/make/target/product/AndroidProducts.mk + device/**/AndroidProducts.mk plus + vendor/**/AndroidProducts.mk
- targetAndroidProductsFile := filepath.Join(*rootDir, "build", "make", "target", "product", androidProductsMk)
+ targetAndroidProductsFile := filepath.Join("build", "make", "target", "product", androidProductsMk)
if _, err := os.Stat(targetAndroidProductsFile); err != nil {
- fmt.Fprintf(os.Stderr, "%s: %s\n(hint: %s is not a source tree root)\n",
- targetAndroidProductsFile, err, *rootDir)
+ fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
}
productConfigMap := make(map[string]string)
if err := mk2rbc.UpdateProductConfigMap(productConfigMap, targetAndroidProductsFile); err != nil {
fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
}
for _, t := range []string{"device", "vendor"} {
- _ = filepath.WalkDir(filepath.Join(*rootDir, t),
+ _ = filepath.WalkDir(t,
func(path string, d os.DirEntry, err error) error {
if err != nil || d.IsDir() || filepath.Base(path) != androidProductsMk {
return nil
@@ -242,10 +255,9 @@
}
func getConfigVariables() {
- path := filepath.Join(*rootDir, "build", "make", "core", "product.mk")
+ path := filepath.Join("build", "make", "core", "product.mk")
if err := mk2rbc.FindConfigVariables(path, mk2rbc.KnownVariables); err != nil {
- quit(fmt.Errorf("%s\n(check --root[=%s], it should point to the source root)",
- err, *rootDir))
+ quit(err)
}
}
@@ -258,11 +270,11 @@
if name != "BUILD_SYSTEM" {
return fmt.Sprintf("$(%s)", name)
}
- return filepath.Join(*rootDir, "build", "make", "core")
+ return filepath.Join("build", "make", "core")
}
func getSoongVariables() {
- path := filepath.Join(*rootDir, "build", "make", "core", "soong_config.mk")
+ path := filepath.Join("build", "make", "core", "soong_config.mk")
err := mk2rbc.FindSoongVariables(path, fileNameScope{}, mk2rbc.KnownVariables)
if err != nil {
quit(err)
@@ -311,19 +323,15 @@
}()
mk2starRequest := mk2rbc.Request{
- MkFile: mkFile,
- Reader: nil,
- RootDir: *rootDir,
- OutputDir: *outputTop,
- OutputSuffix: *suffix,
- TracedVariables: tracedVariables,
- TraceCalls: *traceCalls,
- WarnPartialSuccess: *warn,
- SourceFS: os.DirFS(*rootDir),
- MakefileFinder: makefileFinder,
- }
- if *errstat {
- mk2starRequest.ErrorLogger = errorLogger
+ MkFile: mkFile,
+ Reader: nil,
+ OutputDir: *outputTop,
+ OutputSuffix: *suffix,
+ TracedVariables: tracedVariables,
+ TraceCalls: *traceCalls,
+ SourceFS: os.DirFS("."),
+ MakefileFinder: makefileFinder,
+ ErrorLogger: errorLogger,
}
ss, err := mk2rbc.Convert(mk2starRequest)
if err != nil {
@@ -401,9 +409,6 @@
func printStats() {
var sortedFiles []string
- if !*warn && !*verbose {
- return
- }
for p := range converted {
sortedFiles = append(sortedFiles, p)
}
@@ -419,29 +424,22 @@
nOk++
}
}
- if *warn {
- if nPartial > 0 {
- fmt.Fprintf(os.Stderr, "Conversion was partially successful for:\n")
- for _, f := range sortedFiles {
- if ss := converted[f]; ss != nil && ss.HasErrors() {
- fmt.Fprintln(os.Stderr, " ", f)
- }
- }
- }
-
- if nFailed > 0 {
- fmt.Fprintf(os.Stderr, "Conversion failed for files:\n")
- for _, f := range sortedFiles {
- if converted[f] == nil {
- fmt.Fprintln(os.Stderr, " ", f)
- }
+ if nPartial > 0 {
+ fmt.Fprintf(os.Stderr, "Conversion was partially successful for:\n")
+ for _, f := range sortedFiles {
+ if ss := converted[f]; ss != nil && ss.HasErrors() {
+ fmt.Fprintln(os.Stderr, " ", f)
}
}
}
- if *verbose {
- fmt.Fprintf(os.Stderr, "%-16s%5d\n", "Succeeded:", nOk)
- fmt.Fprintf(os.Stderr, "%-16s%5d\n", "Partial:", nPartial)
- fmt.Fprintf(os.Stderr, "%-16s%5d\n", "Failed:", nFailed)
+
+ if nFailed > 0 {
+ fmt.Fprintf(os.Stderr, "Conversion failed for files:\n")
+ for _, f := range sortedFiles {
+ if converted[f] == nil {
+ fmt.Fprintln(os.Stderr, " ", f)
+ }
+ }
}
}
@@ -450,11 +448,18 @@
formattingArgs []string
}
-type errorsByType struct {
+type errorSink struct {
data map[string]datum
}
-func (ebt errorsByType) NewError(message string, node parser.Node, args ...interface{}) {
+func (ebt errorSink) NewError(el mk2rbc.ErrorLocation, node parser.Node, message string, args ...interface{}) {
+ fmt.Fprint(os.Stderr, el, ": ")
+ fmt.Fprintf(os.Stderr, message, args...)
+ fmt.Fprintln(os.Stderr)
+ if !*errstat {
+ return
+ }
+
v, exists := ebt.data[message]
if exists {
v.count++
@@ -479,7 +484,7 @@
ebt.data[message] = v
}
-func (ebt errorsByType) printStatistics() {
+func (ebt errorSink) printStatistics() {
if len(ebt.data) > 0 {
fmt.Fprintln(os.Stderr, "Error counts:")
}
@@ -524,17 +529,17 @@
return res, len(sorted)
}
-type LinuxMakefileFinder struct {
+// FindCommandMakefileFinder is an implementation of mk2rbc.MakefileFinder that
+// runs the unix find command to find all the makefiles in the source tree.
+type FindCommandMakefileFinder struct {
cachedRoot string
cachedMakefiles []string
}
-func (l *LinuxMakefileFinder) Find(root string) []string {
+func (l *FindCommandMakefileFinder) Find(root string) []string {
if l.cachedMakefiles != nil && l.cachedRoot == root {
return l.cachedMakefiles
}
- l.cachedRoot = root
- l.cachedMakefiles = make([]string, 0)
// Return all *.mk files but not in hidden directories.
@@ -553,9 +558,60 @@
panic(fmt.Errorf("cannot get the output from %s: %s", cmd, err))
}
scanner := bufio.NewScanner(stdout)
+ result := make([]string, 0)
for scanner.Scan() {
- l.cachedMakefiles = append(l.cachedMakefiles, strings.TrimPrefix(scanner.Text(), "./"))
+ result = append(result, strings.TrimPrefix(scanner.Text(), "./"))
}
stdout.Close()
+ err = scanner.Err()
+ if err != nil {
+ panic(fmt.Errorf("cannot get the output from %s: %s", cmd, err))
+ }
+ l.cachedRoot = root
+ l.cachedMakefiles = result
+ return l.cachedMakefiles
+}
+
+// FileListMakefileFinder is an implementation of mk2rbc.MakefileFinder that
+// reads a file containing the list of makefiles in the android source tree.
+// This file is generated by soong's finder, so that it can be computed while
+// soong is already walking the source tree looking for other files. If the root
+// to find makefiles under is not the root of the android source tree, it will
+// fall back to using FindCommandMakefileFinder.
+type FileListMakefileFinder struct {
+ FindCommandMakefileFinder
+ cachedMakefiles []string
+ filePath string
+}
+
+func (l *FileListMakefileFinder) Find(root string) []string {
+ root, err1 := filepath.Abs(root)
+ wd, err2 := os.Getwd()
+ if root != wd || err1 != nil || err2 != nil {
+ return l.FindCommandMakefileFinder.Find(root)
+ }
+ if l.cachedMakefiles != nil {
+ return l.cachedMakefiles
+ }
+
+ file, err := os.Open(l.filePath)
+ if err != nil {
+ panic(fmt.Errorf("Cannot read makefile list: %s\n", err))
+ }
+ defer file.Close()
+
+ result := make([]string, 0)
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if len(line) > 0 {
+ result = append(result, line)
+ }
+ }
+
+ if err = scanner.Err(); err != nil {
+ panic(fmt.Errorf("Cannot read makefile list: %s\n", err))
+ }
+ l.cachedMakefiles = result
return l.cachedMakefiles
}
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
index 0bb8b95..07f7ca1 100644
--- a/mk2rbc/expr.go
+++ b/mk2rbc/expr.go
@@ -16,23 +16,22 @@
import (
"fmt"
- "strconv"
"strings"
-
- mkparser "android/soong/androidmk/parser"
)
-// Represents an expression in the Starlark code. An expression has
-// a type, and it can be evaluated.
+// Represents an expression in the Starlark code. An expression has a type.
type starlarkExpr interface {
starlarkNode
typ() starlarkType
- // Try to substitute variable values. Return substitution result
- // and whether it is the same as the original expression.
- eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool)
// Emit the code to copy the expression, otherwise we will end up
// with source and target pointing to the same list.
emitListVarCopy(gctx *generationContext)
+ // Return the expression, calling the transformer func for
+ // every expression in the tree. If the transformer func returns non-nil,
+ // its result is used in place of the expression it was called with in the
+ // resulting expression. The resulting starlarkExpr will contain as many
+ // of the same objects from the original expression as possible.
+ transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr
}
func maybeString(expr starlarkExpr) (string, bool) {
@@ -46,12 +45,6 @@
literal string
}
-func (s *stringLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
- res = s
- same = true
- return
-}
-
func (s *stringLiteralExpr) emit(gctx *generationContext) {
gctx.writef("%q", s.literal)
}
@@ -64,17 +57,19 @@
s.emit(gctx)
}
+func (s *stringLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ if replacement := transformer(s); replacement != nil {
+ return replacement
+ } else {
+ return s
+ }
+}
+
// Integer literal
type intLiteralExpr struct {
literal int
}
-func (s *intLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
- res = s
- same = true
- return
-}
-
func (s *intLiteralExpr) emit(gctx *generationContext) {
gctx.writef("%d", s.literal)
}
@@ -87,6 +82,66 @@
s.emit(gctx)
}
+func (s *intLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ if replacement := transformer(s); replacement != nil {
+ return replacement
+ } else {
+ return s
+ }
+}
+
+// Boolean literal
+type boolLiteralExpr struct {
+ literal bool
+}
+
+func (b *boolLiteralExpr) emit(gctx *generationContext) {
+ if b.literal {
+ gctx.write("True")
+ } else {
+ gctx.write("False")
+ }
+}
+
+func (_ *boolLiteralExpr) typ() starlarkType {
+ return starlarkTypeBool
+}
+
+func (b *boolLiteralExpr) emitListVarCopy(gctx *generationContext) {
+ b.emit(gctx)
+}
+
+func (b *boolLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ if replacement := transformer(b); replacement != nil {
+ return replacement
+ } else {
+ return b
+ }
+}
+
+type globalsExpr struct {
+}
+
+func (g *globalsExpr) emit(gctx *generationContext) {
+ gctx.write("g")
+}
+
+func (g *globalsExpr) typ() starlarkType {
+ return starlarkTypeUnknown
+}
+
+func (g *globalsExpr) emitListVarCopy(gctx *generationContext) {
+ g.emit(gctx)
+}
+
+func (g *globalsExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ if replacement := transformer(g); replacement != nil {
+ return replacement
+ } else {
+ return g
+ }
+}
+
// interpolateExpr represents Starlark's interpolation operator <string> % list
// we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
// will have chunks = ["first", "second", "third"] and args = [X, Y]
@@ -95,6 +150,35 @@
args []starlarkExpr
}
+func NewInterpolateExpr(parts []starlarkExpr) starlarkExpr {
+ result := &interpolateExpr{}
+ needString := true
+ for _, part := range parts {
+ if needString {
+ if strLit, ok := part.(*stringLiteralExpr); ok {
+ result.chunks = append(result.chunks, strLit.literal)
+ } else {
+ result.chunks = append(result.chunks, "")
+ }
+ needString = false
+ } else {
+ if strLit, ok := part.(*stringLiteralExpr); ok {
+ result.chunks[len(result.chunks)-1] += strLit.literal
+ } else {
+ result.args = append(result.args, part)
+ needString = true
+ }
+ }
+ }
+ if len(result.chunks) == len(result.args) {
+ result.chunks = append(result.chunks, "")
+ }
+ if len(result.args) == 0 {
+ return &stringLiteralExpr{literal: strings.Join(result.chunks, "")}
+ }
+ return result
+}
+
func (xi *interpolateExpr) emit(gctx *generationContext) {
if len(xi.chunks) != len(xi.args)+1 {
panic(fmt.Errorf("malformed interpolateExpr: #chunks(%d) != #args(%d)+1",
@@ -106,7 +190,7 @@
format += "%s" + strings.ReplaceAll(chunk, "%", "%%")
}
gctx.writef("%q %% ", format)
- emitarg := func(arg starlarkExpr) {
+ emitArg := func(arg starlarkExpr) {
if arg.typ() == starlarkTypeList {
gctx.write(`" ".join(`)
arg.emit(gctx)
@@ -116,49 +200,18 @@
}
}
if len(xi.args) == 1 {
- emitarg(xi.args[0])
+ emitArg(xi.args[0])
} else {
sep := "("
for _, arg := range xi.args {
gctx.write(sep)
- emitarg(arg)
+ emitArg(arg)
sep = ", "
}
gctx.write(")")
}
}
-func (xi *interpolateExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
- same = true
- newChunks := []string{xi.chunks[0]}
- var newArgs []starlarkExpr
- for i, arg := range xi.args {
- newArg, sameArg := arg.eval(valueMap)
- same = same && sameArg
- switch x := newArg.(type) {
- case *stringLiteralExpr:
- newChunks[len(newChunks)-1] += x.literal + xi.chunks[i+1]
- same = false
- continue
- case *intLiteralExpr:
- newChunks[len(newChunks)-1] += strconv.Itoa(x.literal) + xi.chunks[i+1]
- same = false
- continue
- default:
- newChunks = append(newChunks, xi.chunks[i+1])
- newArgs = append(newArgs, newArg)
- }
- }
- if same {
- res = xi
- } else if len(newChunks) == 1 {
- res = &stringLiteralExpr{newChunks[0]}
- } else {
- res = &interpolateExpr{chunks: newChunks, args: newArgs}
- }
- return
-}
-
func (_ *interpolateExpr) typ() starlarkType {
return starlarkTypeString
}
@@ -167,19 +220,29 @@
xi.emit(gctx)
}
+func (xi *interpolateExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ argsCopy := make([]starlarkExpr, len(xi.args))
+ for i, arg := range xi.args {
+ argsCopy[i] = arg.transform(transformer)
+ }
+ xi.args = argsCopy
+ if replacement := transformer(xi); replacement != nil {
+ return replacement
+ } else {
+ return xi
+ }
+}
+
type variableRefExpr struct {
ref variable
isDefined bool
}
-func (v *variableRefExpr) eval(map[string]starlarkExpr) (res starlarkExpr, same bool) {
- predefined, ok := v.ref.(*predefinedVariable)
- if same = !ok; same {
- res = v
- } else {
- res = predefined.value
+func NewVariableRefExpr(ref variable, isDefined bool) starlarkExpr {
+ if predefined, ok := ref.(*predefinedVariable); ok {
+ return predefined.value
}
- return
+ return &variableRefExpr{ref, isDefined}
}
func (v *variableRefExpr) emit(gctx *generationContext) {
@@ -197,17 +260,61 @@
}
}
-type notExpr struct {
+func (v *variableRefExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ if replacement := transformer(v); replacement != nil {
+ return replacement
+ } else {
+ return v
+ }
+}
+
+type toStringExpr struct {
expr starlarkExpr
}
-func (n *notExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
- if x, same := n.expr.eval(valueMap); same {
- res = n
- } else {
- res = ¬Expr{expr: x}
+func (s *toStringExpr) emit(ctx *generationContext) {
+ switch s.expr.typ() {
+ case starlarkTypeString, starlarkTypeUnknown:
+ // Assume unknown types are strings already.
+ s.expr.emit(ctx)
+ case starlarkTypeList:
+ ctx.write(`" ".join(`)
+ s.expr.emit(ctx)
+ ctx.write(")")
+ case starlarkTypeInt:
+ ctx.write(`("%d" % (`)
+ s.expr.emit(ctx)
+ ctx.write("))")
+ case starlarkTypeBool:
+ ctx.write(`("true" if (`)
+ s.expr.emit(ctx)
+ ctx.write(`) else "")`)
+ case starlarkTypeVoid:
+ ctx.write(`""`)
+ default:
+ panic("Unknown starlark type!")
}
- return
+}
+
+func (s *toStringExpr) typ() starlarkType {
+ return starlarkTypeString
+}
+
+func (s *toStringExpr) emitListVarCopy(gctx *generationContext) {
+ s.emit(gctx)
+}
+
+func (s *toStringExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ s.expr = s.expr.transform(transformer)
+ if replacement := transformer(s); replacement != nil {
+ return replacement
+ } else {
+ return s
+ }
+}
+
+type notExpr struct {
+ expr starlarkExpr
}
func (n *notExpr) emit(ctx *generationContext) {
@@ -223,38 +330,26 @@
n.emit(gctx)
}
+func (n *notExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ n.expr = n.expr.transform(transformer)
+ if replacement := transformer(n); replacement != nil {
+ return replacement
+ } else {
+ return n
+ }
+}
+
type eqExpr struct {
left, right starlarkExpr
isEq bool // if false, it's !=
}
-func (eq *eqExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
- xLeft, sameLeft := eq.left.eval(valueMap)
- xRight, sameRight := eq.right.eval(valueMap)
- if same = sameLeft && sameRight; same {
- res = eq
- } else {
- res = &eqExpr{left: xLeft, right: xRight, isEq: eq.isEq}
- }
- return
-}
-
func (eq *eqExpr) emit(gctx *generationContext) {
- emitSimple := func(expr starlarkExpr) {
- if eq.isEq {
- gctx.write("not ")
- }
- expr.emit(gctx)
+ if eq.left.typ() != eq.right.typ() {
+ eq.left = &toStringExpr{expr: eq.left}
+ eq.right = &toStringExpr{expr: eq.right}
}
- // Are we checking that a variable is empty?
- if isEmptyString(eq.left) {
- emitSimple(eq.right)
- return
- } else if isEmptyString(eq.right) {
- emitSimple(eq.left)
- return
- }
// General case
eq.left.emit(gctx)
if eq.isEq {
@@ -273,54 +368,20 @@
eq.emit(gctx)
}
-// variableDefinedExpr corresponds to Make's ifdef VAR
-type variableDefinedExpr struct {
- v variable
-}
-
-func (v *variableDefinedExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
- res = v
- same = true
- return
-
-}
-
-func (v *variableDefinedExpr) emit(gctx *generationContext) {
- if v.v != nil {
- v.v.emitDefined(gctx)
- return
+func (eq *eqExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ eq.left = eq.left.transform(transformer)
+ eq.right = eq.right.transform(transformer)
+ if replacement := transformer(eq); replacement != nil {
+ return replacement
+ } else {
+ return eq
}
- gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
-}
-
-func (_ *variableDefinedExpr) typ() starlarkType {
- return starlarkTypeBool
-}
-
-func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
- v.emit(gctx)
}
type listExpr struct {
items []starlarkExpr
}
-func (l *listExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
- newItems := make([]starlarkExpr, len(l.items))
- same = true
- for i, item := range l.items {
- var sameItem bool
- newItems[i], sameItem = item.eval(valueMap)
- same = same && sameItem
- }
- if same {
- res = l
- } else {
- res = &listExpr{newItems}
- }
- return
-}
-
func (l *listExpr) emit(gctx *generationContext) {
if !gctx.inAssignment || len(l.items) < 2 {
gctx.write("[")
@@ -355,6 +416,19 @@
l.emit(gctx)
}
+func (l *listExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ itemsCopy := make([]starlarkExpr, len(l.items))
+ for i, item := range l.items {
+ itemsCopy[i] = item.transform(transformer)
+ }
+ l.items = itemsCopy
+ if replacement := transformer(l); replacement != nil {
+ return replacement
+ } else {
+ return l
+ }
+}
+
func newStringListExpr(items []string) *listExpr {
v := listExpr{}
for _, item := range items {
@@ -363,7 +437,7 @@
return &v
}
-// concatExpr generates epxr1 + expr2 + ... + exprN in Starlark.
+// concatExpr generates expr1 + expr2 + ... + exprN in Starlark.
type concatExpr struct {
items []starlarkExpr
}
@@ -394,22 +468,6 @@
gctx.indentLevel -= 2
}
-func (c *concatExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
- same = true
- xConcat := &concatExpr{items: make([]starlarkExpr, len(c.items))}
- for i, item := range c.items {
- var sameItem bool
- xConcat.items[i], sameItem = item.eval(valueMap)
- same = same && sameItem
- }
- if same {
- res = c
- } else {
- res = xConcat
- }
- return
-}
-
func (_ *concatExpr) typ() starlarkType {
return starlarkTypeList
}
@@ -418,6 +476,19 @@
c.emit(gctx)
}
+func (c *concatExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ itemsCopy := make([]starlarkExpr, len(c.items))
+ for i, item := range c.items {
+ itemsCopy[i] = item.transform(transformer)
+ }
+ c.items = itemsCopy
+ if replacement := transformer(c); replacement != nil {
+ return replacement
+ } else {
+ return c
+ }
+}
+
// inExpr generates <expr> [not] in <list>
type inExpr struct {
expr starlarkExpr
@@ -425,19 +496,6 @@
isNot bool
}
-func (i *inExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
- x := &inExpr{isNot: i.isNot}
- var sameExpr, sameList bool
- x.expr, sameExpr = i.expr.eval(valueMap)
- x.list, sameList = i.list.eval(valueMap)
- if same = sameExpr && sameList; same {
- res = i
- } else {
- res = x
- }
- return
-}
-
func (i *inExpr) emit(gctx *generationContext) {
i.expr.emit(gctx)
if i.isNot {
@@ -456,35 +514,44 @@
i.emit(gctx)
}
+func (i *inExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ i.expr = i.expr.transform(transformer)
+ i.list = i.list.transform(transformer)
+ if replacement := transformer(i); replacement != nil {
+ return replacement
+ } else {
+ return i
+ }
+}
+
type indexExpr struct {
array starlarkExpr
index starlarkExpr
}
-func (ix indexExpr) emit(gctx *generationContext) {
+func (ix *indexExpr) emit(gctx *generationContext) {
ix.array.emit(gctx)
gctx.write("[")
ix.index.emit(gctx)
gctx.write("]")
}
-func (ix indexExpr) typ() starlarkType {
+func (ix *indexExpr) typ() starlarkType {
return starlarkTypeString
}
-func (ix indexExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
- newArray, isSameArray := ix.array.eval(valueMap)
- newIndex, isSameIndex := ix.index.eval(valueMap)
- if same = isSameArray && isSameIndex; same {
- res = ix
- } else {
- res = &indexExpr{newArray, newIndex}
- }
- return
+func (ix *indexExpr) emitListVarCopy(gctx *generationContext) {
+ ix.emit(gctx)
}
-func (ix indexExpr) emitListVarCopy(gctx *generationContext) {
- ix.emit(gctx)
+func (ix *indexExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ ix.array = ix.array.transform(transformer)
+ ix.index = ix.index.transform(transformer)
+ if replacement := transformer(ix); replacement != nil {
+ return replacement
+ } else {
+ return ix
+ }
}
type callExpr struct {
@@ -494,51 +561,16 @@
returnType starlarkType
}
-func (cx *callExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
- newCallExpr := &callExpr{name: cx.name, args: make([]starlarkExpr, len(cx.args)),
- returnType: cx.returnType}
- if cx.object != nil {
- newCallExpr.object, same = cx.object.eval(valueMap)
- } else {
- same = true
- }
- for i, args := range cx.args {
- var s bool
- newCallExpr.args[i], s = args.eval(valueMap)
- same = same && s
- }
- if same {
- res = cx
- } else {
- res = newCallExpr
- }
- return
-}
-
func (cx *callExpr) emit(gctx *generationContext) {
- sep := ""
if cx.object != nil {
gctx.write("(")
cx.object.emit(gctx)
gctx.write(")")
gctx.write(".", cx.name, "(")
} else {
- kf, found := knownFunctions[cx.name]
- if !found {
- panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
- }
- if kf.runtimeName[0] == '!' {
- panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
- }
- gctx.write(kf.runtimeName, "(")
- if kf.hiddenArg == hiddenArgGlobal {
- gctx.write("g")
- sep = ", "
- } else if kf.hiddenArg == hiddenArgConfig {
- gctx.write("cfg")
- sep = ", "
- }
+ gctx.write(cx.name, "(")
}
+ sep := ""
for _, arg := range cx.args {
gctx.write(sep)
arg.emit(gctx)
@@ -555,29 +587,176 @@
cx.emit(gctx)
}
+func (cx *callExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ if cx.object != nil {
+ cx.object = cx.object.transform(transformer)
+ }
+ argsCopy := make([]starlarkExpr, len(cx.args))
+ for i, arg := range cx.args {
+ argsCopy[i] = arg.transform(transformer)
+ }
+ if replacement := transformer(cx); replacement != nil {
+ return replacement
+ } else {
+ return cx
+ }
+}
+
+type ifExpr struct {
+ condition starlarkExpr
+ ifTrue starlarkExpr
+ ifFalse starlarkExpr
+}
+
+func (i *ifExpr) emit(gctx *generationContext) {
+ gctx.write("(")
+ i.ifTrue.emit(gctx)
+ gctx.write(" if ")
+ i.condition.emit(gctx)
+ gctx.write(" else ")
+ i.ifFalse.emit(gctx)
+ gctx.write(")")
+}
+
+func (i *ifExpr) typ() starlarkType {
+ tType := i.ifTrue.typ()
+ fType := i.ifFalse.typ()
+ if tType != fType && tType != starlarkTypeUnknown && fType != starlarkTypeUnknown {
+ panic("Conflicting types in if expression")
+ }
+ if tType != starlarkTypeUnknown {
+ return tType
+ } else {
+ return fType
+ }
+}
+
+func (i *ifExpr) emitListVarCopy(gctx *generationContext) {
+ i.emit(gctx)
+}
+
+func (i *ifExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ i.condition = i.condition.transform(transformer)
+ i.ifTrue = i.ifTrue.transform(transformer)
+ i.ifFalse = i.ifFalse.transform(transformer)
+ if replacement := transformer(i); replacement != nil {
+ return replacement
+ } else {
+ return i
+ }
+}
+
+type identifierExpr struct {
+ name string
+}
+
+func (i *identifierExpr) emit(gctx *generationContext) {
+ gctx.write(i.name)
+}
+
+func (i *identifierExpr) typ() starlarkType {
+ return starlarkTypeUnknown
+}
+
+func (i *identifierExpr) emitListVarCopy(gctx *generationContext) {
+ i.emit(gctx)
+}
+
+func (i *identifierExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ if replacement := transformer(i); replacement != nil {
+ return replacement
+ } else {
+ return i
+ }
+}
+
+type foreachExpr struct {
+ varName string
+ list starlarkExpr
+ action starlarkExpr
+}
+
+func (f *foreachExpr) emit(gctx *generationContext) {
+ gctx.write("[")
+ f.action.emit(gctx)
+ gctx.write(" for " + f.varName + " in ")
+ f.list.emit(gctx)
+ gctx.write("]")
+}
+
+func (f *foreachExpr) typ() starlarkType {
+ return starlarkTypeList
+}
+
+func (f *foreachExpr) emitListVarCopy(gctx *generationContext) {
+ f.emit(gctx)
+}
+
+func (f *foreachExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ f.list = f.list.transform(transformer)
+ f.action = f.action.transform(transformer)
+ if replacement := transformer(f); replacement != nil {
+ return replacement
+ } else {
+ return f
+ }
+}
+
+type binaryOpExpr struct {
+ left, right starlarkExpr
+ op string
+ returnType starlarkType
+}
+
+func (b *binaryOpExpr) emit(gctx *generationContext) {
+ b.left.emit(gctx)
+ gctx.write(" " + b.op + " ")
+ b.right.emit(gctx)
+}
+
+func (b *binaryOpExpr) typ() starlarkType {
+ return b.returnType
+}
+
+func (b *binaryOpExpr) emitListVarCopy(gctx *generationContext) {
+ b.emit(gctx)
+}
+
+func (b *binaryOpExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ b.left = b.left.transform(transformer)
+ b.right = b.right.transform(transformer)
+ if replacement := transformer(b); replacement != nil {
+ return replacement
+ } else {
+ return b
+ }
+}
+
type badExpr struct {
- node mkparser.Node
- message string
+ errorLocation ErrorLocation
+ message string
}
-func (b *badExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
- res = b
- same = true
- return
-}
-
-func (b *badExpr) emit(_ *generationContext) {
- panic("implement me")
+func (b *badExpr) emit(gctx *generationContext) {
+ gctx.emitConversionError(b.errorLocation, b.message)
}
func (_ *badExpr) typ() starlarkType {
return starlarkTypeUnknown
}
-func (b *badExpr) emitListVarCopy(gctx *generationContext) {
+func (_ *badExpr) emitListVarCopy(_ *generationContext) {
panic("implement me")
}
+func (b *badExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+ if replacement := transformer(b); replacement != nil {
+ return replacement
+ } else {
+ return b
+ }
+}
+
func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
if xString, ok := expr.(*stringLiteralExpr); ok {
return newStringListExpr(strings.Fields(xString.literal))
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 3ae8ab9..cb50a50 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -40,15 +40,19 @@
)
const (
- baseUri = "//build/make/core:product_config.rbc"
+ annotationCommentPrefix = "RBC#"
+ baseUri = "//build/make/core:product_config.rbc"
// The name of the struct exported by the product_config.rbc
// that contains the functions and variables available to
// product configuration Starlark files.
baseName = "rblf"
+ soongNsPrefix = "SOONG_CONFIG_"
+
// And here are the functions and variables:
cfnGetCfg = baseName + ".cfg"
cfnMain = baseName + ".product_configuration"
+ cfnBoardMain = baseName + ".board_configuration"
cfnPrintVars = baseName + ".printvars"
cfnWarning = baseName + ".warning"
cfnLocalAppend = baseName + ".local_append"
@@ -58,105 +62,108 @@
)
const (
- // Phony makefile functions, they are eventually rewritten
- // according to knownFunctions map
- addSoongNamespace = "add_soong_config_namespace"
- addSoongConfigVarValue = "add_soong_config_var_value"
- fileExistsPhony = "$file_exists"
- wildcardExistsPhony = "$wildcard_exists"
+ soongConfigAppend = "soong_config_append"
+ soongConfigAssign = "soong_config_set"
)
-const (
- callLoadAlways = "inherit-product"
- callLoadIf = "inherit-product-if-exists"
-)
-
-var knownFunctions = map[string]struct {
- // The name of the runtime function this function call in makefiles maps to.
- // If it starts with !, then this makefile function call is rewritten to
- // something else.
- runtimeName string
- returnType starlarkType
- hiddenArg hiddenArgType
+var knownFunctions = map[string]interface {
+ parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr
}{
- "abspath": {baseName + ".abspath", starlarkTypeString, hiddenArgNone},
- fileExistsPhony: {baseName + ".file_exists", starlarkTypeBool, hiddenArgNone},
- wildcardExistsPhony: {baseName + ".file_wildcard_exists", starlarkTypeBool, hiddenArgNone},
- addSoongNamespace: {baseName + ".add_soong_config_namespace", starlarkTypeVoid, hiddenArgGlobal},
- addSoongConfigVarValue: {baseName + ".add_soong_config_var_value", starlarkTypeVoid, hiddenArgGlobal},
- "add-to-product-copy-files-if-exists": {baseName + ".copy_if_exists", starlarkTypeList, hiddenArgNone},
- "addprefix": {baseName + ".addprefix", starlarkTypeList, hiddenArgNone},
- "addsuffix": {baseName + ".addsuffix", starlarkTypeList, hiddenArgNone},
- "copy-files": {baseName + ".copy_files", starlarkTypeList, hiddenArgNone},
- "dir": {baseName + ".dir", starlarkTypeList, hiddenArgNone},
- "enforce-product-packages-exist": {baseName + ".enforce_product_packages_exist", starlarkTypeVoid, hiddenArgNone},
- "error": {baseName + ".mkerror", starlarkTypeVoid, hiddenArgNone},
- "findstring": {"!findstring", starlarkTypeInt, hiddenArgNone},
- "find-copy-subdir-files": {baseName + ".find_and_copy", starlarkTypeList, hiddenArgNone},
- "find-word-in-list": {"!find-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
- "filter": {baseName + ".filter", starlarkTypeList, hiddenArgNone},
- "filter-out": {baseName + ".filter_out", starlarkTypeList, hiddenArgNone},
- "firstword": {"!firstword", starlarkTypeString, hiddenArgNone},
- "get-vendor-board-platforms": {"!get-vendor-board-platforms", starlarkTypeList, hiddenArgNone}, // internal macro, used by is-board-platform, etc.
- "info": {baseName + ".mkinfo", starlarkTypeVoid, hiddenArgNone},
- "is-android-codename": {"!is-android-codename", starlarkTypeBool, hiddenArgNone}, // unused by product config
- "is-android-codename-in-list": {"!is-android-codename-in-list", starlarkTypeBool, hiddenArgNone}, // unused by product config
- "is-board-platform": {"!is-board-platform", starlarkTypeBool, hiddenArgNone},
- "is-board-platform-in-list": {"!is-board-platform-in-list", starlarkTypeBool, hiddenArgNone},
- "is-chipset-in-board-platform": {"!is-chipset-in-board-platform", starlarkTypeUnknown, hiddenArgNone}, // unused by product config
- "is-chipset-prefix-in-board-platform": {"!is-chipset-prefix-in-board-platform", starlarkTypeBool, hiddenArgNone}, // unused by product config
- "is-not-board-platform": {"!is-not-board-platform", starlarkTypeBool, hiddenArgNone}, // defined but never used
- "is-platform-sdk-version-at-least": {"!is-platform-sdk-version-at-least", starlarkTypeBool, hiddenArgNone}, // unused by product config
- "is-product-in-list": {"!is-product-in-list", starlarkTypeBool, hiddenArgNone},
- "is-vendor-board-platform": {"!is-vendor-board-platform", starlarkTypeBool, hiddenArgNone},
- callLoadAlways: {"!inherit-product", starlarkTypeVoid, hiddenArgNone},
- callLoadIf: {"!inherit-product-if-exists", starlarkTypeVoid, hiddenArgNone},
- "lastword": {"!lastword", starlarkTypeString, hiddenArgNone},
- "match-prefix": {"!match-prefix", starlarkTypeUnknown, hiddenArgNone}, // internal macro
- "match-word": {"!match-word", starlarkTypeUnknown, hiddenArgNone}, // internal macro
- "match-word-in-list": {"!match-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
- "notdir": {baseName + ".notdir", starlarkTypeString, hiddenArgNone},
- "my-dir": {"!my-dir", starlarkTypeString, hiddenArgNone},
- "patsubst": {baseName + ".mkpatsubst", starlarkTypeString, hiddenArgNone},
- "product-copy-files-by-pattern": {baseName + ".product_copy_files_by_pattern", starlarkTypeList, hiddenArgNone},
- "require-artifacts-in-path": {baseName + ".require_artifacts_in_path", starlarkTypeVoid, hiddenArgNone},
- "require-artifacts-in-path-relaxed": {baseName + ".require_artifacts_in_path_relaxed", starlarkTypeVoid, hiddenArgNone},
+ "abspath": &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString, addGlobals: false},
+ "add_soong_config_namespace": &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
+ "add_soong_config_var_value": &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+ soongConfigAssign: &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+ soongConfigAppend: &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
+ "soong_config_get": &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
+ "add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList, addGlobals: false},
+ "addprefix": &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList, addGlobals: false},
+ "addsuffix": &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList, addGlobals: false},
+ "copy-files": &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList, addGlobals: false},
+ "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeList, addGlobals: false},
+ "dist-for-goals": &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
+ "enforce-product-packages-exist": &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addGlobals: false},
+ "error": &makeControlFuncParser{name: baseName + ".mkerror"},
+ "findstring": &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt, addGlobals: false},
+ "find-copy-subdir-files": &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList, addGlobals: false},
+ "filter": &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList, addGlobals: false},
+ "filter-out": &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList, addGlobals: false},
+ "firstword": &firstOrLastwordCallParser{isLastWord: false},
+ "foreach": &foreachCallPaser{},
+ "if": &ifCallParser{},
+ "info": &makeControlFuncParser{name: baseName + ".mkinfo"},
+ "is-board-platform": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+ "is-board-platform2": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+ "is-board-platform-in-list": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+ "is-board-platform-in-list2": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+ "is-product-in-list": &isProductInListCallParser{},
+ "is-vendor-board-platform": &isVendorBoardPlatformCallParser{},
+ "is-vendor-board-qcom": &isVendorBoardQcomCallParser{},
+ "lastword": &firstOrLastwordCallParser{isLastWord: true},
+ "notdir": &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString, addGlobals: false},
+ "math_max": &mathMaxOrMinCallParser{function: "max"},
+ "math_min": &mathMaxOrMinCallParser{function: "min"},
+ "math_gt_or_eq": &mathComparisonCallParser{op: ">="},
+ "math_gt": &mathComparisonCallParser{op: ">"},
+ "math_lt": &mathComparisonCallParser{op: "<"},
+ "my-dir": &myDirCallParser{},
+ "patsubst": &substCallParser{fname: "patsubst"},
+ "product-copy-files-by-pattern": &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList, addGlobals: false},
+ "require-artifacts-in-path": &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addGlobals: false},
+ "require-artifacts-in-path-relaxed": &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addGlobals: false},
// TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
- "shell": {baseName + ".shell", starlarkTypeString, hiddenArgNone},
- "strip": {baseName + ".mkstrip", starlarkTypeString, hiddenArgNone},
- "tb-modules": {"!tb-modules", starlarkTypeUnknown, hiddenArgNone}, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
- "subst": {baseName + ".mksubst", starlarkTypeString, hiddenArgNone},
- "warning": {baseName + ".mkwarning", starlarkTypeVoid, hiddenArgNone},
- "word": {baseName + "!word", starlarkTypeString, hiddenArgNone},
- "wildcard": {baseName + ".expand_wildcard", starlarkTypeList, hiddenArgNone},
+ "shell": &shellCallParser{},
+ "strip": &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString, addGlobals: false},
+ "subst": &substCallParser{fname: "subst"},
+ "warning": &makeControlFuncParser{name: baseName + ".mkwarning"},
+ "word": &wordCallParser{},
+ "wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList, addGlobals: false},
}
-var builtinFuncRex = regexp.MustCompile(
- "^(addprefix|addsuffix|abspath|and|basename|call|dir|error|eval" +
- "|flavor|foreach|file|filter|filter-out|findstring|firstword|guile" +
- "|if|info|join|lastword|notdir|or|origin|patsubst|realpath" +
- "|shell|sort|strip|subst|suffix|value|warning|word|wordlist|words" +
- "|wildcard)")
+// 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{
+ "find-word-in-list": true, // internal macro
+ "get-vendor-board-platforms": true, // internal macro, used by is-board-platform, etc.
+ "is-android-codename": true, // unused by product config
+ "is-android-codename-in-list": true, // unused by product config
+ "is-chipset-in-board-platform": true, // unused by product config
+ "is-chipset-prefix-in-board-platform": true, // unused by product config
+ "is-not-board-platform": true, // defined but never used
+ "is-platform-sdk-version-at-least": true, // unused by product config
+ "match-prefix": true, // internal macro
+ "match-word": true, // internal macro
+ "match-word-in-list": true, // internal macro
+ "tb-modules": true, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
+}
+
+var identifierFullMatchRegex = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
// Conversion request parameters
type Request struct {
- MkFile string // file to convert
- Reader io.Reader // if set, read input from this stream instead
- RootDir string // root directory path used to resolve included files
- OutputSuffix string // generated Starlark files suffix
- OutputDir string // if set, root of the output hierarchy
- ErrorLogger ErrorMonitorCB
- TracedVariables []string // trace assignment to these variables
- TraceCalls bool
- WarnPartialSuccess bool
- SourceFS fs.FS
- MakefileFinder MakefileFinder
+ MkFile string // file to convert
+ Reader io.Reader // if set, read input from this stream instead
+ OutputSuffix string // generated Starlark files suffix
+ OutputDir string // if set, root of the output hierarchy
+ ErrorLogger ErrorLogger
+ TracedVariables []string // trace assignment to these variables
+ TraceCalls bool
+ SourceFS fs.FS
+ MakefileFinder MakefileFinder
}
-// An error sink allowing to gather error statistics.
-// NewError is called on every error encountered during processing.
-type ErrorMonitorCB interface {
- NewError(s string, node mkparser.Node, args ...interface{})
+// ErrorLogger prints errors and gathers error statistics.
+// Its NewError function is called on every error encountered during the conversion.
+type ErrorLogger interface {
+ NewError(el ErrorLocation, node mkparser.Node, text string, args ...interface{})
+}
+
+type ErrorLocation struct {
+ MkFile string
+ MkLine int
+}
+
+func (el ErrorLocation) String() string {
+ return fmt.Sprintf("%s:%d", el.MkFile, el.MkLine)
}
// Derives module name for a given file. It is base name
@@ -232,10 +239,6 @@
node.emit(gctx)
}
- if ss.hasErrors && ss.warnPartialSuccess {
- gctx.newLine()
- gctx.writef("%s(%q, %q)", cfnWarning, filepath.Base(ss.mkFile), "partially successful conversion")
- }
if gctx.starScript.traceCalls {
gctx.newLine()
gctx.writef(`print("<%s")`, gctx.starScript.mkFile)
@@ -250,19 +253,19 @@
gctx.writef("load(%q, %q)", baseUri, baseName)
// Emit exactly one load statement for each URI.
loadedSubConfigs := make(map[string]string)
- for _, sc := range gctx.starScript.inherited {
- uri := sc.path
+ for _, mi := range gctx.starScript.inherited {
+ uri := mi.path
if m, ok := loadedSubConfigs[uri]; ok {
// No need to emit load statement, but fix module name.
- sc.moduleLocalName = m
+ mi.moduleLocalName = m
continue
}
- if sc.optional {
+ if mi.optional || mi.missing {
uri += "|init"
}
gctx.newLine()
- gctx.writef("load(%q, %s = \"init\")", uri, sc.entryName())
- loadedSubConfigs[uri] = sc.moduleLocalName
+ gctx.writef("load(%q, %s = \"init\")", uri, mi.entryName())
+ loadedSubConfigs[uri] = mi.moduleLocalName
}
gctx.write("\n")
}
@@ -290,6 +293,24 @@
gctx.writef("%*s", 2*gctx.indentLevel, "")
}
+func (gctx *generationContext) emitConversionError(el ErrorLocation, message string) {
+ gctx.writef(`rblf.mk2rbc_error("%s", %q)`, el, message)
+}
+
+func (gctx *generationContext) emitLoadCheck(im inheritedModule) {
+ if !im.needsLoadCheck() {
+ return
+ }
+ gctx.newLine()
+ gctx.writef("if not %s:", im.entryName())
+ gctx.indentLevel++
+ gctx.newLine()
+ gctx.write(`rblf.mkerror("`, gctx.starScript.mkFile, `", "Cannot find %s" % (`)
+ im.pathExpr().emit(gctx)
+ gctx.write("))")
+ gctx.indentLevel--
+}
+
type knownVariable struct {
name string
class varClass
@@ -348,27 +369,18 @@
}
}
-type nodeReceiver interface {
- newNode(node starlarkNode)
-}
-
// Information about the generated Starlark script.
type StarlarkScript struct {
- mkFile string
- moduleName string
- mkPos scanner.Position
- nodes []starlarkNode
- inherited []*moduleInfo
- hasErrors bool
- topDir string
- traceCalls bool // print enter/exit each init function
- warnPartialSuccess bool
- sourceFS fs.FS
- makefileFinder MakefileFinder
-}
-
-func (ss *StarlarkScript) newNode(node starlarkNode) {
- ss.nodes = append(ss.nodes, node)
+ mkFile string
+ moduleName string
+ mkPos scanner.Position
+ nodes []starlarkNode
+ inherited []*moduleInfo
+ hasErrors bool
+ traceCalls bool // print enter/exit each init function
+ sourceFS fs.FS
+ makefileFinder MakefileFinder
+ nodeLocator func(pos mkparser.Pos) int
}
// varAssignmentScope points to the last assignment for each variable
@@ -388,25 +400,22 @@
ifNestLevel int
moduleNameCount map[string]int // count of imported modules with given basename
fatalError error
- builtinMakeVars map[string]starlarkExpr
outputSuffix string
- errorLogger ErrorMonitorCB
+ errorLogger ErrorLogger
tracedVariables map[string]bool // variables to be traced in the generated script
variables map[string]variable
varAssignments *varAssignmentScope
- receiver nodeReceiver // receptacle for the generated starlarkNode's
- receiverStack []nodeReceiver
outputDir string
dependentModules map[string]*moduleInfo
soongNamespaces map[string]map[string]bool
+ includeTops []string
}
func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
- topdir, _ := filepath.Split(filepath.Join(ss.topDir, "foo"))
predefined := []struct{ name, value string }{
{"SRC_TARGET_DIR", filepath.Join("build", "make", "target")},
{"LOCAL_PATH", filepath.Dir(ss.mkFile)},
- {"TOPDIR", topdir},
+ {"TOPDIR", ""}, // TOPDIR is just set to an empty string in cleanbuild.mk and core.mk
// TODO(asmundak): maybe read it from build/make/core/envsetup.mk?
{"TARGET_COPY_OUT_SYSTEM", "system"},
{"TARGET_COPY_OUT_SYSTEM_OTHER", "system_other"},
@@ -440,10 +449,10 @@
currentNodeIndex: 0,
ifNestLevel: 0,
moduleNameCount: make(map[string]int),
- builtinMakeVars: map[string]starlarkExpr{},
variables: make(map[string]variable),
dependentModules: make(map[string]*moduleInfo),
soongNamespaces: make(map[string]map[string]bool),
+ includeTops: []string{},
}
ctx.pushVarAssignments()
for _, item := range predefined {
@@ -481,20 +490,6 @@
ctx.varAssignments = ctx.varAssignments.outer
}
-func (ctx *parseContext) pushReceiver(rcv nodeReceiver) {
- ctx.receiverStack = append(ctx.receiverStack, ctx.receiver)
- ctx.receiver = rcv
-}
-
-func (ctx *parseContext) popReceiver() {
- last := len(ctx.receiverStack) - 1
- if last < 0 {
- panic(fmt.Errorf("popReceiver: receiver stack empty"))
- }
- ctx.receiver = ctx.receiverStack[last]
- ctx.receiverStack = ctx.receiverStack[0:last]
-}
-
func (ctx *parseContext) hasNodes() bool {
return ctx.currentNodeIndex < len(ctx.nodes)
}
@@ -515,32 +510,35 @@
ctx.currentNodeIndex--
}
-func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) {
+func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) []starlarkNode {
// Handle only simple variables
if !a.Name.Const() {
- ctx.errorf(a, "Only simple variables are handled")
- return
+ return []starlarkNode{ctx.newBadNode(a, "Only simple variables are handled")}
}
name := a.Name.Strings[0]
- const soongNsPrefix = "SOONG_CONFIG_"
- // Soong confuguration
+ // The `override` directive
+ // override FOO :=
+ // is parsed as an assignment to a variable named `override FOO`.
+ // There are very few places where `override` is used, just flag it.
+ if strings.HasPrefix(name, "override ") {
+ return []starlarkNode{ctx.newBadNode(a, "cannot handle override directive")}
+ }
+
+ // Soong configuration
if strings.HasPrefix(name, soongNsPrefix) {
- ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
- return
+ return ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
}
lhs := ctx.addVariable(name)
if lhs == nil {
- ctx.errorf(a, "unknown variable %s", name)
- return
+ return []starlarkNode{ctx.newBadNode(a, "unknown variable %s", name)}
}
_, isTraced := ctx.tracedVariables[name]
- asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced}
+ asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced, location: ctx.errorLocation(a)}
if lhs.valueType() == starlarkTypeUnknown {
// Try to divine variable type from the RHS
asgn.value = ctx.parseMakeString(a, a.Value)
if xBad, ok := asgn.value.(*badExpr); ok {
- ctx.wrapBadExpr(xBad)
- return
+ return []starlarkNode{&exprNode{xBad}}
}
inferred_type := asgn.value.typ()
if inferred_type != starlarkTypeUnknown {
@@ -548,9 +546,9 @@
}
}
if lhs.valueType() == starlarkTypeList {
- xConcat := ctx.buildConcatExpr(a)
- if xConcat == nil {
- return
+ xConcat, xBad := ctx.buildConcatExpr(a)
+ if xBad != nil {
+ return []starlarkNode{&exprNode{expr: xBad}}
}
switch len(xConcat.items) {
case 0:
@@ -563,14 +561,10 @@
} else {
asgn.value = ctx.parseMakeString(a, a.Value)
if xBad, ok := asgn.value.(*badExpr); ok {
- ctx.wrapBadExpr(xBad)
- return
+ return []starlarkNode{&exprNode{expr: xBad}}
}
}
- // TODO(asmundak): move evaluation to a separate pass
- asgn.value, _ = asgn.value.eval(ctx.builtinMakeVars)
-
asgn.previous = ctx.lastAssignment(name)
ctx.setLastAssignment(name, asgn)
switch a.Type {
@@ -588,16 +582,14 @@
panic(fmt.Errorf("unexpected assignment type %s", a.Type))
}
- ctx.receiver.newNode(asgn)
+ return []starlarkNode{asgn}
}
-func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) {
+func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) []starlarkNode {
val := ctx.parseMakeString(asgn, asgn.Value)
if xBad, ok := val.(*badExpr); ok {
- ctx.wrapBadExpr(xBad)
- return
+ return []starlarkNode{&exprNode{expr: xBad}}
}
- val, _ = val.eval(ctx.builtinMakeVars)
// Unfortunately, Soong namespaces can be set up by directly setting corresponding Make
// variables instead of via add_soong_config_namespace + add_soong_config_var_value.
@@ -609,22 +601,23 @@
// $(call add_soong_config_namespace,foo)
s, ok := maybeString(val)
if !ok {
- ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")}
}
+ result := make([]starlarkNode, 0)
for _, ns := range strings.Fields(s) {
ctx.addSoongNamespace(ns)
- ctx.receiver.newNode(&exprNode{&callExpr{
- name: addSoongNamespace,
- args: []starlarkExpr{&stringLiteralExpr{ns}},
+ result = append(result, &exprNode{&callExpr{
+ name: baseName + ".soong_config_namespace",
+ args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{ns}},
returnType: starlarkTypeVoid,
}})
}
+ return result
} else {
// Upon seeing
// SOONG_CONFIG_x_y = v
// find a namespace called `x` and act as if we encountered
- // $(call add_config_var_value(x,y,v)
+ // $(call soong_config_set,x,y,v)
// or check that `x_y` is a namespace, and then add the RHS of this assignment as variables in
// it.
// Emit an error in the ambiguous situation (namespaces `foo_bar` with a variable `baz`
@@ -639,41 +632,41 @@
continue
}
if namespaceName != "" {
- ctx.errorf(asgn, "ambiguous soong namespace (may be either `%s` or `%s`)", namespaceName, name[0:pos])
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "ambiguous soong namespace (may be either `%s` or `%s`)", namespaceName, name[0:pos])}
}
namespaceName = name[0:pos]
varName = name[pos+1:]
}
if namespaceName == "" {
- ctx.errorf(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")}
}
if varName == "" {
// Remember variables in this namespace
s, ok := maybeString(val)
if !ok {
- ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")}
}
ctx.updateSoongNamespace(asgn.Type != "+=", namespaceName, strings.Fields(s))
- return
+ return []starlarkNode{}
}
// Finally, handle assignment to a namespace variable
if !ctx.hasNamespaceVar(namespaceName, varName) {
- ctx.errorf(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)
- return
+ return []starlarkNode{ctx.newBadNode(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)}
}
- ctx.receiver.newNode(&exprNode{&callExpr{
- name: addSoongConfigVarValue,
- args: []starlarkExpr{&stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
+ fname := baseName + "." + soongConfigAssign
+ if asgn.Type == "+=" {
+ fname = baseName + "." + soongConfigAppend
+ }
+ return []starlarkNode{&exprNode{&callExpr{
+ name: fname,
+ args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
returnType: starlarkTypeVoid,
- }})
+ }}}
}
}
-func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) *concatExpr {
+func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) (*concatExpr, *badExpr) {
xConcat := &concatExpr{}
var xItemList *listExpr
addToItemList := func(x ...starlarkExpr) {
@@ -695,8 +688,7 @@
// expressions return individual elements.
switch x := ctx.parseMakeString(a, item).(type) {
case *badExpr:
- ctx.wrapBadExpr(x)
- return nil
+ return nil, x
case *stringLiteralExpr:
addToItemList(maybeConvertToStringList(x).(*listExpr).items...)
default:
@@ -720,7 +712,7 @@
if xItemList != nil {
xConcat.items = append(xConcat.items, xItemList)
}
- return xConcat
+ return xConcat, nil
}
func (ctx *parseContext) newDependentModule(path string, optional bool) *moduleInfo {
@@ -736,11 +728,13 @@
moduleLocalName += fmt.Sprintf("%d", n)
}
ctx.moduleNameCount[moduleName] = n + 1
+ _, err := fs.Stat(ctx.script.sourceFS, path)
mi := &moduleInfo{
path: modulePath,
originalPath: path,
moduleLocalName: moduleLocalName,
optional: optional,
+ missing: err != nil,
}
ctx.dependentModules[modulePath] = mi
ctx.script.inherited = append(ctx.script.inherited, mi)
@@ -748,8 +742,7 @@
}
func (ctx *parseContext) handleSubConfig(
- v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule)) {
- pathExpr, _ = pathExpr.eval(ctx.builtinMakeVars)
+ v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule) starlarkNode) []starlarkNode {
// In a simple case, the name of a module to inherit/include is known statically.
if path, ok := maybeString(pathExpr); ok {
@@ -758,18 +751,19 @@
moduleShouldExist := loadAlways && ctx.ifNestLevel == 0
if strings.Contains(path, "*") {
if paths, err := fs.Glob(ctx.script.sourceFS, path); err == nil {
+ result := make([]starlarkNode, 0)
for _, p := range paths {
mi := ctx.newDependentModule(p, !moduleShouldExist)
- processModule(inheritedStaticModule{mi, loadAlways})
+ result = append(result, processModule(inheritedStaticModule{mi, loadAlways}))
}
+ return result
} else {
- ctx.errorf(v, "cannot glob wildcard argument")
+ return []starlarkNode{ctx.newBadNode(v, "cannot glob wildcard argument")}
}
} else {
mi := ctx.newDependentModule(path, !moduleShouldExist)
- processModule(inheritedStaticModule{mi, loadAlways})
+ return []starlarkNode{processModule(inheritedStaticModule{mi, loadAlways})}
}
- return
}
// If module path references variables (e.g., $(v1)/foo/$(v2)/device-config.mk), find all the paths in the
@@ -789,8 +783,7 @@
var matchingPaths []string
varPath, ok := pathExpr.(*interpolateExpr)
if !ok {
- ctx.errorf(v, "inherit-product/include argument is too complex")
- return
+ return []starlarkNode{ctx.newBadNode(v, "inherit-product/include argument is too complex")}
}
pathPattern := []string{varPath.chunks[0]}
@@ -799,40 +792,39 @@
pathPattern = append(pathPattern, chunk)
}
}
- if pathPattern[0] != "" {
- matchingPaths = ctx.findMatchingPaths(pathPattern)
- } else {
- // Heuristics -- if pattern starts from top, restrict it to the directories where
- // we know inherit-product uses dynamically calculated path. Restrict it even further
- // for certain path which would yield too many useless matches
- if len(varPath.chunks) == 2 && varPath.chunks[1] == "/BoardConfigVendor.mk" {
- pathPattern[0] = "vendor/google_devices"
- matchingPaths = ctx.findMatchingPaths(pathPattern)
- } else {
- for _, t := range []string{"vendor/qcom", "vendor/google_devices"} {
- pathPattern[0] = t
- matchingPaths = append(matchingPaths, ctx.findMatchingPaths(pathPattern)...)
- }
+ 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 {
- ctx.errorf(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)
- return
+ return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)}
}
- res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways}
- for _, p := range matchingPaths {
- // A product configuration files discovered dynamically may attempt to inherit
- // from another one which does not exist in this source tree. Prevent load errors
- // by always loading the dynamic files as optional.
- res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
+ if len(matchingPaths) == 1 {
+ res := inheritedStaticModule{ctx.newDependentModule(matchingPaths[0], loadAlways && ctx.ifNestLevel == 0), loadAlways}
+ return []starlarkNode{processModule(res)}
+ } else {
+ needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0
+ res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
+ for _, p := range matchingPaths {
+ // A product configuration files discovered dynamically may attempt to inherit
+ // from another one which does not exist in this source tree. Prevent load errors
+ // by always loading the dynamic files as optional.
+ res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
+ }
+ return []starlarkNode{processModule(res)}
}
- processModule(res)
}
func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
- files := ctx.script.makefileFinder.Find(ctx.script.topDir)
+ files := ctx.script.makefileFinder.Find(".")
if len(pattern) == 0 {
return files
}
@@ -855,90 +847,90 @@
return res
}
-func (ctx *parseContext) handleInheritModule(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) {
- ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) {
- ctx.receiver.newNode(&inheritNode{im, loadAlways})
+func (ctx *parseContext) handleInheritModule(v mkparser.Node, args *mkparser.MakeString, loadAlways bool) []starlarkNode {
+ args.TrimLeftSpaces()
+ args.TrimRightSpaces()
+ pathExpr := ctx.parseMakeString(v, args)
+ if _, ok := pathExpr.(*badExpr); ok {
+ return []starlarkNode{ctx.newBadNode(v, "Unable to parse argument to inherit")}
+ }
+ return ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) starlarkNode {
+ return &inheritNode{im, loadAlways}
})
}
-func (ctx *parseContext) handleInclude(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) {
- ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) {
- ctx.receiver.newNode(&includeNode{im, loadAlways})
+func (ctx *parseContext) handleInclude(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) []starlarkNode {
+ return ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) starlarkNode {
+ return &includeNode{im, loadAlways}
})
}
-func (ctx *parseContext) handleVariable(v *mkparser.Variable) {
+func (ctx *parseContext) handleVariable(v *mkparser.Variable) []starlarkNode {
// Handle:
// $(call inherit-product,...)
// $(call inherit-product-if-exists,...)
// $(info xxx)
// $(warning xxx)
// $(error xxx)
- expr := ctx.parseReference(v, v.Name)
- switch x := expr.(type) {
- case *callExpr:
- if x.name == callLoadAlways || x.name == callLoadIf {
- ctx.handleInheritModule(v, x.args[0], x.name == callLoadAlways)
- } else if isMakeControlFunc(x.name) {
- // File name is the first argument
- args := []starlarkExpr{
- &stringLiteralExpr{ctx.script.mkFile},
- x.args[0],
- }
- ctx.receiver.newNode(&exprNode{
- &callExpr{name: x.name, args: args, returnType: starlarkTypeUnknown},
- })
- } else {
- ctx.receiver.newNode(&exprNode{expr})
- }
- case *badExpr:
- ctx.wrapBadExpr(x)
- return
- default:
- ctx.errorf(v, "cannot handle %s", v.Dump())
- return
+ // $(call other-custom-functions,...)
+
+ // inherit-product(-if-exists) gets converted to a series of statements,
+ // not just a single expression like parseReference returns. So handle it
+ // separately at the beginning here.
+ if strings.HasPrefix(v.Name.Dump(), "call inherit-product,") {
+ args := v.Name.Clone()
+ args.ReplaceLiteral("call inherit-product,", "")
+ return ctx.handleInheritModule(v, args, true)
}
+ if strings.HasPrefix(v.Name.Dump(), "call inherit-product-if-exists,") {
+ args := v.Name.Clone()
+ args.ReplaceLiteral("call inherit-product-if-exists,", "")
+ return ctx.handleInheritModule(v, args, false)
+ }
+ return []starlarkNode{&exprNode{expr: ctx.parseReference(v, v.Name)}}
}
-func (ctx *parseContext) handleDefine(directive *mkparser.Directive) {
+func (ctx *parseContext) maybeHandleDefine(directive *mkparser.Directive) starlarkNode {
macro_name := strings.Fields(directive.Args.Strings[0])[0]
// Ignore the macros that we handle
- if _, ok := knownFunctions[macro_name]; !ok {
- ctx.errorf(directive, "define is not supported: %s", macro_name)
+ _, ignored := ignoredDefines[macro_name]
+ _, known := knownFunctions[macro_name]
+ if !ignored && !known {
+ return ctx.newBadNode(directive, "define is not supported: %s", macro_name)
}
+ return nil
}
-func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) {
- ssSwitch := &switchNode{}
- ctx.pushReceiver(ssSwitch)
- for ctx.processBranch(ifDirective); ctx.hasNodes() && ctx.fatalError == nil; {
+func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) starlarkNode {
+ ssSwitch := &switchNode{
+ ssCases: []*switchCase{ctx.processBranch(ifDirective)},
+ }
+ for ctx.hasNodes() && ctx.fatalError == nil {
node := ctx.getNode()
switch x := node.(type) {
case *mkparser.Directive:
switch x.Name {
case "else", "elifdef", "elifndef", "elifeq", "elifneq":
- ctx.processBranch(x)
+ ssSwitch.ssCases = append(ssSwitch.ssCases, ctx.processBranch(x))
case "endif":
- ctx.popReceiver()
- ctx.receiver.newNode(ssSwitch)
- return
+ return ssSwitch
default:
- ctx.errorf(node, "unexpected directive %s", x.Name)
+ return ctx.newBadNode(node, "unexpected directive %s", x.Name)
}
default:
- ctx.errorf(ifDirective, "unexpected statement")
+ return ctx.newBadNode(ifDirective, "unexpected statement")
}
}
if ctx.fatalError == nil {
ctx.fatalError = fmt.Errorf("no matching endif for %s", ifDirective.Dump())
}
- ctx.popReceiver()
+ return ctx.newBadNode(ifDirective, "no matching endif for %s", ifDirective.Dump())
}
// processBranch processes a single branch (if/elseif/else) until the next directive
// on the same level.
-func (ctx *parseContext) processBranch(check *mkparser.Directive) {
- block := switchCase{gate: ctx.parseCondition(check)}
+func (ctx *parseContext) processBranch(check *mkparser.Directive) *switchCase {
+ block := &switchCase{gate: ctx.parseCondition(check)}
defer func() {
ctx.popVarAssignments()
ctx.ifNestLevel--
@@ -947,46 +939,29 @@
ctx.pushVarAssignments()
ctx.ifNestLevel++
- ctx.pushReceiver(&block)
for ctx.hasNodes() {
node := ctx.getNode()
- if ctx.handleSimpleStatement(node) {
- continue
- }
- switch d := node.(type) {
- case *mkparser.Directive:
+ if d, ok := node.(*mkparser.Directive); ok {
switch d.Name {
case "else", "elifdef", "elifndef", "elifeq", "elifneq", "endif":
- ctx.popReceiver()
- ctx.receiver.newNode(&block)
ctx.backNode()
- return
- case "ifdef", "ifndef", "ifeq", "ifneq":
- ctx.handleIfBlock(d)
- default:
- ctx.errorf(d, "unexpected directive %s", d.Name)
+ return block
}
- default:
- ctx.errorf(node, "unexpected statement")
}
+ block.nodes = append(block.nodes, ctx.handleSimpleStatement(node)...)
}
ctx.fatalError = fmt.Errorf("no matching endif for %s", check.Dump())
- ctx.popReceiver()
-}
-
-func (ctx *parseContext) newIfDefinedNode(check *mkparser.Directive) (starlarkExpr, bool) {
- if !check.Args.Const() {
- return ctx.newBadExpr(check, "ifdef variable ref too complex: %s", check.Args.Dump()), false
- }
- v := ctx.addVariable(check.Args.Strings[0])
- return &variableDefinedExpr{v}, true
+ return block
}
func (ctx *parseContext) parseCondition(check *mkparser.Directive) starlarkNode {
switch check.Name {
case "ifdef", "ifndef", "elifdef", "elifndef":
- v, ok := ctx.newIfDefinedNode(check)
- if ok && strings.HasSuffix(check.Name, "ndef") {
+ if !check.Args.Const() {
+ return ctx.newBadNode(check, "ifdef variable ref too complex: %s", check.Args.Dump())
+ }
+ v := NewVariableRefExpr(ctx.addVariable(check.Args.Strings[0]), false)
+ if strings.HasSuffix(check.Name, "ndef") {
v = ¬Expr{v}
}
return &ifNode{
@@ -1006,12 +981,16 @@
}
func (ctx *parseContext) newBadExpr(node mkparser.Node, text string, args ...interface{}) starlarkExpr {
- message := fmt.Sprintf(text, args...)
if ctx.errorLogger != nil {
- ctx.errorLogger.NewError(text, node, args)
+ ctx.errorLogger.NewError(ctx.errorLocation(node), node, text, args...)
}
ctx.script.hasErrors = true
- return &badExpr{node, message}
+ return &badExpr{errorLocation: ctx.errorLocation(node), message: fmt.Sprintf(text, args...)}
+}
+
+// records that the given node failed to be converted and includes an explanatory message
+func (ctx *parseContext) newBadNode(failedNode mkparser.Node, message string, args ...interface{}) starlarkNode {
+ return &exprNode{ctx.newBadExpr(failedNode, message, args...)}
}
func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr {
@@ -1029,179 +1008,165 @@
args[1].TrimLeftSpaces()
isEq := !strings.HasSuffix(cond.Name, "neq")
- switch xLeft := ctx.parseMakeString(cond, args[0]).(type) {
- case *stringLiteralExpr, *variableRefExpr:
- switch xRight := ctx.parseMakeString(cond, args[1]).(type) {
- case *stringLiteralExpr, *variableRefExpr:
- return &eqExpr{left: xLeft, right: xRight, isEq: isEq}
- case *badExpr:
- return xRight
- default:
- expr, ok := ctx.parseCheckFunctionCallResult(cond, xLeft, args[1])
- if ok {
- return expr
+ xLeft := ctx.parseMakeString(cond, args[0])
+ xRight := ctx.parseMakeString(cond, args[1])
+ if bad, ok := xLeft.(*badExpr); ok {
+ return bad
+ }
+ if bad, ok := xRight.(*badExpr); ok {
+ return bad
+ }
+
+ if expr, ok := ctx.parseCompareSpecialCases(cond, xLeft, xRight); ok {
+ return expr
+ }
+
+ var stringOperand string
+ var otherOperand starlarkExpr
+ if s, ok := maybeString(xLeft); ok {
+ stringOperand = s
+ otherOperand = xRight
+ } else if s, ok := maybeString(xRight); ok {
+ stringOperand = s
+ otherOperand = xLeft
+ }
+
+ not := func(expr starlarkExpr) starlarkExpr {
+ switch typedExpr := expr.(type) {
+ case *inExpr:
+ typedExpr.isNot = !typedExpr.isNot
+ return typedExpr
+ case *eqExpr:
+ typedExpr.isEq = !typedExpr.isEq
+ return typedExpr
+ case *binaryOpExpr:
+ switch typedExpr.op {
+ case ">":
+ typedExpr.op = "<="
+ return typedExpr
+ case "<":
+ typedExpr.op = ">="
+ return typedExpr
+ case ">=":
+ typedExpr.op = "<"
+ return typedExpr
+ case "<=":
+ typedExpr.op = ">"
+ return typedExpr
+ default:
+ return ¬Expr{expr: expr}
}
- return ctx.newBadExpr(cond, "right operand is too complex: %s", args[1].Dump())
- }
- case *badExpr:
- return xLeft
- default:
- switch xRight := ctx.parseMakeString(cond, args[1]).(type) {
- case *stringLiteralExpr, *variableRefExpr:
- expr, ok := ctx.parseCheckFunctionCallResult(cond, xRight, args[0])
- if ok {
- return expr
- }
- return ctx.newBadExpr(cond, "left operand is too complex: %s", args[0].Dump())
- case *badExpr:
- return xRight
default:
- return ctx.newBadExpr(cond, "operands are too complex: (%s,%s)", args[0].Dump(), args[1].Dump())
+ return ¬Expr{expr: expr}
}
}
+
+ // If we've identified one of the operands as being a string literal, check
+ // for some special cases we can do to simplify the resulting expression.
+ if otherOperand != nil {
+ if stringOperand == "" {
+ if isEq {
+ return not(otherOperand)
+ } else {
+ return otherOperand
+ }
+ }
+ if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
+ if !isEq {
+ return not(otherOperand)
+ } else {
+ return otherOperand
+ }
+ }
+ if intOperand, err := strconv.Atoi(strings.TrimSpace(stringOperand)); err == nil && otherOperand.typ() == starlarkTypeInt {
+ return &eqExpr{
+ left: otherOperand,
+ right: &intLiteralExpr{literal: intOperand},
+ isEq: isEq,
+ }
+ }
+ }
+
+ return &eqExpr{left: xLeft, right: xRight, isEq: isEq}
}
-func (ctx *parseContext) parseCheckFunctionCallResult(directive *mkparser.Directive, xValue starlarkExpr,
- varArg *mkparser.MakeString) (starlarkExpr, bool) {
- mkSingleVar, ok := varArg.SingleVariable()
- if !ok {
+// Given an if statement's directive and the left/right starlarkExprs,
+// check if the starlarkExprs are one of a few hardcoded special cases
+// that can be converted to a simpler equality expression than simply comparing
+// the two.
+func (ctx *parseContext) parseCompareSpecialCases(directive *mkparser.Directive, left starlarkExpr,
+ right starlarkExpr) (starlarkExpr, bool) {
+ isEq := !strings.HasSuffix(directive.Name, "neq")
+
+ // All the special cases require a call on one side and a
+ // string literal/variable on the other. Turn the left/right variables into
+ // call/value variables, and return false if that's not possible.
+ var value starlarkExpr = nil
+ call, ok := left.(*callExpr)
+ if ok {
+ switch right.(type) {
+ case *stringLiteralExpr, *variableRefExpr:
+ value = right
+ }
+ } else {
+ call, _ = right.(*callExpr)
+ switch left.(type) {
+ case *stringLiteralExpr, *variableRefExpr:
+ value = left
+ }
+ }
+
+ if call == nil || value == nil {
return nil, false
}
- expr := ctx.parseReference(directive, mkSingleVar)
- negate := strings.HasSuffix(directive.Name, "neq")
- checkIsSomethingFunction := func(xCall *callExpr) starlarkExpr {
- s, ok := maybeString(xValue)
- if !ok || s != "true" {
- return ctx.newBadExpr(directive,
- fmt.Sprintf("the result of %s can be compared only to 'true'", xCall.name))
- }
- if len(xCall.args) < 1 {
- return ctx.newBadExpr(directive, "%s requires an argument", xCall.name)
- }
- return nil
+
+ 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":
+ return ctx.parseCompareStripFuncResult(directive, call, value, !isEq), true
}
- switch x := expr.(type) {
- case *callExpr:
- switch x.name {
- case "filter":
- return ctx.parseCompareFilterFuncResult(directive, x, xValue, !negate), true
- case "filter-out":
- return ctx.parseCompareFilterFuncResult(directive, x, xValue, negate), true
- case "wildcard":
- return ctx.parseCompareWildcardFuncResult(directive, x, xValue, negate), true
- case "findstring":
- return ctx.parseCheckFindstringFuncResult(directive, x, xValue, negate), true
- case "strip":
- return ctx.parseCompareStripFuncResult(directive, x, xValue, negate), true
- case "is-board-platform":
- if xBad := checkIsSomethingFunction(x); xBad != nil {
- return xBad, true
- }
- return &eqExpr{
- left: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
- right: x.args[0],
- isEq: !negate,
- }, true
- case "is-board-platform-in-list":
- if xBad := checkIsSomethingFunction(x); xBad != nil {
- return xBad, true
- }
- return &inExpr{
- expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
- list: maybeConvertToStringList(x.args[0]),
- isNot: negate,
- }, true
- case "is-product-in-list":
- if xBad := checkIsSomethingFunction(x); xBad != nil {
- return xBad, true
- }
- return &inExpr{
- expr: &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
- list: maybeConvertToStringList(x.args[0]),
- isNot: negate,
- }, true
- case "is-vendor-board-platform":
- if xBad := checkIsSomethingFunction(x); xBad != nil {
- return xBad, true
- }
- s, ok := maybeString(x.args[0])
- if !ok {
- return ctx.newBadExpr(directive, "cannot handle non-constant argument to is-vendor-board-platform"), true
- }
- return &inExpr{
- expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
- list: &variableRefExpr{ctx.addVariable(s + "_BOARD_PLATFORMS"), true},
- isNot: negate,
- }, true
- default:
- return ctx.newBadExpr(directive, "Unknown function in ifeq: %s", x.name), true
- }
- case *badExpr:
- return x, true
- default:
- return nil, false
- }
+ return nil, false
}
func (ctx *parseContext) parseCompareFilterFuncResult(cond *mkparser.Directive,
- filterFuncCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
+ filterFuncCall *callExpr, xValue starlarkExpr, negate bool) (starlarkExpr, bool) {
// We handle:
// * ifeq/ifneq (,$(filter v1 v2 ..., EXPR) becomes if EXPR not in/in ["v1", "v2", ...]
// * ifeq/ifneq (,$(filter EXPR, v1 v2 ...) becomes if EXPR not in/in ["v1", "v2", ...]
- // * ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...) becomes if VAR in/not in ["v1", "v2"]
- // TODO(Asmundak): check the last case works for filter-out, too.
+ if x, ok := xValue.(*stringLiteralExpr); !ok || x.literal != "" {
+ return nil, false
+ }
xPattern := filterFuncCall.args[0]
xText := filterFuncCall.args[1]
var xInList *stringLiteralExpr
var expr starlarkExpr
var ok bool
- switch x := xValue.(type) {
- case *stringLiteralExpr:
- if x.literal != "" {
- return ctx.newBadExpr(cond, "filter comparison to non-empty value: %s", xValue)
- }
- // Either pattern or text should be const, and the
- // non-const one should be varRefExpr
- if xInList, ok = xPattern.(*stringLiteralExpr); ok {
- expr = xText
- } else if xInList, ok = xText.(*stringLiteralExpr); ok {
- expr = xPattern
- } else {
- return &callExpr{
- object: nil,
- name: filterFuncCall.name,
- args: filterFuncCall.args,
- returnType: starlarkTypeBool,
- }
- }
- case *variableRefExpr:
- if v, ok := xPattern.(*variableRefExpr); ok {
- if xInList, ok = xText.(*stringLiteralExpr); ok && v.ref.name() == x.ref.name() {
- // ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...), flip negate,
- // it's the opposite to what is done when comparing to empty.
- expr = xPattern
- negate = !negate
- }
- }
+ if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
+ expr = xText
+ } else if xInList, ok = xText.(*stringLiteralExpr); ok {
+ expr = xPattern
+ } else {
+ return nil, false
}
- if expr != nil && xInList != nil {
- slExpr := newStringListExpr(strings.Fields(xInList.literal))
- // Generate simpler code for the common cases:
- if expr.typ() == starlarkTypeList {
- if len(slExpr.items) == 1 {
- // Checking that a string belongs to list
- return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}
- } else {
- // TODO(asmundak):
- panic("TBD")
- }
- } else if len(slExpr.items) == 1 {
- return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}
+ slExpr := newStringListExpr(strings.Fields(xInList.literal))
+ // Generate simpler code for the common cases:
+ if expr.typ() == starlarkTypeList {
+ if len(slExpr.items) == 1 {
+ // Checking that a string belongs to list
+ return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}, true
} else {
- return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}
+ return nil, false
}
+ } else if len(slExpr.items) == 1 {
+ return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}, true
+ } else {
+ return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}, true
}
- return ctx.newBadExpr(cond, "filter arguments are too complex: %s", cond.Dump())
}
func (ctx *parseContext) parseCompareWildcardFuncResult(directive *mkparser.Directive,
@@ -1209,9 +1174,9 @@
if !isEmptyString(xValue) {
return ctx.newBadExpr(directive, "wildcard result can be compared only to empty: %s", xValue)
}
- callFunc := wildcardExistsPhony
+ callFunc := baseName + ".file_wildcard_exists"
if s, ok := xCall.args[0].(*stringLiteralExpr); ok && !strings.ContainsAny(s.literal, "*?{[") {
- callFunc = fileExistsPhony
+ callFunc = baseName + ".file_exists"
}
var cc starlarkExpr = &callExpr{name: callFunc, args: xCall.args, returnType: starlarkTypeBool}
if !negate {
@@ -1233,8 +1198,21 @@
right: &intLiteralExpr{-1},
isEq: !negate,
}
+ } else if s, ok := maybeString(xValue); ok {
+ if s2, ok := maybeString(xCall.args[0]); ok && s == s2 {
+ return &eqExpr{
+ left: &callExpr{
+ object: xCall.args[1],
+ name: "find",
+ args: []starlarkExpr{xCall.args[0]},
+ returnType: starlarkTypeInt,
+ },
+ right: &intLiteralExpr{-1},
+ isEq: negate,
+ }
+ }
}
- return ctx.newBadExpr(directive, "findstring result can be compared only to empty: %s", xValue)
+ return ctx.newBadExpr(directive, "$(findstring) can only be compared to nothing or its first argument")
}
func (ctx *parseContext) parseCompareStripFuncResult(directive *mkparser.Directive,
@@ -1265,33 +1243,51 @@
// If it is a single word, it can be a simple variable
// reference or a function call
- if len(words) == 1 {
- if isMakeControlFunc(refDump) || refDump == "shell" {
+ if len(words) == 1 && !isMakeControlFunc(refDump) && refDump != "shell" {
+ if strings.HasPrefix(refDump, soongNsPrefix) {
+ // TODO (asmundak): if we find many, maybe handle them.
+ return ctx.newBadExpr(node, "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: %s", refDump)
+ }
+ // Handle substitution references: https://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html
+ if strings.Contains(refDump, ":") {
+ parts := strings.SplitN(refDump, ":", 2)
+ substParts := strings.SplitN(parts[1], "=", 2)
+ if len(substParts) < 2 || strings.Count(substParts[0], "%") > 1 {
+ return ctx.newBadExpr(node, "Invalid substitution reference")
+ }
+ if !strings.Contains(substParts[0], "%") {
+ if strings.Contains(substParts[1], "%") {
+ return ctx.newBadExpr(node, "A substitution reference must have a %% in the \"before\" part of the substitution if it has one in the \"after\" part.")
+ }
+ substParts[0] = "%" + substParts[0]
+ substParts[1] = "%" + substParts[1]
+ }
+ v := ctx.addVariable(parts[0])
+ if v == nil {
+ return ctx.newBadExpr(node, "unknown variable %s", refDump)
+ }
return &callExpr{
- name: refDump,
- args: []starlarkExpr{&stringLiteralExpr{""}},
- returnType: starlarkTypeUnknown,
+ name: baseName + ".mkpatsubst",
+ returnType: starlarkTypeString,
+ args: []starlarkExpr{
+ &stringLiteralExpr{literal: substParts[0]},
+ &stringLiteralExpr{literal: substParts[1]},
+ NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil),
+ },
}
}
if v := ctx.addVariable(refDump); v != nil {
- return &variableRefExpr{v, ctx.lastAssignment(v.name()) != nil}
+ return NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil)
}
return ctx.newBadExpr(node, "unknown variable %s", refDump)
}
expr := &callExpr{name: words[0].Dump(), returnType: starlarkTypeUnknown}
- args := words[1]
- args.TrimLeftSpaces()
- // Make control functions and shell need special treatment as everything
- // after the name is a single text argument
- if isMakeControlFunc(expr.name) || expr.name == "shell" {
- x := ctx.parseMakeString(node, args)
- if xBad, ok := x.(*badExpr); ok {
- return xBad
- }
- expr.args = []starlarkExpr{x}
- return expr
+ args := mkparser.SimpleMakeString("", words[0].Pos())
+ if len(words) >= 2 {
+ args = words[1]
}
+ args.TrimLeftSpaces()
if expr.name == "call" {
words = args.SplitN(",", 2)
if words[0].Empty() || !words[0].Const() {
@@ -1305,64 +1301,225 @@
}
}
if kf, found := knownFunctions[expr.name]; found {
- expr.returnType = kf.returnType
+ return kf.parse(ctx, node, args)
} else {
return ctx.newBadExpr(node, "cannot handle invoking %s", expr.name)
}
- switch expr.name {
- case "word":
- return ctx.parseWordFunc(node, args)
- case "firstword", "lastword":
- return ctx.parseFirstOrLastwordFunc(node, expr.name, args)
- case "my-dir":
- return &variableRefExpr{ctx.addVariable("LOCAL_PATH"), true}
- case "subst", "patsubst":
- return ctx.parseSubstFunc(node, expr.name, args)
- default:
- for _, arg := range args.Split(",") {
- arg.TrimLeftSpaces()
- arg.TrimRightSpaces()
- x := ctx.parseMakeString(node, arg)
- if xBad, ok := x.(*badExpr); ok {
- return xBad
- }
- expr.args = append(expr.args, x)
+}
+
+type simpleCallParser struct {
+ name string
+ returnType starlarkType
+ addGlobals bool
+}
+
+func (p *simpleCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ expr := &callExpr{name: p.name, returnType: p.returnType}
+ if p.addGlobals {
+ expr.args = append(expr.args, &globalsExpr{})
+ }
+ for _, arg := range args.Split(",") {
+ arg.TrimLeftSpaces()
+ arg.TrimRightSpaces()
+ x := ctx.parseMakeString(node, arg)
+ if xBad, ok := x.(*badExpr); ok {
+ return xBad
}
+ expr.args = append(expr.args, x)
}
return expr
}
-func (ctx *parseContext) parseSubstFunc(node mkparser.Node, fname string, args *mkparser.MakeString) starlarkExpr {
+type makeControlFuncParser struct {
+ name string
+}
+
+func (p *makeControlFuncParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ // Make control functions need special treatment as everything
+ // after the name is a single text argument
+ x := ctx.parseMakeString(node, args)
+ if xBad, ok := x.(*badExpr); ok {
+ return xBad
+ }
+ return &callExpr{
+ name: p.name,
+ args: []starlarkExpr{
+ &stringLiteralExpr{ctx.script.mkFile},
+ x,
+ },
+ returnType: starlarkTypeUnknown,
+ }
+}
+
+type shellCallParser struct{}
+
+func (p *shellCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ // Shell functions need special treatment as everything
+ // after the name is a single text argument
+ x := ctx.parseMakeString(node, args)
+ if xBad, ok := x.(*badExpr); ok {
+ return xBad
+ }
+ return &callExpr{
+ name: baseName + ".shell",
+ args: []starlarkExpr{x},
+ returnType: starlarkTypeUnknown,
+ }
+}
+
+type myDirCallParser struct{}
+
+func (p *myDirCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ if !args.Empty() {
+ return ctx.newBadExpr(node, "my-dir function cannot have any arguments passed to it.")
+ }
+ return &variableRefExpr{ctx.addVariable("LOCAL_PATH"), true}
+}
+
+type isProductInListCallParser struct{}
+
+func (p *isProductInListCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ if args.Empty() {
+ return ctx.newBadExpr(node, "is-product-in-list requires an argument")
+ }
+ return &inExpr{
+ expr: &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
+ list: maybeConvertToStringList(ctx.parseMakeString(node, args)),
+ isNot: false,
+ }
+}
+
+type isVendorBoardPlatformCallParser struct{}
+
+func (p *isVendorBoardPlatformCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ if args.Empty() || !identifierFullMatchRegex.MatchString(args.Dump()) {
+ return ctx.newBadExpr(node, "cannot handle non-constant argument to is-vendor-board-platform")
+ }
+ return &inExpr{
+ expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+ list: &variableRefExpr{ctx.addVariable(args.Dump() + "_BOARD_PLATFORMS"), true},
+ isNot: false,
+ }
+}
+
+type isVendorBoardQcomCallParser struct{}
+
+func (p *isVendorBoardQcomCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ if !args.Empty() {
+ return ctx.newBadExpr(node, "is-vendor-board-qcom does not accept any arguments")
+ }
+ return &inExpr{
+ expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+ list: &variableRefExpr{ctx.addVariable("QCOM_BOARD_PLATFORMS"), true},
+ isNot: false,
+ }
+}
+
+type substCallParser struct {
+ fname string
+}
+
+func (p *substCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
words := args.Split(",")
if len(words) != 3 {
- return ctx.newBadExpr(node, "%s function should have 3 arguments", fname)
+ return ctx.newBadExpr(node, "%s function should have 3 arguments", p.fname)
}
- if !words[0].Const() || !words[1].Const() {
- return ctx.newBadExpr(node, "%s function's from and to arguments should be constant", fname)
+ from := ctx.parseMakeString(node, words[0])
+ if xBad, ok := from.(*badExpr); ok {
+ return xBad
}
- from := words[0].Strings[0]
- to := words[1].Strings[0]
+ to := ctx.parseMakeString(node, words[1])
+ if xBad, ok := to.(*badExpr); ok {
+ return xBad
+ }
words[2].TrimLeftSpaces()
words[2].TrimRightSpaces()
obj := ctx.parseMakeString(node, words[2])
typ := obj.typ()
- if typ == starlarkTypeString && fname == "subst" {
+ if typ == starlarkTypeString && p.fname == "subst" {
// Optimization: if it's $(subst from, to, string), emit string.replace(from, to)
return &callExpr{
object: obj,
name: "replace",
- args: []starlarkExpr{&stringLiteralExpr{from}, &stringLiteralExpr{to}},
+ args: []starlarkExpr{from, to},
returnType: typ,
}
}
return &callExpr{
- name: fname,
- args: []starlarkExpr{&stringLiteralExpr{from}, &stringLiteralExpr{to}, obj},
+ name: baseName + ".mk" + p.fname,
+ args: []starlarkExpr{from, to, obj},
returnType: obj.typ(),
}
}
-func (ctx *parseContext) parseWordFunc(node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+type ifCallParser struct{}
+
+func (p *ifCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ words := args.Split(",")
+ if len(words) != 2 && len(words) != 3 {
+ return ctx.newBadExpr(node, "if function should have 2 or 3 arguments, found "+strconv.Itoa(len(words)))
+ }
+ condition := ctx.parseMakeString(node, words[0])
+ ifTrue := ctx.parseMakeString(node, words[1])
+ var ifFalse starlarkExpr
+ if len(words) == 3 {
+ ifFalse = ctx.parseMakeString(node, words[2])
+ } else {
+ switch ifTrue.typ() {
+ case starlarkTypeList:
+ ifFalse = &listExpr{items: []starlarkExpr{}}
+ case starlarkTypeInt:
+ ifFalse = &intLiteralExpr{literal: 0}
+ case starlarkTypeBool:
+ ifFalse = &boolLiteralExpr{literal: false}
+ default:
+ ifFalse = &stringLiteralExpr{literal: ""}
+ }
+ }
+ return &ifExpr{
+ condition,
+ ifTrue,
+ ifFalse,
+ }
+}
+
+type foreachCallPaser struct{}
+
+func (p *foreachCallPaser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ words := args.Split(",")
+ if len(words) != 3 {
+ return ctx.newBadExpr(node, "foreach function should have 3 arguments, found "+strconv.Itoa(len(words)))
+ }
+ if !words[0].Const() || words[0].Empty() || !identifierFullMatchRegex.MatchString(words[0].Strings[0]) {
+ return ctx.newBadExpr(node, "first argument to foreach function must be a simple string identifier")
+ }
+ loopVarName := words[0].Strings[0]
+ list := ctx.parseMakeString(node, words[1])
+ action := ctx.parseMakeString(node, words[2]).transform(func(expr starlarkExpr) starlarkExpr {
+ if varRefExpr, ok := expr.(*variableRefExpr); ok && varRefExpr.ref.name() == loopVarName {
+ return &identifierExpr{loopVarName}
+ }
+ return nil
+ })
+
+ if list.typ() != starlarkTypeList {
+ list = &callExpr{
+ name: baseName + ".words",
+ returnType: starlarkTypeList,
+ args: []starlarkExpr{list},
+ }
+ }
+
+ return &foreachExpr{
+ varName: loopVarName,
+ list: list,
+ action: action,
+ }
+}
+
+type wordCallParser struct{}
+
+func (p *wordCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
words := args.Split(",")
if len(words) != 2 {
return ctx.newBadExpr(node, "word function should have 2 arguments")
@@ -1383,16 +1540,20 @@
if array.typ() != starlarkTypeList {
array = &callExpr{object: array, name: "split", returnType: starlarkTypeList}
}
- return indexExpr{array, &intLiteralExpr{int(index - 1)}}
+ return &indexExpr{array, &intLiteralExpr{int(index - 1)}}
}
-func (ctx *parseContext) parseFirstOrLastwordFunc(node mkparser.Node, name string, args *mkparser.MakeString) starlarkExpr {
+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 {
return bad
}
index := &intLiteralExpr{0}
- if name == "lastword" {
+ if p.isLastWord {
if v, ok := arg.(*variableRefExpr); ok && v.ref.name() == "MAKEFILE_LIST" {
return &stringLiteralExpr{ctx.script.mkFile}
}
@@ -1404,6 +1565,68 @@
return &indexExpr{&callExpr{object: arg, name: "split", returnType: starlarkTypeList}, index}
}
+func parseIntegerArguments(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString, expectedArgs int) ([]starlarkExpr, error) {
+ parsedArgs := make([]starlarkExpr, 0)
+ for _, arg := range args.Split(",") {
+ expr := ctx.parseMakeString(node, arg)
+ if expr.typ() == starlarkTypeList {
+ return nil, fmt.Errorf("argument to math argument has type list, which cannot be converted to int")
+ }
+ if s, ok := maybeString(expr); ok {
+ intVal, err := strconv.Atoi(strings.TrimSpace(s))
+ if err != nil {
+ return nil, err
+ }
+ expr = &intLiteralExpr{literal: intVal}
+ } else if expr.typ() != starlarkTypeInt {
+ expr = &callExpr{
+ name: "int",
+ args: []starlarkExpr{expr},
+ returnType: starlarkTypeInt,
+ }
+ }
+ parsedArgs = append(parsedArgs, expr)
+ }
+ if len(parsedArgs) != expectedArgs {
+ return nil, fmt.Errorf("function should have %d arguments", expectedArgs)
+ }
+ return parsedArgs, nil
+}
+
+type mathComparisonCallParser struct {
+ op string
+}
+
+func (p *mathComparisonCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ parsedArgs, err := parseIntegerArguments(ctx, node, args, 2)
+ if err != nil {
+ return ctx.newBadExpr(node, err.Error())
+ }
+ return &binaryOpExpr{
+ left: parsedArgs[0],
+ right: parsedArgs[1],
+ op: p.op,
+ returnType: starlarkTypeBool,
+ }
+}
+
+type mathMaxOrMinCallParser struct {
+ function string
+}
+
+func (p *mathMaxOrMinCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ parsedArgs, err := parseIntegerArguments(ctx, node, args, 2)
+ if err != nil {
+ return ctx.newBadExpr(node, err.Error())
+ }
+ return &callExpr{
+ object: nil,
+ name: p.function,
+ args: parsedArgs,
+ returnType: starlarkTypeInt,
+ }
+}
+
func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr {
if mk.Const() {
return &stringLiteralExpr{mk.Dump()}
@@ -1414,70 +1637,92 @@
// If we reached here, it's neither string literal nor a simple variable,
// we need a full-blown interpolation node that will generate
// "a%b%c" % (X, Y) for a$(X)b$(Y)c
- xInterp := &interpolateExpr{args: make([]starlarkExpr, len(mk.Variables))}
- for i, ref := range mk.Variables {
- arg := ctx.parseReference(node, ref.Name)
- if x, ok := arg.(*badExpr); ok {
- return x
+ parts := make([]starlarkExpr, len(mk.Variables)+len(mk.Strings))
+ for i := 0; i < len(parts); i++ {
+ if i%2 == 0 {
+ parts[i] = &stringLiteralExpr{literal: mk.Strings[i/2]}
+ } else {
+ parts[i] = ctx.parseReference(node, mk.Variables[i/2].Name)
+ if x, ok := parts[i].(*badExpr); ok {
+ return x
+ }
}
- xInterp.args[i] = arg
}
- xInterp.chunks = append(xInterp.chunks, mk.Strings...)
- return xInterp
+ return NewInterpolateExpr(parts)
}
// Handles the statements whose treatment is the same in all contexts: comment,
// assignment, variable (which is a macro call in reality) and all constructs that
// do not handle in any context ('define directive and any unrecognized stuff).
-// Return true if we handled it.
-func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) bool {
- handled := true
+func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) []starlarkNode {
+ var result []starlarkNode
switch x := node.(type) {
case *mkparser.Comment:
- ctx.insertComment("#" + x.Comment)
+ if n, handled := ctx.maybeHandleAnnotation(x); handled && n != nil {
+ result = []starlarkNode{n}
+ } else if !handled {
+ result = []starlarkNode{&commentNode{strings.TrimSpace("#" + x.Comment)}}
+ }
case *mkparser.Assignment:
- ctx.handleAssignment(x)
+ result = ctx.handleAssignment(x)
case *mkparser.Variable:
- ctx.handleVariable(x)
+ result = ctx.handleVariable(x)
case *mkparser.Directive:
switch x.Name {
case "define":
- ctx.handleDefine(x)
+ if res := ctx.maybeHandleDefine(x); res != nil {
+ result = []starlarkNode{res}
+ }
case "include", "-include":
- ctx.handleInclude(node, ctx.parseMakeString(node, x.Args), x.Name[0] != '-')
+ result = ctx.handleInclude(node, ctx.parseMakeString(node, x.Args), x.Name[0] != '-')
+ case "ifeq", "ifneq", "ifdef", "ifndef":
+ result = []starlarkNode{ctx.handleIfBlock(x)}
default:
- handled = false
+ result = []starlarkNode{ctx.newBadNode(x, "unexpected directive %s", x.Name)}
}
default:
- ctx.errorf(x, "unsupported line %s", x.Dump())
+ result = []starlarkNode{ctx.newBadNode(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))}
}
- return handled
-}
-func (ctx *parseContext) insertComment(s string) {
- ctx.receiver.newNode(&commentNode{strings.TrimSpace(s)})
-}
-
-func (ctx *parseContext) carryAsComment(failedNode mkparser.Node) {
- for _, line := range strings.Split(failedNode.Dump(), "\n") {
- ctx.insertComment("# " + line)
+ // Clear the includeTops after each non-comment statement
+ // so that include annotations placed on certain statements don't apply
+ // globally for the rest of the makefile was well.
+ if _, wasComment := node.(*mkparser.Comment); !wasComment && len(ctx.includeTops) > 0 {
+ ctx.includeTops = []string{}
}
-}
-// records that the given node failed to be converted and includes an explanatory message
-func (ctx *parseContext) errorf(failedNode mkparser.Node, message string, args ...interface{}) {
- if ctx.errorLogger != nil {
- ctx.errorLogger.NewError(message, failedNode, args...)
+ if result == nil {
+ result = []starlarkNode{}
}
- message = fmt.Sprintf(message, args...)
- ctx.insertComment(fmt.Sprintf("# MK2RBC TRANSLATION ERROR: %s", message))
- ctx.carryAsComment(failedNode)
- ctx.script.hasErrors = true
+ return result
}
-func (ctx *parseContext) wrapBadExpr(xBad *badExpr) {
- ctx.insertComment(fmt.Sprintf("# MK2RBC TRANSLATION ERROR: %s", xBad.message))
- ctx.carryAsComment(xBad.node)
+// Processes annotation. An annotation is a comment that starts with #RBC# and provides
+// a conversion hint -- say, where to look for the dynamically calculated inherit/include
+// paths. Returns true if the comment was a successfully-handled annotation.
+func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) (starlarkNode, bool) {
+ maybeTrim := func(s, prefix string) (string, bool) {
+ if strings.HasPrefix(s, prefix) {
+ return strings.TrimSpace(strings.TrimPrefix(s, prefix)), true
+ }
+ return s, false
+ }
+ annotation, ok := maybeTrim(cnode.Comment, annotationCommentPrefix)
+ if !ok {
+ return nil, false
+ }
+ if p, ok := maybeTrim(annotation, "include_top"); ok {
+ // Don't allow duplicate include tops, because then we will generate
+ // invalid starlark code. (duplicate keys in the _entry dictionary)
+ for _, top := range ctx.includeTops {
+ if top == p {
+ return nil, true
+ }
+ }
+ ctx.includeTops = append(ctx.includeTops, p)
+ return nil, true
+ }
+ return ctx.newBadNode(cnode, "unsupported annotation %s", cnode.Comment), true
}
func (ctx *parseContext) loadedModulePath(path string) string {
@@ -1543,6 +1788,10 @@
return ok
}
+func (ctx *parseContext) errorLocation(node mkparser.Node) ErrorLocation {
+ return ErrorLocation{ctx.script.mkFile, ctx.script.nodeLocator(node.Pos())}
+}
+
func (ss *StarlarkScript) String() string {
return NewGenerateContext(ss).emit()
}
@@ -1581,13 +1830,13 @@
return nil, fmt.Errorf("bad makefile %s", req.MkFile)
}
starScript := &StarlarkScript{
- moduleName: moduleNameForFile(req.MkFile),
- mkFile: req.MkFile,
- topDir: req.RootDir,
- traceCalls: req.TraceCalls,
- warnPartialSuccess: req.WarnPartialSuccess,
- sourceFS: req.SourceFS,
- makefileFinder: req.MakefileFinder,
+ moduleName: moduleNameForFile(req.MkFile),
+ mkFile: req.MkFile,
+ traceCalls: req.TraceCalls,
+ sourceFS: req.SourceFS,
+ makefileFinder: req.MakefileFinder,
+ nodeLocator: func(pos mkparser.Pos) int { return parser.Unpack(pos).Line },
+ nodes: make([]starlarkNode, 0),
}
ctx := newParseContext(starScript, nodes)
ctx.outputSuffix = req.OutputSuffix
@@ -1599,23 +1848,8 @@
ctx.tracedVariables[v] = true
}
}
- ctx.pushReceiver(starScript)
for ctx.hasNodes() && ctx.fatalError == nil {
- node := ctx.getNode()
- if ctx.handleSimpleStatement(node) {
- continue
- }
- switch x := node.(type) {
- case *mkparser.Directive:
- switch x.Name {
- case "ifeq", "ifneq", "ifdef", "ifndef":
- ctx.handleIfBlock(x)
- default:
- ctx.errorf(x, "unexpected directive %s", x.Name)
- }
- default:
- ctx.errorf(x, "unsupported line")
- }
+ starScript.nodes = append(starScript.nodes, ctx.handleSimpleStatement(ctx.getNode())...)
}
if ctx.fatalError != nil {
return nil, ctx.fatalError
@@ -1623,12 +1857,23 @@
return starScript, nil
}
-func Launcher(mainModuleUri, versionDefaultsUri, mainModuleName string) string {
+func Launcher(mainModuleUri, inputVariablesUri, mainModuleName string) string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
- fmt.Fprintf(&buf, "load(%q, \"version_defaults\")\n", versionDefaultsUri)
+ fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri)
fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri)
- fmt.Fprintf(&buf, "%s(%s(%q, init, version_defaults))\n", cfnPrintVars, cfnMain, mainModuleName)
+ fmt.Fprintf(&buf, "%s(%s(%q, init, input_variables_init))\n", cfnPrintVars, cfnMain, mainModuleName)
+ return buf.String()
+}
+
+func BoardLauncher(mainModuleUri string, inputVariablesUri string) string {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
+ fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri)
+ fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri)
+ fmt.Fprintf(&buf, "globals, cfg, globals_base = %s(init, input_variables_init)\n", cfnBoardMain)
+ fmt.Fprintf(&buf, "# TODO: Some product config variables need to be printed, but most are readonly so we can't just print cfg here.\n")
+ fmt.Fprintf(&buf, "%s((globals, cfg, globals_base))\n", cfnPrintVars)
return buf.String()
}
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 434500b..447f658 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -105,15 +105,12 @@
PRODUCT_NAME := $(call foo1, bar)
PRODUCT_NAME := $(call foo0)
`,
- expected: `# MK2RBC TRANSLATION ERROR: cannot handle invoking foo1
-# PRODUCT_NAME := $(call foo1, bar)
-# MK2RBC TRANSLATION ERROR: cannot handle invoking foo0
-# PRODUCT_NAME := $(call foo0)
-load("//build/make/core:product_config.rbc", "rblf")
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
- rblf.warning("product.mk", "partially successful conversion")
+ rblf.mk2rbc_error("product.mk:2", "cannot handle invoking foo1")
+ rblf.mk2rbc_error("product.mk:3", "cannot handle invoking foo0")
`,
},
{
@@ -124,7 +121,7 @@
ifdef PRODUCT_NAME
$(call inherit-product, part1.mk)
else # Comment
-$(call inherit-product, $(LOCAL_PATH)/part1.mk)
+$(call inherit-product, $(LOCAL_PATH)/part.mk)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -134,11 +131,13 @@
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.inherit(handle, "part", _part_init)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
+ if not _part1_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
rblf.inherit(handle, "part1", _part1_init)
else:
# Comment
- rblf.inherit(handle, "part1", _part1_init)
+ rblf.inherit(handle, "part", _part_init)
`,
},
{
@@ -175,7 +174,9 @@
def init(g, handle):
cfg = rblf.cfg(handle)
_part_init(g, handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
+ if not _part1_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
_part1_init(g, handle)
else:
if _part1_init != None:
@@ -207,15 +208,11 @@
$(info foo)
endef
`,
- expected: `# MK2RBC TRANSLATION ERROR: define is not supported: some-macro
-# define some-macro
-# $(info foo)
-# endef
-load("//build/make/core:product_config.rbc", "rblf")
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
- rblf.warning("product.mk", "partially successful conversion")
+ rblf.mk2rbc_error("product.mk:2", "define is not supported: some-macro")
`,
},
{
@@ -226,15 +223,21 @@
PRODUCT_NAME = gizmo
else
endif
+local_var :=
+ifdef local_var
+endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_NAME"] = "gizmo"
else:
pass
+ _local_var = ""
+ if _local_var:
+ pass
`,
},
{
@@ -272,13 +275,11 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
# Comment
pass
else:
- # MK2RBC TRANSLATION ERROR: cannot set predefined variable TARGET_COPY_OUT_RECOVERY to "foo", its value should be "recovery"
- pass
- rblf.warning("product.mk", "partially successful conversion")
+ rblf.mk2rbc_error("product.mk:5", "cannot set predefined variable TARGET_COPY_OUT_RECOVERY to \"foo\", its value should be \"recovery\"")
`,
},
{
@@ -295,7 +296,7 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if not g.get("PRODUCT_NAME") != None:
+ if not cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_NAME"] = "gizmo1"
else:
cfg["PRODUCT_NAME"] = "gizmo2"
@@ -314,9 +315,9 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_NAME"] = "gizmo"
- elif not g.get("PRODUCT_PACKAGES") != None:
+ elif not cfg.get("PRODUCT_PACKAGES", []):
# Comment
pass
`,
@@ -347,6 +348,36 @@
`,
},
{
+ desc: "ifeq with soong_config_get",
+ mkname: "product.mk",
+ in: `
+ifeq (true,$(call soong_config_get,art_module,source_build))
+endif
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ if "true" == rblf.soong_config_get(g, "art_module", "source_build"):
+ pass
+`,
+ },
+ {
+ desc: "ifeq with $(NATIVE_COVERAGE)",
+ mkname: "product.mk",
+ in: `
+ifeq ($(NATIVE_COVERAGE),true)
+endif
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ if g.get("NATIVE_COVERAGE", False):
+ pass
+`,
+ },
+ {
desc: "Check filter result",
mkname: "product.mk",
in: `
@@ -358,22 +389,41 @@
endif
ifeq ($(TARGET_BUILD_VARIANT), $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
endif
+ifneq (, $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
+endif
+ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+endif
ifneq (,$(filter true, $(v1)$(v2)))
endif
+ifeq (,$(filter barbet coral%,$(TARGET_PRODUCT)))
+else ifneq (,$(filter barbet%,$(TARGET_PRODUCT)))
+endif
+ifeq (,$(filter-out sunfish_kasan, $(TARGET_PRODUCT)))
+endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
- if g["TARGET_BUILD_VARIANT"] not in ["userdebug", "eng"]:
+ if not rblf.filter("userdebug eng", g["TARGET_BUILD_VARIANT"]):
pass
- if g["TARGET_BUILD_VARIANT"] == "userdebug":
+ if rblf.filter("userdebug", g["TARGET_BUILD_VARIANT"]):
pass
if "plaf" in g.get("PLATFORM_LIST", []):
pass
+ if g["TARGET_BUILD_VARIANT"] == " ".join(rblf.filter(g["TARGET_BUILD_VARIANT"], "userdebug eng")):
+ pass
if g["TARGET_BUILD_VARIANT"] in ["userdebug", "eng"]:
pass
- if "%s%s" % (_v1, _v2) == "true":
+ if rblf.filter("userdebug eng", g["TARGET_BUILD_VARIANT"]):
+ pass
+ if rblf.filter("true", "%s%s" % (_v1, _v2)):
+ pass
+ if not rblf.filter("barbet coral%", g["TARGET_PRODUCT"]):
+ pass
+ elif rblf.filter("barbet%", g["TARGET_PRODUCT"]):
+ pass
+ if not rblf.filter_out("sunfish_kasan", g["TARGET_PRODUCT"]):
pass
`,
},
@@ -467,11 +517,11 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if g.get("PRODUCT_NAME") != None:
+ if cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_PACKAGES"] = ["pack-if0"]
- if g.get("PRODUCT_MODEL") != None:
+ if cfg.get("PRODUCT_MODEL", ""):
cfg["PRODUCT_PACKAGES"] = ["pack-if-if"]
- elif g.get("PRODUCT_NAME") != None:
+ elif cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_PACKAGES"] = ["pack-if-elif"]
else:
cfg["PRODUCT_PACKAGES"] = ["pack-if-else"]
@@ -502,6 +552,21 @@
`,
},
{
+ desc: "if with interpolation",
+ mkname: "product.mk",
+ in: `
+ifeq ($(VARIABLE1)text$(VARIABLE2),true)
+endif
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ if "%stext%s" % (g.get("VARIABLE1", ""), g.get("VARIABLE2", "")) == "true":
+ pass
+`,
+ },
+ {
desc: "ifneq $(X),true",
mkname: "product.mk",
in: `
@@ -545,9 +610,9 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- if g.get("TARGET_BOARD_PLATFORM", "") in ["msm8998"]:
+ if rblf.board_platform_in(g, "msm8998"):
pass
- elif g.get("TARGET_BOARD_PLATFORM", "") != "copper":
+ elif not rblf.board_platform_is(g, "copper"):
pass
elif g.get("TARGET_BOARD_PLATFORM", "") not in g["QCOM_BOARD_PLATFORMS"]:
pass
@@ -556,18 +621,66 @@
`,
},
{
- desc: "findstring call",
+ desc: "new is-board calls",
mkname: "product.mk",
in: `
-ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),)
+ifneq (,$(call is-board-platform-in-list2,msm8998 $(X))
+else ifeq (,$(call is-board-platform2,copper)
+else ifneq (,$(call is-vendor-board-qcom))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
+ if rblf.board_platform_in(g, "msm8998 %s" % g.get("X", "")):
+ pass
+ elif not rblf.board_platform_is(g, "copper"):
+ pass
+ elif g.get("TARGET_BOARD_PLATFORM", "") in g["QCOM_BOARD_PLATFORMS"]:
+ pass
+`,
+ },
+ {
+ desc: "findstring call",
+ mkname: "product.mk",
+ in: `
+result := $(findstring a,a b c)
+result := $(findstring b,x y z)
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ _result = rblf.findstring("a", "a b c")
+ _result = rblf.findstring("b", "x y z")
+`,
+ },
+ {
+ desc: "findstring in if statement",
+ mkname: "product.mk",
+ in: `
+ifeq ($(findstring foo,$(PRODUCT_PACKAGES)),)
+endif
+ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),)
+endif
+ifeq ($(findstring foo,$(PRODUCT_PACKAGES)),foo)
+endif
+ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),foo)
+endif
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") == -1:
+ pass
if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") != -1:
pass
+ if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") != -1:
+ pass
+ if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") == -1:
+ pass
`,
},
{
@@ -625,6 +738,7 @@
$(call enforce-product-packages-exist, foo)
$(call require-artifacts-in-path, foo, bar)
$(call require-artifacts-in-path-relaxed, foo, bar)
+$(call dist-for-goals, goal, from:to)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -634,6 +748,7 @@
rblf.enforce_product_packages_exist("foo")
rblf.require_artifacts_in_path("foo", "bar")
rblf.require_artifacts_in_path_relaxed("foo", "bar")
+ rblf.mkdist_for_goals(g, "goal", "from:to")
`,
},
{
@@ -663,7 +778,7 @@
PRODUCT_COPY_FILES := $(addprefix pfx-,a b c)
PRODUCT_COPY_FILES := $(addsuffix .sff, a b c)
PRODUCT_NAME := $(word 1, $(subst ., ,$(TARGET_BOARD_PLATFORM)))
-$(info $(patsubst %.pub,%,$(PRODUCT_ADB_KEYS)))
+$(info $(patsubst %.pub,$(PRODUCT_NAME)%,$(PRODUCT_ADB_KEYS)))
$(info $(dir foo/bar))
$(info $(firstword $(PRODUCT_COPY_FILES)))
$(info $(lastword $(PRODUCT_COPY_FILES)))
@@ -674,6 +789,8 @@
$(info $(notdir foo/bar))
$(call add_soong_config_namespace,snsconfig)
$(call add_soong_config_var_value,snsconfig,imagetype,odm_image)
+$(call soong_config_set, snsconfig, foo, foo_value)
+$(call soong_config_append, snsconfig, bar, bar_value)
PRODUCT_COPY_FILES := $(call copy-files,$(wildcard foo*.mk),etc)
PRODUCT_COPY_FILES := $(call product-copy-files-by-pattern,from/%,to/%,a b c)
`,
@@ -684,7 +801,7 @@
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]
- rblf.mkinfo("product.mk", rblf.mkpatsubst("%.pub", "%", g.get("PRODUCT_ADB_KEYS", "")))
+ rblf.mkinfo("product.mk", rblf.mkpatsubst("%.pub", "%s%%" % cfg["PRODUCT_NAME"], g.get("PRODUCT_ADB_KEYS", "")))
rblf.mkinfo("product.mk", rblf.dir("foo/bar"))
rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][0])
rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][-1])
@@ -693,8 +810,10 @@
rblf.mkinfo("product.mk", rblf.dir((_foobar).split()[-1]))
rblf.mkinfo("product.mk", rblf.abspath("foo/bar"))
rblf.mkinfo("product.mk", rblf.notdir("foo/bar"))
- rblf.add_soong_config_namespace(g, "snsconfig")
- rblf.add_soong_config_var_value(g, "snsconfig", "imagetype", "odm_image")
+ rblf.soong_config_namespace(g, "snsconfig")
+ rblf.soong_config_set(g, "snsconfig", "imagetype", "odm_image")
+ rblf.soong_config_set(g, "snsconfig", "foo", "foo_value")
+ rblf.soong_config_append(g, "snsconfig", "bar", "bar_value")
cfg["PRODUCT_COPY_FILES"] = rblf.copy_files(rblf.expand_wildcard("foo*.mk"), "etc")
cfg["PRODUCT_COPY_FILES"] = rblf.product_copy_files_by_pattern("from/%", "to/%", "a b c")
`,
@@ -777,17 +896,39 @@
in: `
SOONG_CONFIG_NAMESPACES += cvd
SOONG_CONFIG_cvd += launch_configs
-SOONG_CONFIG_cvd_launch_configs += cvd_config_auto.json
+SOONG_CONFIG_cvd_launch_configs = cvd_config_auto.json
SOONG_CONFIG_cvd += grub_config
SOONG_CONFIG_cvd_grub_config += grub.cfg
+x := $(SOONG_CONFIG_cvd_grub_config)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
- rblf.add_soong_config_namespace(g, "cvd")
- rblf.add_soong_config_var_value(g, "cvd", "launch_configs", "cvd_config_auto.json")
- rblf.add_soong_config_var_value(g, "cvd", "grub_config", "grub.cfg")
+ 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")
+`,
+ }, {
+ desc: "soong namespace accesses",
+ mkname: "product.mk",
+ in: `
+SOONG_CONFIG_NAMESPACES += cvd
+SOONG_CONFIG_cvd += launch_configs
+SOONG_CONFIG_cvd_launch_configs = cvd_config_auto.json
+SOONG_CONFIG_cvd += grub_config
+SOONG_CONFIG_cvd_grub_config += grub.cfg
+x := $(call soong_config_get,cvd,grub_config)
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ 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")
+ _x = rblf.soong_config_get(g, "cvd", "grub_config")
`,
},
{
@@ -904,7 +1045,7 @@
desc: "Dynamic inherit path",
mkname: "product.mk",
in: `
-MY_PATH=foo
+MY_PATH:=foo
$(call inherit-product,vendor/$(MY_PATH)/cfg.mk)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -915,15 +1056,274 @@
cfg = rblf.cfg(handle)
g["MY_PATH"] = "foo"
_entry = {
- "vendor/foo1/cfg.mk": ("_cfg", _cfg_init),
- "vendor/bar/baz/cfg.mk": ("_cfg1", _cfg1_init),
+ "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+ "vendor/bar/baz/cfg.mk": ("vendor/bar/baz/cfg", _cfg1_init),
}.get("vendor/%s/cfg.mk" % g["MY_PATH"])
(_varmod, _varmod_init) = _entry if _entry else (None, None)
if not _varmod_init:
- rblf.mkerror("cannot")
+ rblf.mkerror("product.mk", "Cannot find %s" % ("vendor/%s/cfg.mk" % g["MY_PATH"]))
rblf.inherit(handle, _varmod, _varmod_init)
`,
},
+ {
+ desc: "Dynamic inherit with hint",
+ mkname: "product.mk",
+ in: `
+MY_PATH:=foo
+#RBC# include_top vendor/foo1
+$(call inherit-product,$(MY_PATH)/cfg.mk)
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+load("//vendor/foo1:cfg.star|init", _cfg_init = "init")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["MY_PATH"] = "foo"
+ rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+`,
+ },
+ {
+ desc: "Dynamic inherit with duplicated hint",
+ mkname: "product.mk",
+ in: `
+MY_PATH:=foo
+#RBC# include_top vendor/foo1
+$(call inherit-product,$(MY_PATH)/cfg.mk)
+#RBC# include_top vendor/foo1
+#RBC# include_top vendor/foo1
+$(call inherit-product,$(MY_PATH)/cfg.mk)
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+load("//vendor/foo1:cfg.star|init", _cfg_init = "init")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["MY_PATH"] = "foo"
+ rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+ rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+`,
+ },
+ {
+ desc: "Dynamic inherit path that lacks hint",
+ mkname: "product.mk",
+ in: `
+#RBC# include_top foo
+$(call inherit-product,$(MY_VAR)/font.mk)
+
+#RBC# include_top foo
+
+# There's some space and even this comment between the include_top and the inherit-product
+
+$(call inherit-product,$(MY_VAR)/font.mk)
+
+$(call inherit-product,$(MY_VAR)/font.mk)
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+load("//foo:font.star|init", _font_init = "init")
+load("//bar:font.star|init", _font1_init = "init")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ rblf.inherit(handle, "foo/font", _font_init)
+ # There's some space and even this comment between the include_top and the inherit-product
+ rblf.inherit(handle, "foo/font", _font_init)
+ rblf.mkwarning("product.mk:11", "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
+ _entry = {
+ "foo/font.mk": ("foo/font", _font_init),
+ "bar/font.mk": ("bar/font", _font1_init),
+ }.get("%s/font.mk" % g.get("MY_VAR", ""))
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
+ rblf.inherit(handle, _varmod, _varmod_init)
+`,
+ },
+ {
+ desc: "Ignore make rules",
+ mkname: "product.mk",
+ in: `
+foo: 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 $@ $*")
+`,
+ },
+ {
+ desc: "Flag override",
+ mkname: "product.mk",
+ in: `
+override FOO:=`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ rblf.mk2rbc_error("product.mk:2", "cannot handle override directive")
+`,
+ },
+ {
+ desc: "Bad expression",
+ mkname: "build/product.mk",
+ in: `
+ifeq (,$(call foobar))
+endif
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ if rblf.mk2rbc_error("build/product.mk:2", "cannot handle invoking foobar"):
+ pass
+`,
+ },
+ {
+ desc: "if expression",
+ mkname: "product.mk",
+ in: `
+TEST_VAR := foo
+TEST_VAR_LIST := foo
+TEST_VAR_LIST += bar
+TEST_VAR_2 := $(if $(TEST_VAR),bar)
+TEST_VAR_3 := $(if $(TEST_VAR),bar,baz)
+TEST_VAR_3 := $(if $(TEST_VAR),$(TEST_VAR_LIST))
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["TEST_VAR"] = "foo"
+ g["TEST_VAR_LIST"] = ["foo"]
+ g["TEST_VAR_LIST"] += ["bar"]
+ g["TEST_VAR_2"] = ("bar" if g["TEST_VAR"] else "")
+ g["TEST_VAR_3"] = ("bar" if g["TEST_VAR"] else "baz")
+ g["TEST_VAR_3"] = (g["TEST_VAR_LIST"] if g["TEST_VAR"] else [])
+`,
+ },
+ {
+ desc: "substitution references",
+ mkname: "product.mk",
+ in: `
+SOURCES := foo.c bar.c
+OBJECTS := $(SOURCES:.c=.o)
+OBJECTS2 := $(SOURCES:%.c=%.o)
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["SOURCES"] = "foo.c bar.c"
+ g["OBJECTS"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"])
+ g["OBJECTS2"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"])
+`,
+ },
+ {
+ desc: "foreach expressions",
+ mkname: "product.mk",
+ in: `
+BOOT_KERNEL_MODULES := foo.ko bar.ko
+BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m))
+BOOT_KERNEL_MODULES_LIST := foo.ko
+BOOT_KERNEL_MODULES_LIST += bar.ko
+BOOT_KERNEL_MODULES_FILTER_2 := $(foreach m,$(BOOT_KERNEL_MODULES_LIST),%/$(m))
+
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["BOOT_KERNEL_MODULES"] = "foo.ko bar.ko"
+ g["BOOT_KERNEL_MODULES_FILTER"] = ["%%/%s" % m for m in rblf.words(g["BOOT_KERNEL_MODULES"])]
+ 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"]]
+`,
+ },
+ {
+ desc: "List appended to string",
+ mkname: "product.mk",
+ in: `
+NATIVE_BRIDGE_PRODUCT_PACKAGES := \
+ libnative_bridge_vdso.native_bridge \
+ native_bridge_guest_app_process.native_bridge \
+ native_bridge_guest_linker.native_bridge
+
+NATIVE_BRIDGE_MODIFIED_GUEST_LIBS := \
+ libaaudio \
+ libamidi \
+ libandroid \
+ libandroid_runtime
+
+NATIVE_BRIDGE_PRODUCT_PACKAGES += \
+ $(addsuffix .native_bridge,$(NATIVE_BRIDGE_ORIG_GUEST_LIBS))
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] = "libnative_bridge_vdso.native_bridge native_bridge_guest_app_process.native_bridge native_bridge_guest_linker.native_bridge"
+ g["NATIVE_BRIDGE_MODIFIED_GUEST_LIBS"] = "libaaudio libamidi libandroid libandroid_runtime"
+ g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] += " " + " ".join(rblf.addsuffix(".native_bridge", g.get("NATIVE_BRIDGE_ORIG_GUEST_LIBS", "")))
+`,
+ },
+ {
+ desc: "Math functions",
+ mkname: "product.mk",
+ in: `
+# Test the math functions defined in build/make/common/math.mk
+ifeq ($(call math_max,2,5),5)
+endif
+ifeq ($(call math_min,2,5),2)
+endif
+ifeq ($(call math_gt_or_eq,2,5),true)
+endif
+ifeq ($(call math_gt,2,5),true)
+endif
+ifeq ($(call math_lt,2,5),true)
+endif
+ifeq ($(call math_gt_or_eq,2,5),)
+endif
+ifeq ($(call math_gt,2,5),)
+endif
+ifeq ($(call math_lt,2,5),)
+endif
+ifeq ($(call math_gt_or_eq,$(MY_VAR), 5),true)
+endif
+ifeq ($(call math_gt_or_eq,$(MY_VAR),$(MY_OTHER_VAR)),true)
+endif
+ifeq ($(call math_gt_or_eq,100$(MY_VAR),10),true)
+endif
+`,
+ expected: `# Test the math functions defined in build/make/common/math.mk
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ if max(2, 5) == 5:
+ pass
+ if min(2, 5) == 2:
+ pass
+ if 2 >= 5:
+ pass
+ if 2 > 5:
+ pass
+ if 2 < 5:
+ pass
+ if 2 < 5:
+ pass
+ if 2 <= 5:
+ pass
+ if 2 >= 5:
+ pass
+ if int(g.get("MY_VAR", "")) >= 5:
+ pass
+ if int(g.get("MY_VAR", "")) >= int(g.get("MY_OTHER_VAR", "")):
+ pass
+ if int("100%s" % g.get("MY_VAR", "")) >= 10:
+ pass
+`,
+ },
}
var known_variables = []struct {
@@ -931,6 +1331,7 @@
class varClass
starlarkType
}{
+ {"NATIVE_COVERAGE", VarClassSoong, starlarkTypeBool},
{"PRODUCT_NAME", VarClassConfig, starlarkTypeString},
{"PRODUCT_MODEL", VarClassConfig, starlarkTypeString},
{"PRODUCT_PACKAGES", VarClassConfig, starlarkTypeList},
@@ -992,13 +1393,11 @@
t.Run(test.desc,
func(t *testing.T) {
ss, err := Convert(Request{
- MkFile: test.mkname,
- Reader: bytes.NewBufferString(test.in),
- RootDir: ".",
- OutputSuffix: ".star",
- WarnPartialSuccess: true,
- SourceFS: fs,
- MakefileFinder: &testMakefileFinder{fs: fs},
+ MkFile: test.mkname,
+ Reader: bytes.NewBufferString(test.in),
+ OutputSuffix: ".star",
+ SourceFS: fs,
+ MakefileFinder: &testMakefileFinder{fs: fs},
})
if err != nil {
t.Error(err)
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index 3fe1a17..5d98d7b 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -47,17 +47,23 @@
originalPath string // Makefile file path
moduleLocalName string
optional bool
+ missing bool // a module may not exist if a module that depends on it is loaded dynamically
}
func (im moduleInfo) entryName() string {
return im.moduleLocalName + "_init"
}
+func (mi moduleInfo) name() string {
+ return fmt.Sprintf("%q", MakePath2ModuleName(mi.originalPath))
+}
+
type inheritedModule interface {
name() string
entryName() string
emitSelect(gctx *generationContext)
- shouldExist() bool
+ pathExpr() starlarkExpr
+ needsLoadCheck() bool
}
type inheritedStaticModule struct {
@@ -65,21 +71,23 @@
loadAlways bool
}
-func (im inheritedStaticModule) name() string {
- return fmt.Sprintf("%q", MakePath2ModuleName(im.originalPath))
-}
-
func (im inheritedStaticModule) emitSelect(_ *generationContext) {
}
-func (im inheritedStaticModule) shouldExist() bool {
- return im.loadAlways
+func (im inheritedStaticModule) pathExpr() starlarkExpr {
+ return &stringLiteralExpr{im.path}
+}
+
+func (im inheritedStaticModule) needsLoadCheck() bool {
+ return im.missing
}
type inheritedDynamicModule struct {
path interpolateExpr
candidateModules []*moduleInfo
loadAlways bool
+ location ErrorLocation
+ needsWarning bool
}
func (i inheritedDynamicModule) name() string {
@@ -91,12 +99,16 @@
}
func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
+ if i.needsWarning {
+ gctx.newLine()
+ gctx.writef("%s.mkwarning(%q, %q)", baseName, i.location, "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
+ }
gctx.newLine()
gctx.writef("_entry = {")
gctx.indentLevel++
for _, mi := range i.candidateModules {
gctx.newLine()
- gctx.writef(`"%s": (%q, %s),`, mi.originalPath, mi.moduleLocalName, mi.entryName())
+ gctx.writef(`"%s": (%s, %s),`, mi.originalPath, mi.name(), mi.entryName())
}
gctx.indentLevel--
gctx.newLine()
@@ -105,18 +117,14 @@
gctx.write(")")
gctx.newLine()
gctx.writef("(%s, %s) = _entry if _entry else (None, None)", i.name(), i.entryName())
- if i.loadAlways {
- gctx.newLine()
- gctx.writef("if not %s:", i.entryName())
- gctx.indentLevel++
- gctx.newLine()
- gctx.write(`rblf.mkerror("cannot")`)
- gctx.indentLevel--
- }
}
-func (i inheritedDynamicModule) shouldExist() bool {
- return i.loadAlways
+func (i inheritedDynamicModule) pathExpr() starlarkExpr {
+ return &i.path
+}
+
+func (i inheritedDynamicModule) needsLoadCheck() bool {
+ return true
}
type inheritNode struct {
@@ -126,20 +134,22 @@
func (inn *inheritNode) emit(gctx *generationContext) {
// Unconditional case:
+ // maybe check that loaded
// rblf.inherit(handle, <module>, module_init)
// Conditional case:
// if <module>_init != None:
// same as above
inn.module.emitSelect(gctx)
-
name := inn.module.name()
entry := inn.module.entryName()
- gctx.newLine()
if inn.loadAlways {
+ gctx.emitLoadCheck(inn.module)
+ gctx.newLine()
gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
return
}
+ gctx.newLine()
gctx.writef("if %s:", entry)
gctx.indentLevel++
gctx.newLine()
@@ -155,12 +165,14 @@
func (inn *includeNode) emit(gctx *generationContext) {
inn.module.emitSelect(gctx)
entry := inn.module.entryName()
- gctx.newLine()
if inn.loadAlways {
+ gctx.emitLoadCheck(inn.module)
+ gctx.newLine()
gctx.writef("%s(g, handle)", entry)
return
}
+ gctx.newLine()
gctx.writef("if %s != None:", entry)
gctx.indentLevel++
gctx.newLine()
@@ -183,6 +195,7 @@
value starlarkExpr
mkValue *mkparser.MakeString
flavor assignmentFlavor
+ location ErrorLocation
isTraced bool
previous *assignmentNode
}
@@ -223,18 +236,6 @@
}
gctx.newLine()
- if bad, ok := in.expr.(*badExpr); ok {
- gctx.write("# MK2STAR ERROR converting:")
- gctx.newLine()
- gctx.writef("# %s", bad.node.Dump())
- gctx.newLine()
- gctx.writef("# %s", bad.message)
- gctx.newLine()
- // The init function emits a warning if the conversion was not
- // fullly successful, so here we (arbitrarily) take the false path.
- gctx.writef("%sFalse:", ifElif)
- return
- }
gctx.write(ifElif)
in.expr.emit(gctx)
gctx.write(":")
@@ -254,29 +255,17 @@
nodes []starlarkNode
}
-func (cb *switchCase) newNode(node starlarkNode) {
- cb.nodes = append(cb.nodes, node)
-}
-
func (cb *switchCase) emit(gctx *generationContext) {
cb.gate.emit(gctx)
gctx.indentLevel++
hasStatements := false
- emitNode := func(node starlarkNode) {
+ for _, node := range cb.nodes {
if _, ok := node.(*commentNode); !ok {
hasStatements = true
}
node.emit(gctx)
}
- if len(cb.nodes) > 0 {
- emitNode(cb.nodes[0])
- for _, node := range cb.nodes[1:] {
- emitNode(node)
- }
- if !hasStatements {
- gctx.emitPass()
- }
- } else {
+ if !hasStatements {
gctx.emitPass()
}
gctx.indentLevel--
@@ -287,22 +276,8 @@
ssCases []*switchCase
}
-func (ssw *switchNode) newNode(node starlarkNode) {
- switch br := node.(type) {
- case *switchCase:
- ssw.ssCases = append(ssw.ssCases, br)
- default:
- panic(fmt.Errorf("expected switchCase node, got %t", br))
- }
-}
-
func (ssw *switchNode) emit(gctx *generationContext) {
- if len(ssw.ssCases) == 0 {
- gctx.emitPass()
- } else {
- ssw.ssCases[0].emit(gctx)
- for _, ssCase := range ssw.ssCases[1:] {
- ssCase.emit(gctx)
- }
+ for _, ssCase := range ssw.ssCases {
+ ssCase.emit(gctx)
}
}
diff --git a/mk2rbc/types.go b/mk2rbc/types.go
index ebd52d8..46c6aa9 100644
--- a/mk2rbc/types.go
+++ b/mk2rbc/types.go
@@ -53,7 +53,7 @@
NewVariable(name string, varClass varClass, valueType starlarkType)
}
-// ScopeBase is a dummy implementation of the mkparser.Scope.
+// ScopeBase is a placeholder implementation of the mkparser.Scope.
// All our scopes are read-only and resolve only simple variables.
type ScopeBase struct{}
diff --git a/mk2rbc/variable.go b/mk2rbc/variable.go
index 4bb9ed5..f7adca5 100644
--- a/mk2rbc/variable.go
+++ b/mk2rbc/variable.go
@@ -81,10 +81,12 @@
emitAppend := func() {
pcv.emitGet(gctx, true)
gctx.write(" += ")
+ value := asgn.value
if pcv.valueType() == starlarkTypeString {
gctx.writef(`" " + `)
+ value = &toStringExpr{expr: value}
}
- asgn.value.emit(gctx)
+ value.emit(gctx)
}
switch asgn.flavor {
@@ -136,10 +138,12 @@
emitAppend := func() {
scv.emitGet(gctx, true)
gctx.write(" += ")
+ value := asgn.value
if scv.valueType() == starlarkTypeString {
gctx.writef(`" " + `)
+ value = &toStringExpr{expr: value}
}
- asgn.value.emit(gctx)
+ value.emit(gctx)
}
switch asgn.flavor {
@@ -177,8 +181,8 @@
baseVariable
}
-func (lv localVariable) emitDefined(_ *generationContext) {
- panic("implement me")
+func (lv localVariable) emitDefined(gctx *generationContext) {
+ gctx.writef(lv.String())
}
func (lv localVariable) String() string {
@@ -193,10 +197,12 @@
case asgnAppend:
lv.emitGet(gctx, false)
gctx.write(" += ")
+ value := asgn.value
if lv.valueType() == starlarkTypeString {
gctx.writef(`" " + `)
+ value = &toStringExpr{expr: value}
}
- asgn.value.emit(gctx)
+ value.emit(gctx)
case asgnMaybeAppend:
gctx.writef("%s(%q, ", cfnLocalAppend, lv)
asgn.value.emit(gctx)
@@ -228,10 +234,9 @@
if actualValue == expectedValue {
return
}
- gctx.writef("# MK2RBC TRANSLATION ERROR: cannot set predefined variable %s to %q, its value should be %q",
- pv.name(), actualValue, expectedValue)
- gctx.newLine()
- gctx.write("pass")
+ gctx.emitConversionError(asgn.location,
+ fmt.Sprintf("cannot set predefined variable %s to %q, its value should be %q",
+ pv.name(), actualValue, expectedValue))
gctx.starScript.hasErrors = true
return
}
diff --git a/mk2rbc/version_defaults.go b/mk2rbc/version_defaults.go
deleted file mode 100644
index 27e8198..0000000
--- a/mk2rbc/version_defaults.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2021 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package mk2rbc
-
-import (
- mkparser "android/soong/androidmk/parser"
- "bytes"
- "fmt"
- "io/ioutil"
- "os"
- "sort"
- "strconv"
- "strings"
-)
-
-const codenamePrefix = "PLATFORM_VERSION_CODENAME."
-
-// ParseVersionDefaults extracts version settings from the given file
-// and returns the map.
-func ParseVersionDefaults(path string) (map[string]string, error) {
- contents, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, err
- }
- parser := mkparser.NewParser(path, bytes.NewBuffer(contents))
- nodes, errs := parser.Parse()
- if len(errs) > 0 {
- for _, e := range errs {
- fmt.Fprintln(os.Stderr, "ERROR:", e)
- }
- return nil, fmt.Errorf("cannot parse %s", path)
- }
-
- result := map[string]string{
- "DEFAULT_PLATFORM_VERSION": "",
- "MAX_PLATFORM_VERSION": "",
- "MIN_PLATFORM_VERSION": "A",
- "PLATFORM_BASE_SDK_EXTENSION_VERSION": "",
- "PLATFORM_SDK_EXTENSION_VERSION": "",
- "PLATFORM_SDK_VERSION": "",
- "PLATFORM_SECURITY_PATCH": "",
- "PLATFORM_VERSION_LAST_STABLE": "",
- }
- for _, node := range nodes {
- asgn, ok := node.(*mkparser.Assignment)
- if !(ok && asgn.Name.Const()) {
- continue
- }
- s := asgn.Name.Strings[0]
- _, ok = result[s]
- if !ok {
- ok = strings.HasPrefix(s, codenamePrefix)
- }
- if !ok {
- continue
- }
- v := asgn.Value
- if !v.Const() {
- return nil, fmt.Errorf("the value of %s should be constant", s)
- }
- result[s] = strings.TrimSpace(v.Strings[0])
- }
- return result, nil
-}
-
-func genericValue(s string) interface{} {
- if ival, err := strconv.ParseInt(s, 0, 0); err == nil {
- return ival
- }
- return s
-}
-
-// VersionDefaults generates the contents of the version_defaults.rbc file
-func VersionDefaults(values map[string]string) string {
- var sink bytes.Buffer
- var lines []string
- var codenames []string
- for name, value := range values {
- if strings.HasPrefix(name, codenamePrefix) {
- codenames = append(codenames,
- fmt.Sprintf("%q: %q", strings.TrimPrefix(name, codenamePrefix), value))
- } else {
- // Print numbers as such
- lines = append(lines, fmt.Sprintf(" %s = %#v,\n",
- strings.ToLower(name), genericValue(value)))
- }
- }
- sort.Strings(lines)
- sink.WriteString("version_defaults = struct(\n")
- for _, l := range lines {
- sink.WriteString(l)
- }
- sink.WriteString(" codenames = { ")
- sink.WriteString(strings.Join(codenames, ", "))
- sink.WriteString(" }\n)\n")
- return sink.String()
-}
diff --git a/mk2rbc/version_defaults_test.go b/mk2rbc/version_defaults_test.go
deleted file mode 100644
index c78fa32..0000000
--- a/mk2rbc/version_defaults_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package mk2rbc
-
-import (
- "path/filepath"
- "reflect"
- "strings"
- "testing"
-)
-
-func TestParseVersionDefaults(t *testing.T) {
- testDir := getTestDirectory()
- abspath := func(relPath string) string { return filepath.Join(testDir, relPath) }
- actualProducts, err := ParseVersionDefaults(abspath("version_defaults.mk.test"))
- if err != nil {
- t.Fatal(err)
- }
- expectedProducts := map[string]string{
- "DEFAULT_PLATFORM_VERSION": "TP1A",
- "MAX_PLATFORM_VERSION": "TP1A",
- "MIN_PLATFORM_VERSION": "TP1A",
- "PLATFORM_BASE_SDK_EXTENSION_VERSION": "0",
- "PLATFORM_SDK_EXTENSION_VERSION": "1",
- "PLATFORM_SDK_VERSION": "31",
- "PLATFORM_SECURITY_PATCH": "2021-10-05",
- "PLATFORM_VERSION_LAST_STABLE": "12",
- "PLATFORM_VERSION_CODENAME.SP2A": "Sv2",
- "PLATFORM_VERSION_CODENAME.TP1A": "Tiramisu",
- }
- if !reflect.DeepEqual(actualProducts, expectedProducts) {
- t.Errorf("\nExpected: %v\n Actual: %v", expectedProducts, actualProducts)
- }
-}
-
-func TestVersionDefaults(t *testing.T) {
- testDir := getTestDirectory()
- abspath := func(relPath string) string { return filepath.Join(testDir, relPath) }
- actualProducts, err := ParseVersionDefaults(abspath("version_defaults.mk.test"))
- if err != nil {
- t.Fatal(err)
- }
- expectedString := `version_defaults = struct(
- default_platform_version = "TP1A",
- max_platform_version = "TP1A",
- min_platform_version = "TP1A",
- platform_base_sdk_extension_version = 0,
- platform_sdk_extension_version = 1,
- platform_sdk_version = 31,
- platform_security_patch = "2021-10-05",
- platform_version_last_stable = 12,
- codenames = { "SP2A": "Sv2", "TP1A": "Tiramisu" }
-)
-`
- actualString := VersionDefaults(actualProducts)
- if !reflect.DeepEqual(actualString, expectedString) {
- t.Errorf("\nExpected: %v\nActual:\n%v",
- strings.ReplaceAll(expectedString, "\n", "\n"),
- strings.ReplaceAll(actualString, "\n", "\n"))
- }
-
-}
diff --git a/python/androidmk.go b/python/androidmk.go
index 13b4172..233d867 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -75,16 +75,10 @@
}
func (installer *pythonInstaller) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- // Soong installation is only supported for host modules. Have Make
- // installation trigger Soong installation.
- if base.Target().Os.Class == android.Host {
- entries.OutputFile = android.OptionalPathForPath(installer.path)
- }
-
entries.Required = append(entries.Required, "libc++")
entries.ExtraEntries = append(entries.ExtraEntries,
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- path, file := filepath.Split(installer.path.ToMakePath().String())
+ path, file := filepath.Split(installer.path.String())
stem := strings.TrimSuffix(file, filepath.Ext(file))
entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
diff --git a/python/binary.go b/python/binary.go
index bf6167c..99c6259 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -27,7 +27,6 @@
func init() {
registerPythonBinaryComponents(android.InitRegistrationContext)
- android.RegisterBp2BuildMutator("python_binary_host", PythonBinaryBp2Build)
}
func registerPythonBinaryComponents(ctx android.RegistrationContext) {
@@ -35,29 +34,19 @@
}
type bazelPythonBinaryAttributes struct {
- Main string
+ Main *string
Srcs bazel.LabelListAttribute
Deps bazel.LabelListAttribute
- Python_version string
+ Python_version *string
}
-func PythonBinaryBp2Build(ctx android.TopDownMutatorContext) {
- m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build(ctx) {
- return
- }
-
- // a Module can be something other than a python_binary_host
- if ctx.ModuleType() != "python_binary_host" {
- return
- }
-
- var main 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
+ main = props.Main
break
}
}
@@ -69,13 +58,13 @@
// under Bionic.
py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
- var python_version string
+ 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 = "PY2"
+ python_version = &pyVersion2
} else {
// do nothing, since python_version defaults to PY3.
}
diff --git a/python/library.go b/python/library.go
index d136a4e..d026c13 100644
--- a/python/library.go
+++ b/python/library.go
@@ -17,17 +17,14 @@
// This file contains the module types for building Python library.
import (
- "fmt"
-
"android/soong/android"
"android/soong/bazel"
+
"github.com/google/blueprint/proptools"
)
func init() {
registerPythonLibraryComponents(android.InitRegistrationContext)
- android.RegisterBp2BuildMutator("python_library_host", PythonLibraryHostBp2Build)
- android.RegisterBp2BuildMutator("python_library", PythonLibraryBp2Build)
}
func registerPythonLibraryComponents(ctx android.RegistrationContext) {
@@ -46,43 +43,23 @@
type bazelPythonLibraryAttributes struct {
Srcs bazel.LabelListAttribute
Deps bazel.LabelListAttribute
- Srcs_version string
+ Srcs_version *string
}
-func PythonLibraryHostBp2Build(ctx android.TopDownMutatorContext) {
- pythonLibBp2Build(ctx, "python_library_host")
-}
-
-func PythonLibraryBp2Build(ctx android.TopDownMutatorContext) {
- pythonLibBp2Build(ctx, "python_library")
-}
-
-func pythonLibBp2Build(ctx android.TopDownMutatorContext, modType string) {
- m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build(ctx) {
- return
- }
-
- // a Module can be something other than a `modType`
- if ctx.ModuleType() != modType {
- return
- }
-
+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
+ var python_version *string
if py2Enabled && !py3Enabled {
- python_version = "PY2"
+ python_version = &pyVersion2
} else if !py2Enabled && py3Enabled {
- python_version = "PY3"
+ python_version = &pyVersion3
} else if !py2Enabled && !py3Enabled {
- panic(fmt.Errorf(
- "error for '%s' module: bp2build's %s converter doesn't understand having "+
- "neither py2 nor py3 enabled", m.Name(), modType))
+ ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
} else {
// do nothing, since python_version defaults to PY2ANDPY3
}
@@ -95,8 +72,8 @@
}
props := bazel.BazelTargetModuleProperties{
- // Use the native py_library rule.
- Rule_class: "py_library",
+ Rule_class: "py_library",
+ Bzl_load_location: "//build/bazel/rules/python:library.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{
diff --git a/python/python.go b/python/python.go
index 401d91f..734ac57 100644
--- a/python/python.go
+++ b/python/python.go
@@ -23,6 +23,7 @@
"strings"
"android/soong/bazel"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -667,18 +668,25 @@
}
// isPythonLibModule returns whether the given module is a Python library Module or not
-// This is distinguished by the fact that Python libraries are not installable, while other Python
-// modules are.
func isPythonLibModule(module blueprint.Module) bool {
if m, ok := module.(*Module); ok {
- // Python library has no bootstrapper or installer
- if m.bootstrapper == nil && m.installer == nil {
- return true
- }
+ return m.isLibrary()
}
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.
@@ -752,6 +760,14 @@
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/rust/Android.bp b/rust/Android.bp
index 0ee673d..3fd68e5 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -14,6 +14,7 @@
"soong-snapshot",
],
srcs: [
+ "afdo.go",
"androidmk.go",
"benchmark.go",
"binary.go",
@@ -38,6 +39,7 @@
"strip.go",
"test.go",
"testing.go",
+ "toolchain_library.go",
],
testSrcs: [
"benchmark_test.go",
@@ -54,6 +56,7 @@
"project_json_test.go",
"protobuf_test.go",
"rust_test.go",
+ "sanitize_test.go",
"source_provider_test.go",
"test_test.go",
"vendor_snapshot_test.go",
diff --git a/rust/OWNERS b/rust/OWNERS
index b5b795c..d07ef7e 100644
--- a/rust/OWNERS
+++ b/rust/OWNERS
@@ -2,4 +2,4 @@
per-file * = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
# Limited owners/reviewers of the allowed list.
-per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, jgalenson@google.com, mmaurer@google.com, srhines@google.com
+per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
diff --git a/rust/afdo.go b/rust/afdo.go
new file mode 100644
index 0000000..996fd7e
--- /dev/null
+++ b/rust/afdo.go
@@ -0,0 +1,48 @@
+// Copyright 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 rust
+
+import (
+ "fmt"
+
+ "android/soong/cc"
+)
+
+const afdoFlagFormat = "-Zprofile-sample-use=%s"
+
+type afdo struct {
+ Properties cc.AfdoProperties
+}
+
+func (afdo *afdo) props() []interface{} {
+ return []interface{}{&afdo.Properties}
+}
+
+func (afdo *afdo) flags(ctx 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)
+ flags.RustFlags = append(flags.RustFlags, profileUseFlag)
+
+ profileFilePath := profileFile.Path()
+ deps.AfdoProfiles = append(deps.AfdoProfiles, profileFilePath)
+ }
+ }
+ return flags, deps
+}
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 630805a..2361e03 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -50,8 +50,8 @@
}
ret := android.AndroidMkEntries{
- OutputFile: mod.unstrippedOutputFile,
- Include: "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
+ OutputFile: android.OptionalPathForPath(mod.UnstrippedOutputFile()),
+ Include: "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.AddStrings("LOCAL_RLIB_LIBRARIES", mod.Properties.AndroidMkRlibs...)
@@ -106,6 +106,9 @@
}
entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
+ if test.Properties.Data_bins != nil {
+ entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+ }
})
cc.AndroidMkWriteTestData(test.data, ret)
@@ -137,10 +140,15 @@
} else if library.shared() {
ret.Class = "SHARED_LIBRARIES"
}
-
if library.distFile.Valid() {
ret.DistFiles = android.MakeDefaultDistFiles(library.distFile.Path())
}
+ ret.ExtraEntries = append(ret.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ if library.tocFile.Valid() {
+ entries.SetString("LOCAL_SOONG_TOC", library.tocFile.String())
+ }
+ })
}
func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
@@ -181,19 +189,14 @@
return
}
- var unstrippedOutputFile android.OptionalPath
- // Soong installation is only supported for host modules. Have Make
- // installation trigger Soong installation.
- if ctx.Target().Os.Class == android.Host {
- ret.OutputFile = android.OptionalPathForPath(compiler.path)
- } else if compiler.strippedOutputFile.Valid() {
- unstrippedOutputFile = ret.OutputFile
+ if compiler.strippedOutputFile.Valid() {
ret.OutputFile = compiler.strippedOutputFile
}
+
ret.ExtraEntries = append(ret.ExtraEntries,
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetOptionalPath("LOCAL_SOONG_UNSTRIPPED_BINARY", unstrippedOutputFile)
- path, file := filepath.Split(compiler.path.ToMakePath().String())
+ entries.SetPath("LOCAL_SOONG_UNSTRIPPED_BINARY", compiler.unstrippedOutputFile)
+ path, file := filepath.Split(compiler.path.String())
stem, suffix, _ := android.SplitFileExt(file)
entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
entries.SetString("LOCAL_MODULE_PATH", path)
diff --git a/rust/binary.go b/rust/binary.go
index 2c3f548..0dc320e 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -31,6 +31,12 @@
Static_executable *bool `android:"arch_variant"`
}
+type binaryInterface interface {
+ binary() bool
+ staticallyLinked() bool
+ testBinary() bool
+}
+
type binaryDecorator struct {
*baseCompiler
stripper Stripper
@@ -86,14 +92,23 @@
func (binary *binaryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
deps = binary.baseCompiler.compilerDeps(ctx, deps)
+ static := Bool(binary.Properties.Static_executable)
if ctx.toolchain().Bionic() {
- deps = bionicDeps(ctx, deps, Bool(binary.Properties.Static_executable))
- if Bool(binary.Properties.Static_executable) {
- deps.CrtBegin = "crtbegin_static"
+ deps = bionicDeps(ctx, deps, static)
+ if static {
+ deps.CrtBegin = []string{"crtbegin_static"}
} else {
- deps.CrtBegin = "crtbegin_dynamic"
+ deps.CrtBegin = []string{"crtbegin_dynamic"}
}
- deps.CrtEnd = "crtend_android"
+ deps.CrtEnd = []string{"crtend_android"}
+ } else if ctx.Os() == android.LinuxMusl {
+ deps = muslDeps(ctx, deps, static)
+ if static {
+ deps.CrtBegin = []string{"libc_musl_crtbegin_static"}
+ } else {
+ deps.CrtBegin = []string{"libc_musl_crtbegin_dynamic", "musl_linker_script"}
+ }
+ deps.CrtEnd = []string{"libc_musl_crtend"}
}
return deps
@@ -117,20 +132,24 @@
fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
srcPath, _ := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
outputFile := android.PathForModuleOut(ctx, fileName)
+ ret := outputFile
flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
+ if binary.stripper.NeedsStrip(ctx) {
+ strippedOutputFile := outputFile
+ outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
+ binary.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile)
+
+ binary.baseCompiler.strippedOutputFile = android.OptionalPathForPath(strippedOutputFile)
+ }
+ binary.baseCompiler.unstrippedOutputFile = outputFile
+
TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile)
- if binary.stripper.NeedsStrip(ctx) {
- strippedOutputFile := android.PathForModuleOut(ctx, "stripped", fileName)
- binary.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile)
- binary.strippedOutputFile = android.OptionalPathForPath(strippedOutputFile)
- }
-
- return outputFile
+ return ret
}
func (binary *binaryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
@@ -155,3 +174,15 @@
}
return binary.baseCompiler.stdLinkage(ctx)
}
+
+func (binary *binaryDecorator) binary() bool {
+ return true
+}
+
+func (binary *binaryDecorator) staticallyLinked() bool {
+ return Bool(binary.Properties.Static_executable)
+}
+
+func (binary *binaryDecorator) testBinary() bool {
+ return false
+}
diff --git a/rust/binary_test.go b/rust/binary_test.go
index 968c0c1..7dac249 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -106,7 +106,7 @@
srcs: ["foo.rs"],
}`)
- fizzBuzz := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Output("fizz-buzz")
+ fizzBuzz := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Rule("rustc")
flags := fizzBuzz.Args["rustcFlags"]
if strings.Contains(flags, "--test") {
@@ -139,7 +139,7 @@
static_executable: true,
}`)
- fizzOut := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Output("fizz")
+ fizzOut := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc")
fizzMod := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)
flags := fizzOut.Args["rustcFlags"]
@@ -173,7 +173,7 @@
name: "libfoo",
}`)
- fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Output("fizz-buzz")
+ fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Rule("rustc")
linkFlags := fizzBuzz.Args["linkFlags"]
if !strings.Contains(linkFlags, "/libfoo.so") {
t.Errorf("missing shared dependency 'libfoo.so' in linkFlags: %#v", linkFlags)
@@ -197,15 +197,17 @@
`)
foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a")
- foo.Output("stripped/foo")
+ foo.Output("unstripped/foo")
+ foo.Output("foo")
+
// Check that the `cp` rules is using the stripped version as input.
cp := foo.Rule("android.Cp")
- if !strings.HasSuffix(cp.Input.String(), "stripped/foo") {
+ if strings.HasSuffix(cp.Input.String(), "unstripped/foo") {
t.Errorf("installed binary not based on stripped version: %v", cp.Input)
}
- fizzBar := ctx.ModuleForTests("bar", "android_arm64_armv8-a").MaybeOutput("stripped/bar")
+ fizzBar := ctx.ModuleForTests("bar", "android_arm64_armv8-a").MaybeOutput("unstripped/bar")
if fizzBar.Rule != nil {
- t.Errorf("stripped version of bar has been generated")
+ t.Errorf("unstripped binary exists, so stripped binary has incorrectly been generated")
}
}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 845f258..f4c337d 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -15,6 +15,7 @@
package rust
import (
+ "fmt"
"strings"
"github.com/google/blueprint"
@@ -29,7 +30,7 @@
defaultBindgenFlags = []string{""}
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
- bindgenClangVersion = "clang-r433403"
+ bindgenClangVersion = "clang-r437112b"
_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -147,6 +148,31 @@
cflags = append(cflags, strings.ReplaceAll(ccToolchain.Cflags(), "${config.", "${cc_config."))
cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainCflags(), "${config.", "${cc_config."))
+ if ctx.RustModule().UseVndk() {
+ cflags = append(cflags, "-D__ANDROID_VNDK__")
+ if ctx.RustModule().InVendor() {
+ cflags = append(cflags, "-D__ANDROID_VENDOR__")
+ } else if ctx.RustModule().InProduct() {
+ cflags = append(cflags, "-D__ANDROID_PRODUCT__")
+ }
+ }
+
+ if ctx.RustModule().InRecovery() {
+ cflags = append(cflags, "-D__ANDROID_RECOVERY__")
+ }
+
+ 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 {
+ cflags = append(cflags, "-D__ANDROID_NATIVE_BRIDGE__")
+ }
+
// Dependency clang flags and include paths
cflags = append(cflags, deps.depClangFlags...)
for _, include := range deps.depIncludePaths {
@@ -262,6 +288,8 @@
deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
if ctx.toolchain().Bionic() {
deps = bionicDeps(ctx, deps, false)
+ } else if ctx.Os() == android.LinuxMusl {
+ deps = muslDeps(ctx, deps, false)
}
deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
diff --git a/rust/builder.go b/rust/builder.go
index 426a569..00035b9 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -95,7 +95,7 @@
func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
- flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
+ flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin")
}
@@ -112,13 +112,13 @@
func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
- flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
+ flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib")
}
func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
- flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
+ flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib")
}
@@ -185,7 +185,7 @@
}
func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags,
- outputFile android.WritablePath, crate_type string) buildOutput {
+ outputFile android.WritablePath, crateType string) buildOutput {
var inputs android.Paths
var implicits android.Paths
@@ -204,7 +204,7 @@
// Collect rustc flags
rustcFlags = append(rustcFlags, flags.GlobalRustFlags...)
rustcFlags = append(rustcFlags, flags.RustFlags...)
- rustcFlags = append(rustcFlags, "--crate-type="+crate_type)
+ rustcFlags = append(rustcFlags, "--crate-type="+crateType)
if crateName != "" {
rustcFlags = append(rustcFlags, "--crate-name="+crateName)
}
@@ -216,6 +216,13 @@
// Suppress an implicit sysroot
rustcFlags = append(rustcFlags, "--sysroot=/dev/null")
+ // Enable incremental compilation if requested by user
+ if ctx.Config().IsEnvTrue("SOONG_RUSTC_INCREMENTAL") {
+ incrementalPath := android.PathForOutput(ctx, "rustc").String()
+
+ rustcFlags = append(rustcFlags, "-C incremental="+incrementalPath)
+ }
+
// Collect linker flags
linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
linkFlags = append(linkFlags, flags.LinkFlags...)
@@ -238,10 +245,10 @@
implicits = append(implicits, deps.StaticLibs...)
implicits = append(implicits, deps.SharedLibDeps...)
implicits = append(implicits, deps.srcProviderFiles...)
+ implicits = append(implicits, deps.AfdoProfiles...)
- if deps.CrtBegin.Valid() {
- implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
- }
+ implicits = append(implicits, deps.CrtBegin...)
+ implicits = append(implicits, deps.CrtEnd...)
if len(deps.SrcDeps) > 0 {
moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
@@ -311,8 +318,8 @@
"rustcFlags": strings.Join(rustcFlags, " "),
"linkFlags": strings.Join(linkFlags, " "),
"libFlags": strings.Join(libFlags, " "),
- "crtBegin": deps.CrtBegin.String(),
- "crtEnd": deps.CrtEnd.String(),
+ "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "),
+ "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "),
"envVars": strings.Join(envVars, " "),
},
})
@@ -361,7 +368,7 @@
Description: "rustdoc " + main.Rel(),
Output: docTimestampFile,
Input: main,
- Implicit: ctx.RustModule().unstrippedOutputFile.Path(),
+ Implicit: ctx.RustModule().UnstrippedOutputFile(),
Args: map[string]string{
"rustdocFlags": strings.Join(rustdocFlags, " "),
"outDir": docDir.String(),
diff --git a/rust/compiler.go b/rust/compiler.go
index 1ce71f6..c5d40f4 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -181,7 +181,11 @@
sanitize *sanitize
distFile android.OptionalPath
- // Stripped output file. If Valid(), this file will be installed instead of outputFile.
+
+ // unstripped output file.
+ unstrippedOutputFile android.Path
+
+ // stripped output file.
strippedOutputFile android.OptionalPath
// If a crate has a source-generated dependency, a copy of the source file
@@ -340,6 +344,10 @@
return String(compiler.Properties.Cargo_pkg_version)
}
+func (compiler *baseCompiler) unstrippedOutputFilePath() android.Path {
+ return compiler.unstrippedOutputFile
+}
+
func (compiler *baseCompiler) strippedOutputFilePath() android.OptionalPath {
return compiler.strippedOutputFile
}
@@ -355,9 +363,9 @@
if !Bool(compiler.Properties.No_stdlibs) {
for _, stdlib := range config.Stdlibs {
- // If we're building for the primary arch of the build host, use the compiler's stdlibs
- if ctx.Target().Os == ctx.Config().BuildOS {
- stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
+ // If we're building for the build host, use the prebuilt stdlibs
+ if ctx.Target().Os == android.Linux || ctx.Target().Os == android.Darwin {
+ stdlib = "prebuilt_" + stdlib
}
deps.Stdlibs = append(deps.Stdlibs, stdlib)
}
@@ -386,6 +394,20 @@
return deps
}
+func muslDeps(ctx DepsContext, deps Deps, static bool) Deps {
+ muslLibs := []string{"libc_musl"}
+ if static {
+ deps.StaticLibs = append(deps.StaticLibs, muslLibs...)
+ } else {
+ deps.SharedLibs = append(deps.SharedLibs, muslLibs...)
+ }
+ if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" {
+ deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins)
+ }
+
+ return deps
+}
+
func (compiler *baseCompiler) crateName() string {
return compiler.Properties.Crate_name
}
@@ -449,6 +471,10 @@
// Returns the Path for the main source file along with Paths for generated source files from modules listed in srcs.
func srcPathFromModuleSrcs(ctx ModuleContext, srcs []string) (android.Path, android.Paths) {
+ if len(srcs) == 0 {
+ ctx.PropertyErrorf("srcs", "srcs must not be empty")
+ }
+
// The srcs can contain strings with prefix ":".
// They are dependent modules of this module, with android.SourceDepTag.
// They are not the main source file compiled by rustc.
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index f589b69..ec6829a 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -98,6 +98,41 @@
}`)
}
+// Test that we reject _no_ source files.
+func TestEnforceMissingSourceFiles(t *testing.T) {
+
+ singleSrcError := "srcs must not be empty"
+
+ // Test libraries
+ testRustError(t, singleSrcError, `
+ rust_library_host {
+ name: "foo-bar-library",
+ crate_name: "foo",
+ }`)
+
+ // Test binaries
+ testRustError(t, singleSrcError, `
+ rust_binary_host {
+ name: "foo-bar-binary",
+ crate_name: "foo",
+ }`)
+
+ // Test proc_macros
+ testRustError(t, singleSrcError, `
+ rust_proc_macro {
+ name: "foo-bar-proc-macro",
+ crate_name: "foo",
+ }`)
+
+ // Test prebuilts
+ testRustError(t, singleSrcError, `
+ rust_prebuilt_dylib {
+ name: "foo-bar-prebuilt",
+ crate_name: "foo",
+ host_supported: true,
+ }`)
+}
+
// Test environment vars for Cargo compat are set.
func TestCargoCompat(t *testing.T) {
ctx := testRust(t, `
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index 5b121c3..7757c79 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -16,7 +16,7 @@
"lints.go",
"toolchain.go",
"allowed_list.go",
- "x86_darwin_host.go",
+ "darwin_host.go",
"x86_linux_bionic_host.go",
"x86_linux_host.go",
"x86_device.go",
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 47ca3a7..14fcb02 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -11,16 +11,20 @@
"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",
"prebuilts/rust",
- "system/bt",
"system/core/libstats/pull_rust",
"system/extras/profcollectd",
"system/extras/simpleperf",
@@ -32,6 +36,9 @@
"system/security",
"system/tools/aidl",
"tools/security/fuzzing/example_rust_fuzzer",
+ "tools/security/fuzzing/orphans",
+ "tools/security/remote_provisioning/cert_validator",
+ "tools/vendor",
"vendor/",
}
diff --git a/rust/config/x86_darwin_host.go b/rust/config/darwin_host.go
similarity index 67%
rename from rust/config/x86_darwin_host.go
rename to rust/config/darwin_host.go
index 8ff0dd4..03bea82 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/darwin_host.go
@@ -25,41 +25,64 @@
DarwinRustLinkFlags = []string{
"-B${cc_config.MacToolPath}",
}
+ darwinArm64Rustflags = []string{}
+ darwinArm64Linkflags = []string{}
darwinX8664Rustflags = []string{}
darwinX8664Linkflags = []string{}
)
func init() {
+ registerToolchainFactory(android.Darwin, android.Arm64, darwinArm64ToolchainFactory)
registerToolchainFactory(android.Darwin, android.X86_64, darwinX8664ToolchainFactory)
+
pctx.StaticVariable("DarwinToolchainRustFlags", strings.Join(DarwinRustFlags, " "))
pctx.StaticVariable("DarwinToolchainLinkFlags", strings.Join(DarwinRustLinkFlags, " "))
+
+ pctx.StaticVariable("DarwinToolchainArm64RustFlags", strings.Join(darwinArm64Rustflags, " "))
+ pctx.StaticVariable("DarwinToolchainArm64LinkFlags", strings.Join(darwinArm64Linkflags, " "))
pctx.StaticVariable("DarwinToolchainX8664RustFlags", strings.Join(darwinX8664Rustflags, " "))
pctx.StaticVariable("DarwinToolchainX8664LinkFlags", strings.Join(darwinX8664Linkflags, " "))
}
type toolchainDarwin struct {
+ toolchain64Bit
toolchainRustFlags string
toolchainLinkFlags string
}
-type toolchainDarwinX8664 struct {
- toolchain64Bit
+type toolchainDarwinArm64 struct {
toolchainDarwin
}
+type toolchainDarwinX8664 struct {
+ toolchainDarwin
+}
+
+func (toolchainDarwinArm64) Supported() bool {
+ return true
+}
+
func (toolchainDarwinX8664) Supported() bool {
return true
}
-func (toolchainDarwinX8664) Bionic() bool {
+func (toolchainDarwin) Bionic() bool {
return false
}
+func (t *toolchainDarwinArm64) Name() string {
+ return "arm64"
+}
+
func (t *toolchainDarwinX8664) Name() string {
return "x86_64"
}
+func (t *toolchainDarwinArm64) RustTriple() string {
+ return "aarch64-apple-darwin"
+}
+
func (t *toolchainDarwinX8664) RustTriple() string {
return "x86_64-apple-darwin"
}
@@ -76,6 +99,15 @@
return ".dylib"
}
+func (t *toolchainDarwinArm64) ToolchainLinkFlags() string {
+ // Prepend the lld flags from cc_config so we stay in sync with cc
+ return "${cc_config.DarwinLldflags} ${config.DarwinToolchainLinkFlags} ${config.DarwinToolchainArm64LinkFlags}"
+}
+
+func (t *toolchainDarwinArm64) ToolchainRustFlags() string {
+ return "${config.DarwinToolchainRustFlags} ${config.DarwinToolchainArm64RustFlags}"
+}
+
func (t *toolchainDarwinX8664) ToolchainLinkFlags() string {
// Prepend the lld flags from cc_config so we stay in sync with cc
return "${cc_config.DarwinLldflags} ${config.DarwinToolchainLinkFlags} ${config.DarwinToolchainX8664LinkFlags}"
@@ -85,8 +117,13 @@
return "${config.DarwinToolchainRustFlags} ${config.DarwinToolchainX8664RustFlags}"
}
+func darwinArm64ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainDarwinArm64Singleton
+}
+
func darwinX8664ToolchainFactory(arch android.Arch) Toolchain {
return toolchainDarwinX8664Singleton
}
+var toolchainDarwinArm64Singleton Toolchain = &toolchainDarwinArm64{}
var toolchainDarwinX8664Singleton Toolchain = &toolchainDarwinX8664{}
diff --git a/rust/config/global.go b/rust/config/global.go
index b163bb6..c1ce13f 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,9 +24,9 @@
var pctx = android.NewPackageContext("android/soong/rust/config")
var (
- RustDefaultVersion = "1.55.0"
+ RustDefaultVersion = "1.58.1"
RustDefaultBase = "prebuilts/rust/"
- DefaultEdition = "2018"
+ DefaultEdition = "2021"
Stdlibs = []string{
"libstd",
}
@@ -41,12 +41,13 @@
}
GlobalRustFlags = []string{
- "--remap-path-prefix $$(pwd)=",
+ "-Z remap-cwd-prefix=.",
"-C codegen-units=1",
"-C debuginfo=2",
"-C opt-level=3",
"-C relocation-model=pic",
"-C overflow-checks=on",
+ "-C force-unwind-tables=yes",
// Use v0 mangling to distinguish from C++ symbols
"-Z symbol-mangling-version=v0",
}
@@ -54,6 +55,8 @@
deviceGlobalRustFlags = []string{
"-C panic=abort",
"-Z link-native-libraries=no",
+ // Generate additional debug info for AutoFDO
+ "-Z debug-info-for-profiling",
}
deviceGlobalLinkFlags = []string{
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index c10afd8..7608349 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -22,12 +22,29 @@
var (
LinuxRustFlags = []string{}
+ LinuxMuslRustFlags = []string{
+ // disable rustc's builtin fallbacks for crt objects
+ "-C link_self_contained=no",
+ // force rustc to use a dynamic musl libc
+ "-C target-feature=-crt-static",
+ "-Z link-native-libraries=no",
+ }
LinuxRustLinkFlags = []string{
"-B${cc_config.ClangBin}",
"-fuse-ld=lld",
"-Wl,--undefined-version",
+ }
+ LinuxRustGlibcLinkFlags = []string{
"--sysroot ${cc_config.LinuxGccRoot}/sysroot",
}
+ LinuxRustMuslLinkFlags = []string{
+ "--sysroot /dev/null",
+ "-nodefaultlibs",
+ "-nostdlib",
+ "-Wl,--no-dynamic-linker",
+ // for unwind
+ "-lgcc", "-lgcc_eh",
+ }
linuxX86Rustflags = []string{}
linuxX86Linkflags = []string{}
linuxX8664Rustflags = []string{}
@@ -35,15 +52,17 @@
)
func init() {
- registerToolchainFactory(android.Linux, android.X86_64, linuxX8664ToolchainFactory)
- registerToolchainFactory(android.Linux, android.X86, linuxX86ToolchainFactory)
+ registerToolchainFactory(android.Linux, android.X86_64, linuxGlibcX8664ToolchainFactory)
+ registerToolchainFactory(android.Linux, android.X86, linuxGlibcX86ToolchainFactory)
- // TODO: musl rust support
- registerToolchainFactory(android.LinuxMusl, android.X86_64, linuxX8664ToolchainFactory)
- registerToolchainFactory(android.LinuxMusl, android.X86, linuxX86ToolchainFactory)
+ registerToolchainFactory(android.LinuxMusl, android.X86_64, linuxMuslX8664ToolchainFactory)
+ registerToolchainFactory(android.LinuxMusl, android.X86, linuxMuslX86ToolchainFactory)
pctx.StaticVariable("LinuxToolchainRustFlags", strings.Join(LinuxRustFlags, " "))
+ pctx.StaticVariable("LinuxMuslToolchainRustFlags", strings.Join(LinuxMuslRustFlags, " "))
pctx.StaticVariable("LinuxToolchainLinkFlags", strings.Join(LinuxRustLinkFlags, " "))
+ pctx.StaticVariable("LinuxGlibcToolchainLinkFlags", strings.Join(LinuxRustGlibcLinkFlags, " "))
+ pctx.StaticVariable("LinuxMuslToolchainLinkFlags", strings.Join(LinuxRustMuslLinkFlags, " "))
pctx.StaticVariable("LinuxToolchainX86RustFlags", strings.Join(linuxX86Rustflags, " "))
pctx.StaticVariable("LinuxToolchainX86LinkFlags", strings.Join(linuxX86Linkflags, " "))
pctx.StaticVariable("LinuxToolchainX8664RustFlags", strings.Join(linuxX8664Rustflags, " "))
@@ -51,19 +70,9 @@
}
-type toolchainLinux struct {
- toolchainRustFlags string
- toolchainLinkFlags string
-}
-
-type toolchainLinuxX86 struct {
- toolchain32Bit
- toolchainLinux
-}
-
+// Base 64-bit linux rust toolchain
type toolchainLinuxX8664 struct {
toolchain64Bit
- toolchainLinux
}
func (toolchainLinuxX8664) Supported() bool {
@@ -78,10 +87,6 @@
return "x86_64"
}
-func (t *toolchainLinuxX8664) RustTriple() string {
- return "x86_64-unknown-linux-gnu"
-}
-
func (t *toolchainLinuxX8664) ToolchainLinkFlags() string {
// Prepend the lld flags from cc_config so we stay in sync with cc
return "${cc_config.LinuxLldflags} ${cc_config.LinuxX8664Lldflags} " +
@@ -92,8 +97,49 @@
return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainX8664RustFlags}"
}
-func linuxX8664ToolchainFactory(arch android.Arch) Toolchain {
- return toolchainLinuxX8664Singleton
+// Specialization of the 64-bit linux rust toolchain for glibc. Adds the gnu rust triple and
+// sysroot linker flags.
+type toolchainLinuxGlibcX8664 struct {
+ toolchainLinuxX8664
+}
+
+func (t *toolchainLinuxX8664) RustTriple() string {
+ return "x86_64-unknown-linux-gnu"
+}
+
+func (t *toolchainLinuxGlibcX8664) ToolchainLinkFlags() string {
+ return t.toolchainLinuxX8664.ToolchainLinkFlags() + " " + "${config.LinuxGlibcToolchainLinkFlags}"
+}
+
+func linuxGlibcX8664ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxGlibcX8664Singleton
+}
+
+// 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 toolchainLinuxMuslX8664 struct {
+ toolchainLinuxX8664
+}
+
+func (t *toolchainLinuxMuslX8664) RustTriple() string {
+ return "x86_64-unknown-linux-musl"
+}
+
+func (t *toolchainLinuxMuslX8664) ToolchainLinkFlags() string {
+ return t.toolchainLinuxX8664.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslX8664) ToolchainRustFlags() string {
+ return t.toolchainLinuxX8664.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslX8664ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxMuslX8664Singleton
+}
+
+// Base 32-bit linux rust toolchain
+type toolchainLinuxX86 struct {
+ toolchain32Bit
}
func (toolchainLinuxX86) Supported() bool {
@@ -116,10 +162,6 @@
return "x86_64"
}
-func (t *toolchainLinuxX86) RustTriple() string {
- return "i686-unknown-linux-gnu"
-}
-
func (t *toolchainLinuxX86) ToolchainLinkFlags() string {
// Prepend the lld flags from cc_config so we stay in sync with cc
return "${cc_config.LinuxLldflags} ${cc_config.LinuxX86Lldflags} " +
@@ -130,9 +172,47 @@
return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainX86RustFlags}"
}
-func linuxX86ToolchainFactory(arch android.Arch) Toolchain {
- return toolchainLinuxX86Singleton
+// Specialization of the 32-bit linux rust toolchain for glibc. Adds the gnu rust triple and
+// sysroot linker flags.
+type toolchainLinuxGlibcX86 struct {
+ toolchainLinuxX86
}
-var toolchainLinuxX8664Singleton Toolchain = &toolchainLinuxX8664{}
-var toolchainLinuxX86Singleton Toolchain = &toolchainLinuxX86{}
+func (t *toolchainLinuxGlibcX86) RustTriple() string {
+ return "i686-unknown-linux-gnu"
+}
+
+func (t *toolchainLinuxGlibcX86) ToolchainLinkFlags() string {
+ return t.toolchainLinuxX86.ToolchainLinkFlags() + " " + "${config.LinuxGlibcToolchainLinkFlags}"
+}
+
+func linuxGlibcX86ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxGlibcX86Singleton
+}
+
+// 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 toolchainLinuxMuslX86 struct {
+ toolchainLinuxX86
+}
+
+func (t *toolchainLinuxMuslX86) RustTriple() string {
+ return "i686-unknown-linux-musl"
+}
+
+func (t *toolchainLinuxMuslX86) ToolchainLinkFlags() string {
+ return t.toolchainLinuxX86.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslX86) ToolchainRustFlags() string {
+ return t.toolchainLinuxX86.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslX86ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxMuslX86Singleton
+}
+
+var toolchainLinuxGlibcX8664Singleton Toolchain = &toolchainLinuxGlibcX8664{}
+var toolchainLinuxGlibcX86Singleton Toolchain = &toolchainLinuxGlibcX86{}
+var toolchainLinuxMuslX8664Singleton Toolchain = &toolchainLinuxMuslX8664{}
+var toolchainLinuxMuslX86Singleton Toolchain = &toolchainLinuxMuslX86{}
diff --git a/rust/coverage.go b/rust/coverage.go
index 8fdfa23..050b811 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -57,18 +57,7 @@
flags.RustFlags = append(flags.RustFlags,
"-Z instrument-coverage", "-g")
flags.LinkFlags = append(flags.LinkFlags,
- profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open",
- // Upstream LLVM change 6d2d3bd0a6 made
- // -z,start-stop-gc the default. It drops metadata
- // sections like __llvm_prf_data unless they are marked
- // SHF_GNU_RETAIN. https://reviews.llvm.org/D97448
- // marks generated sections, including __llvm_prf_data
- // as SHF_GNU_RETAIN. However this change is not in
- // the Rust toolchain. Since we link Rust libs with
- // new lld, we should use nostart-stop-gc until the
- // Rust toolchain updates past D97448.
- "-Wl,-z,nostart-stop-gc",
- )
+ profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
}
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 5fb56ff..55921ba 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -36,7 +36,7 @@
fuzzPackagedModule fuzz.FuzzPackagedModule
}
-var _ compiler = (*binaryDecorator)(nil)
+var _ compiler = (*fuzzDecorator)(nil)
// rust_binary produces a binary that is runnable on a device.
func RustFuzzFactory() android.Module {
@@ -111,6 +111,10 @@
// 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)
@@ -143,7 +147,13 @@
files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
// The executable.
- files = append(files, fuzz.FileToZip{rustModule.unstrippedOutputFile.Path(), ""})
+ 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 {
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index 2524f91..98be7c2 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -45,7 +45,7 @@
}
// Check that compiler flags are set appropriately .
- fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Output("fuzz_libtest")
+ fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
!strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov'") ||
!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
diff --git a/rust/library.go b/rust/library.go
index 38dae4d..62eaefd 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -102,6 +102,9 @@
sourceProvider SourceProvider
collectedSnapshotHeaders android.Paths
+
+ // table-of-contents file for cdylib crates to optimize out relinking when possible
+ tocFile android.OptionalPath
}
type libraryInterface interface {
@@ -137,12 +140,18 @@
BuildOnlyDylib()
BuildOnlyStatic()
BuildOnlyShared()
+
+ toc() android.OptionalPath
}
func (library *libraryDecorator) nativeCoverage() bool {
return true
}
+func (library *libraryDecorator) toc() android.OptionalPath {
+ return library.tocFile
+}
+
func (library *libraryDecorator) rlib() bool {
return library.MutatedProperties.VariantIsRlib
}
@@ -417,10 +426,16 @@
func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
deps = library.baseCompiler.compilerDeps(ctx, deps)
- if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
- deps = bionicDeps(ctx, deps, false)
- deps.CrtBegin = "crtbegin_so"
- deps.CrtEnd = "crtend_so"
+ if library.dylib() || library.shared() {
+ if ctx.toolchain().Bionic() {
+ deps = bionicDeps(ctx, deps, false)
+ deps.CrtBegin = []string{"crtbegin_so"}
+ deps.CrtEnd = []string{"crtend_so"}
+ } else if ctx.Os() == android.LinuxMusl {
+ deps = muslDeps(ctx, deps, false)
+ deps.CrtBegin = []string{"libc_musl_crtbegin_so"}
+ deps.CrtEnd = []string{"libc_musl_crtend_so"}
+ }
}
return deps
@@ -460,7 +475,7 @@
}
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
- var outputFile android.ModuleOutPath
+ var outputFile, ret android.ModuleOutPath
var fileName string
srcPath := library.srcPath(ctx, deps)
@@ -468,6 +483,34 @@
deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...)
}
+ // Calculate output filename
+ if library.rlib() {
+ fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
+ outputFile = android.PathForModuleOut(ctx, fileName)
+ ret = outputFile
+ } else if library.dylib() {
+ fileName = library.getStem(ctx) + ctx.toolchain().DylibSuffix()
+ outputFile = android.PathForModuleOut(ctx, fileName)
+ ret = outputFile
+ } else if library.static() {
+ fileName = library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
+ outputFile = android.PathForModuleOut(ctx, fileName)
+ ret = outputFile
+ } else if library.shared() {
+ fileName = library.sharedLibFilename(ctx)
+ outputFile = android.PathForModuleOut(ctx, fileName)
+ ret = outputFile
+ }
+
+ if !library.rlib() && !library.static() && library.stripper.NeedsStrip(ctx) {
+ strippedOutputFile := outputFile
+ outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
+ library.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile)
+
+ library.baseCompiler.strippedOutputFile = android.OptionalPathForPath(strippedOutputFile)
+ }
+ library.baseCompiler.unstrippedOutputFile = outputFile
+
flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
@@ -479,34 +522,17 @@
flags.RustFlags = append(flags.RustFlags, "-C prefer-dynamic")
}
+ // Call the appropriate builder for this library type
if library.rlib() {
- fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
- outputFile = android.PathForModuleOut(ctx, fileName)
-
TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile)
} else if library.dylib() {
- fileName = library.getStem(ctx) + ctx.toolchain().DylibSuffix()
- outputFile = android.PathForModuleOut(ctx, fileName)
-
TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile)
} else if library.static() {
- fileName = library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
- outputFile = android.PathForModuleOut(ctx, fileName)
-
TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile)
} else if library.shared() {
- fileName = library.sharedLibFilename(ctx)
- outputFile = android.PathForModuleOut(ctx, fileName)
-
TransformSrctoShared(ctx, srcPath, deps, flags, outputFile)
}
- if !library.rlib() && !library.static() && library.stripper.NeedsStrip(ctx) {
- strippedOutputFile := android.PathForModuleOut(ctx, "stripped", fileName)
- library.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile)
- library.strippedOutputFile = android.OptionalPathForPath(strippedOutputFile)
- }
-
if library.rlib() || library.dylib() {
library.flagExporter.exportLinkDirs(deps.linkDirs...)
library.flagExporter.exportLinkObjects(deps.linkObjects...)
@@ -519,9 +545,16 @@
}
if library.shared() {
+ // Optimize out relinking against shared libraries whose interface hasn't changed by
+ // depending on a table of contents file instead of the library itself.
+ tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.SharedLibSuffix()[1:]+".toc")
+ library.tocFile = android.OptionalPathForPath(tocFile)
+ cc.TransformSharedObjectToToc(ctx, outputFile, tocFile)
+
ctx.SetProvider(cc.SharedLibraryInfoProvider, cc.SharedLibraryInfo{
- SharedLibrary: outputFile,
- Target: ctx.Target(),
+ TableOfContents: android.OptionalPathForPath(tocFile),
+ SharedLibrary: outputFile,
+ Target: ctx.Target(),
})
}
@@ -536,7 +569,7 @@
library.flagExporter.setProvider(ctx)
- return outputFile
+ return ret
}
func (library *libraryDecorator) srcPath(ctx ModuleContext, deps PathDeps) android.Path {
@@ -654,6 +687,12 @@
}
variation := v.(*Module).ModuleBase.ImageVariation().Variation
+ if strings.HasPrefix(variation, cc.VendorVariationPrefix) {
+ // TODO(b/204303985)
+ // Disable vendor dylibs until they are supported
+ v.(*Module).Disable()
+ }
+
if strings.HasPrefix(variation, cc.VendorVariationPrefix) &&
m.HasVendorVariant() &&
!snapshot.IsVendorProprietaryModule(mctx) &&
@@ -734,9 +773,10 @@
// Glob together the headers from the modules include_dirs property
for _, path := range android.CopyOfPaths(l.includeDirs) {
dir := path.String()
- glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+ globDir := dir + "/**/*"
+ glob, err := ctx.GlobWithDeps(globDir, nil)
if err != nil {
- ctx.ModuleErrorf("glob failed: %#v", err)
+ ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
return
}
diff --git a/rust/library_test.go b/rust/library_test.go
index cb4ef7e..d78dcdd 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -37,10 +37,10 @@
}`)
// Test all variants are being built.
- libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Output("libfoo.rlib")
- libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
- libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Output("libfoo.ffi.a")
- libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Output("libfoo.ffi.so")
+ libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
+ libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+ libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc")
+ libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc")
rlibCrateType := "rlib"
dylibCrateType := "dylib"
@@ -78,7 +78,7 @@
crate_name: "foo",
}`)
- libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
+ libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
if !strings.Contains(libfooDylib.Args["rustcFlags"], "prefer-dynamic") {
t.Errorf("missing prefer-dynamic flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
@@ -94,7 +94,7 @@
crate_name: "foo",
}`)
- libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
+ libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
if !strings.Contains(libfooDylib.Args["rustcFlags"], "--cfg 'android_dylib'") {
t.Errorf("missing android_dylib cfg flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
@@ -148,7 +148,7 @@
libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared")
- libfooOutput := libfoo.Output("libfoo.so")
+ libfooOutput := libfoo.Rule("rustc")
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"])
@@ -160,6 +160,26 @@
}
}
+func TestSharedLibraryToc(t *testing.T) {
+ ctx := testRust(t, `
+ rust_ffi_shared {
+ name: "libfoo",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ }
+ cc_binary {
+ name: "fizzbuzz",
+ shared_libs: ["libfoo"],
+ }`)
+
+ fizzbuzz := ctx.ModuleForTests("fizzbuzz", "android_arm64_armv8-a").Rule("ld")
+
+ if !android.SuffixInList(fizzbuzz.Implicits.Strings(), "libfoo.so.toc") {
+ t.Errorf("missing expected libfoo.so.toc implicit dependency, instead found: %#v",
+ fizzbuzz.Implicits.Strings())
+ }
+}
+
func TestStaticLibraryLinkage(t *testing.T) {
ctx := testRust(t, `
rust_ffi_static {
@@ -242,16 +262,17 @@
`)
foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib")
- foo.Output("stripped/libfoo.dylib.so")
+ foo.Output("libfoo.dylib.so")
+ foo.Output("unstripped/libfoo.dylib.so")
// Check that the `cp` rule is using the stripped version as input.
cp := foo.Rule("android.Cp")
- if !strings.HasSuffix(cp.Input.String(), "stripped/libfoo.dylib.so") {
- t.Errorf("installed binary not based on stripped version: %v", cp.Input)
+ if strings.HasSuffix(cp.Input.String(), "unstripped/libfoo.dylib.so") {
+ t.Errorf("installed library not based on stripped version: %v", cp.Input)
}
- fizzBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeOutput("stripped/libbar.dylib.so")
+ fizzBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeOutput("unstripped/libbar.dylib.so")
if fizzBar.Rule != nil {
- t.Errorf("stripped version of bar has been generated")
+ t.Errorf("unstripped library exists, so stripped library has incorrectly been generated")
}
}
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 49f3c0f..6f17272 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -32,6 +32,8 @@
}
type prebuiltLibraryDecorator struct {
+ android.Prebuilt
+
*libraryDecorator
Properties PrebuiltProperties
}
@@ -54,6 +56,13 @@
return module.Init()
}
+func addSrcSupplier(module android.PrebuiltInterface, prebuilt *prebuiltLibraryDecorator) {
+ srcsSupplier := func(_ android.BaseModuleContext, _ android.Module) []string {
+ return prebuilt.prebuiltSrcs()
+ }
+ android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
+}
+
func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *prebuiltLibraryDecorator) {
module, library := NewRustLibrary(hod)
library.BuildOnlyRust()
@@ -62,6 +71,9 @@
libraryDecorator: library,
}
module.compiler = prebuilt
+
+ addSrcSupplier(module, prebuilt)
+
return module, prebuilt
}
@@ -73,6 +85,9 @@
libraryDecorator: library,
}
module.compiler = prebuilt
+
+ addSrcSupplier(module, prebuilt)
+
return module, prebuilt
}
@@ -84,6 +99,9 @@
libraryDecorator: library,
}
module.compiler = prebuilt
+
+ addSrcSupplier(module, prebuilt)
+
return module, prebuilt
}
@@ -100,6 +118,7 @@
if len(paths) > 0 {
ctx.PropertyErrorf("srcs", "prebuilt libraries can only have one entry in srcs (the prebuilt path)")
}
+ prebuilt.baseCompiler.unstrippedOutputFile = srcPath
return srcPath
}
@@ -129,3 +148,7 @@
return srcs
}
+
+func (prebuilt *prebuiltLibraryDecorator) prebuilt() *android.Prebuilt {
+ return &prebuilt.Prebuilt
+}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 804d79f..974c096 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -75,6 +75,7 @@
srcPath, _ := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
+ procMacro.baseCompiler.unstrippedOutputFile = outputFile
return outputFile
}
diff --git a/rust/project_json.go b/rust/project_json.go
index faa7db5..fe259d6 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -51,6 +51,7 @@
Deps []rustProjectDep `json:"deps"`
Cfg []string `json:"cfg"`
Env map[string]string `json:"env"`
+ ProcMacro bool `json:"is_proc_macro"`
}
type rustProjectJson struct {
@@ -208,6 +209,10 @@
comp = c.baseCompiler
case *testDecorator:
comp = c.binaryDecorator.baseCompiler
+ case *procMacroDecorator:
+ comp = c.baseCompiler
+ case *toolchainLibraryDecorator:
+ comp = c.baseCompiler
default:
return nil, nil, false
}
@@ -224,6 +229,8 @@
return 0, false
}
+ _, procMacro := rModule.compiler.(*procMacroDecorator)
+
crate := rustProjectCrate{
DisplayName: rModule.Name(),
RootModule: rootModule,
@@ -231,6 +238,7 @@
Deps: make([]rustProjectDep, 0),
Cfg: make([]string, 0),
Env: make(map[string]string),
+ ProcMacro: procMacro,
}
if comp.CargoOutDir().Valid() {
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index f7b6681..255b2e5 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -117,6 +117,58 @@
validateJsonCrates(t, jsonContent)
}
+func TestProjectJsonProcMacroDep(t *testing.T) {
+ bp := `
+ rust_proc_macro {
+ name: "libproc_macro",
+ srcs: ["a/src/lib.rs"],
+ crate_name: "proc_macro"
+ }
+ rust_library {
+ name: "librust",
+ srcs: ["b/src/lib.rs"],
+ crate_name: "rust",
+ proc_macros: ["libproc_macro"],
+ }
+ `
+ jsonContent := testProjectJson(t, bp)
+ crates := validateJsonCrates(t, jsonContent)
+ libproc_macro_count := 0
+ librust_count := 0
+ for _, c := range crates {
+ crate := validateCrate(t, c)
+ procMacro, ok := crate["is_proc_macro"].(bool)
+ if !ok {
+ t.Fatalf("Unexpected type for is_proc_macro: %v", crate["is_proc_macro"])
+ }
+
+ name, ok := crate["display_name"].(string)
+ if !ok {
+ t.Fatalf("Unexpected type for display_name: %v", crate["display_name"])
+ }
+
+ switch name {
+ case "libproc_macro":
+ libproc_macro_count += 1
+ if !procMacro {
+ t.Fatalf("'libproc_macro' is marked with is_proc_macro=false")
+ }
+ case "librust":
+ librust_count += 1
+ if procMacro {
+ t.Fatalf("'librust' is not a proc macro crate, but is marked with is_proc_macro=true")
+ }
+ default:
+ break
+ }
+ }
+
+ if libproc_macro_count != 1 || librust_count != 1 {
+ t.Fatalf("Unexpected crate counts: libproc_macro_count: %v, librust_count: %v",
+ libproc_macro_count, librust_count)
+ }
+}
+
func TestProjectJsonFeature(t *testing.T) {
bp := `
rust_library {
diff --git a/rust/protobuf.go b/rust/protobuf.go
index b91fea8..9fe27c4c 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -188,6 +188,12 @@
lines,
"pub mod empty {",
" pub use protobuf::well_known_types::Empty;",
+ "}",
+ "pub mod wrappers {",
+ " pub use protobuf::well_known_types::{",
+ " DoubleValue, FloatValue, Int64Value, UInt64Value, Int32Value, UInt32Value,",
+ " BoolValue, StringValue, BytesValue",
+ " };",
"}")
}
diff --git a/rust/rust.go b/rust/rust.go
index b9afc7f..f40f1a8 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -130,9 +130,10 @@
// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
Min_sdk_version *string
- PreventInstall bool
- HideFromMake bool
- Installable *bool
+ HideFromMake bool `blueprint:"mutated"`
+ PreventInstall bool `blueprint:"mutated"`
+
+ Installable *bool
}
type Module struct {
@@ -147,6 +148,7 @@
makeLinkType string
+ afdo *afdo
compiler compiler
coverage *coverage
clippy *clippy
@@ -155,13 +157,15 @@
sourceProvider SourceProvider
subAndroidMkOnce map[SubAndroidMkProvider]bool
- // Unstripped output. This is usually used when this module is linked to another module
- // as a library. The stripped output which is used for installation can be found via
- // compiler.strippedOutputFile if it exists.
- unstrippedOutputFile android.OptionalPath
- docTimestampFile android.OptionalPath
+ // Output file to be installed, may be stripped or unstripped.
+ outputFile android.OptionalPath
+
+ docTimestampFile android.OptionalPath
hideApexVariantFromMake bool
+
+ // For apex variants, this is set as apex.min_sdk_version
+ apexSdkVersion android.ApiLevel
}
func (mod *Module) Header() bool {
@@ -177,8 +181,8 @@
mod.Properties.HideFromMake = true
}
-func (c *Module) HiddenFromMake() bool {
- return c.Properties.HideFromMake
+func (mod *Module) HiddenFromMake() bool {
+ return mod.Properties.HideFromMake
}
func (mod *Module) SanitizePropDefined() bool {
@@ -260,10 +264,8 @@
}
func (mod *Module) Binary() bool {
- if mod.compiler != nil {
- if _, ok := mod.compiler.(*binaryDecorator); ok {
- return true
- }
+ if binary, ok := mod.compiler.(binaryInterface); ok {
+ return binary.binary()
}
return false
}
@@ -272,7 +274,7 @@
if !mod.Binary() {
return false
}
- return Bool(mod.compiler.(*binaryDecorator).Properties.Static_executable)
+ return mod.StaticallyLinked()
}
func (mod *Module) Object() bool {
@@ -282,8 +284,8 @@
func (mod *Module) Toc() android.OptionalPath {
if mod.compiler != nil {
- if _, ok := mod.compiler.(libraryInterface); ok {
- return android.OptionalPath{}
+ if lib, ok := mod.compiler.(libraryInterface); ok {
+ return lib.toc()
}
}
panic(fmt.Errorf("Toc() called on non-library module: %q", mod.BaseModuleName()))
@@ -365,10 +367,6 @@
return ""
}
-func (mod *Module) MinSdkVersion() string {
- return ""
-}
-
func (mod *Module) AlwaysSdk() bool {
return false
}
@@ -392,7 +390,11 @@
WholeStaticLibs []string
HeaderLibs []string
- CrtBegin, CrtEnd string
+ // Used for data dependencies adjacent to tests
+ DataLibs []string
+ DataBins []string
+
+ CrtBegin, CrtEnd []string
}
type PathDeps struct {
@@ -402,6 +404,7 @@
SharedLibDeps android.Paths
StaticLibs android.Paths
ProcMacros RustLibraries
+ AfdoProfiles android.Paths
// depFlags and depLinkFlags are rustc and linker (clang) flags.
depFlags []string
@@ -418,8 +421,8 @@
depGeneratedHeaders android.Paths
depSystemIncludePaths android.Paths
- CrtBegin android.OptionalPath
- CrtEnd android.OptionalPath
+ CrtBegin android.Paths
+ CrtEnd android.Paths
// Paths to generated source files
SrcDeps android.Paths
@@ -466,6 +469,7 @@
stdLinkage(ctx *depsContext) RustLinkage
+ unstrippedOutputFilePath() android.Path
strippedOutputFilePath() android.OptionalPath
}
@@ -526,10 +530,6 @@
return mod.Properties.PreventInstall
}
-func (mod *Module) HideFromMake() {
- mod.Properties.HideFromMake = true
-}
-
func (mod *Module) MarkAsCoverageVariant(coverage bool) {
mod.coverage.Properties.IsCoverageVariant = coverage
}
@@ -553,6 +553,7 @@
module.AddProperties(props...)
module.AddProperties(
&BaseProperties{},
+ &cc.AfdoProperties{},
&cc.VendorProperties{},
&BenchmarkProperties{},
&BindgenProperties{},
@@ -579,7 +580,7 @@
func (mod *Module) CcLibrary() bool {
if mod.compiler != nil {
- if _, ok := mod.compiler.(*libraryDecorator); ok {
+ if _, ok := mod.compiler.(libraryInterface); ok {
return true
}
}
@@ -597,6 +598,13 @@
return false
}
+func (mod *Module) UnstrippedOutputFile() android.Path {
+ if mod.compiler != nil {
+ return mod.compiler.unstrippedOutputFilePath()
+ }
+ return nil
+}
+
func (mod *Module) IncludeDirs() android.Paths {
if mod.compiler != nil {
if library, ok := mod.compiler.(*libraryDecorator); ok {
@@ -649,10 +657,7 @@
}
func (mod *Module) OutputFile() android.OptionalPath {
- if mod.compiler != nil && mod.compiler.strippedOutputFilePath().Valid() {
- return mod.compiler.strippedOutputFilePath()
- }
- return mod.unstrippedOutputFile
+ return mod.outputFile
}
func (mod *Module) CoverageFiles() android.Paths {
@@ -676,12 +681,19 @@
return mod.OutputFile().Valid() && !mod.Properties.PreventInstall
}
+func (ctx moduleContext) apexVariationName() string {
+ return ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).ApexVariationName
+}
+
var _ cc.LinkableInterface = (*Module)(nil)
func (mod *Module) Init() android.Module {
mod.AddProperties(&mod.Properties)
mod.AddProperties(&mod.VendorProperties)
+ if mod.afdo != nil {
+ mod.AddProperties(mod.afdo.props()...)
+ }
if mod.compiler != nil {
mod.AddProperties(mod.compiler.compilerProps()...)
}
@@ -713,6 +725,7 @@
}
func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
module := newBaseModule(hod, multilib)
+ module.afdo = &afdo{}
module.coverage = &coverage{}
module.clippy = &clippy{}
module.sanitize = &sanitize{}
@@ -850,6 +863,9 @@
}
// Calculate rustc flags
+ if mod.afdo != nil {
+ flags, deps = mod.afdo.flags(ctx, flags, deps)
+ }
if mod.compiler != nil {
flags = mod.compiler.compilerFlags(ctx, flags)
flags = mod.compiler.cfgFlags(ctx, flags)
@@ -883,11 +899,17 @@
if mod.compiler != nil && !mod.compiler.Disabled() {
mod.compiler.initialize(ctx)
- unstrippedOutputFile := mod.compiler.compile(ctx, flags, deps)
- mod.unstrippedOutputFile = android.OptionalPathForPath(unstrippedOutputFile)
- bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), mod.unstrippedOutputFile)
+ outputFile := mod.compiler.compile(ctx, flags, deps)
+ if ctx.Failed() {
+ return
+ }
+ mod.outputFile = android.OptionalPathForPath(outputFile)
+ bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), android.OptionalPathForPath(mod.compiler.unstrippedOutputFilePath()))
mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
+ if mod.docTimestampFile.Valid() {
+ ctx.CheckbuildFile(mod.docTimestampFile.Path())
+ }
// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
// RECOVERY_SNAPSHOT_VERSION is current.
@@ -898,8 +920,24 @@
}
apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- if mod.installable(apexInfo) {
+ if !proptools.BoolDefault(mod.Installable(), mod.EverInstallable()) {
+ // If the module has been specifically configure to not be installed then
+ // hide from make as otherwise it will break when running inside make as the
+ // output path to install will not be specified. Not all uninstallable
+ // modules can be hidden from make as some are needed for resolving make
+ // side dependencies.
+ mod.HideFromMake()
+ } else if !mod.installable(apexInfo) {
+ mod.SkipInstall()
+ }
+
+ // Still call install though, the installs will be stored as PackageSpecs to allow
+ // using the outputs in a genrule.
+ if mod.OutputFile().Valid() {
mod.compiler.install(ctx)
+ if ctx.Failed() {
+ return
+ }
}
ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
@@ -940,6 +978,7 @@
name string
library bool
procMacro bool
+ dynamic bool
}
// InstallDepNeeded returns true for rlibs, dylibs, and proc macros so that they or their transitive
@@ -950,13 +989,24 @@
var _ android.InstallNeededDependencyTag = dependencyTag{}
+func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
+ if d.library && d.dynamic {
+ return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
+ }
+ return nil
+}
+
+var _ android.LicenseAnnotationsDependencyTag = dependencyTag{}
+
var (
customBindgenDepTag = dependencyTag{name: "customBindgenTag"}
rlibDepTag = dependencyTag{name: "rlibTag", library: true}
- dylibDepTag = dependencyTag{name: "dylib", library: true}
+ dylibDepTag = dependencyTag{name: "dylib", library: true, dynamic: true}
procMacroDepTag = dependencyTag{name: "procMacro", procMacro: true}
testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"}
sourceDepTag = dependencyTag{name: "source"}
+ dataLibDepTag = dependencyTag{name: "data lib"}
+ dataBinDepTag = dependencyTag{name: "data bin"}
)
func IsDylibDepTag(depTag blueprint.DependencyTag) bool {
@@ -994,6 +1044,13 @@
}
}
+func (mod *Module) Prebuilt() *android.Prebuilt {
+ if p, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
+ return p.prebuilt()
+ }
+ return nil
+}
+
func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
var depPaths PathDeps
@@ -1005,6 +1062,20 @@
directSrcProvidersDeps := []*Module{}
directSrcDeps := [](android.SourceFileProducer){}
+ // For the dependency from platform to apex, use the latest stubs
+ mod.apexSdkVersion = android.FutureApiLevel
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if !apexInfo.IsForPlatform() {
+ mod.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)
+ mod.apexSdkVersion = android.FutureApiLevel
+ }
+
ctx.VisitDirectDeps(func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
depTag := ctx.OtherModuleDependencyTag(dep)
@@ -1065,13 +1136,8 @@
}
if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
- linkFile := rustDep.unstrippedOutputFile
- if !linkFile.Valid() {
- ctx.ModuleErrorf("Invalid output file when adding dep %q to %q",
- depName, ctx.ModuleName())
- return
- }
- linkDir := linkPathFromFilePath(linkFile.Path())
+ linkFile := rustDep.UnstrippedOutputFile()
+ linkDir := linkPathFromFilePath(linkFile)
if lib, ok := mod.compiler.(exportedFlagsProducer); ok {
lib.exportLinkDirs(linkDir)
}
@@ -1103,7 +1169,12 @@
if cc.IsWholeStaticLib(depTag) {
// rustc will bundle static libraries when they're passed with "-lstatic=<lib>". This will fail
// if the library is not prefixed by "lib".
- if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
+ if mod.Binary() {
+ // Binaries may sometimes need to link whole static libraries that don't start with 'lib'.
+ // Since binaries don't need to 'rebundle' these like libraries and only use these for the
+ // final linkage, pass the args directly to the linker to handle these cases.
+ 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)
} 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())
@@ -1121,6 +1192,10 @@
depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
directStaticLibDeps = append(directStaticLibDeps, ccDep)
+
+ // Record baseLibName for snapshots.
+ mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
+
mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName)
case cc.IsSharedDepTag(depTag):
// For the shared lib dependencies, we may link to the stub variant
@@ -1143,7 +1218,6 @@
// Record baseLibName for snapshots.
mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
- mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
exportDep = true
@@ -1153,9 +1227,9 @@
depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
case depTag == cc.CrtBeginDepTag:
- depPaths.CrtBegin = linkObject
+ depPaths.CrtBegin = append(depPaths.CrtBegin, linkObject.Path())
case depTag == cc.CrtEndDepTag:
- depPaths.CrtEnd = linkObject
+ depPaths.CrtEnd = append(depPaths.CrtEnd, linkObject.Path())
}
// Make sure these dependencies are propagated
@@ -1163,6 +1237,13 @@
lib.exportLinkDirs(linkPath)
lib.exportLinkObjects(linkObject.String())
}
+ } else {
+ switch {
+ case depTag == cc.CrtBeginDepTag:
+ depPaths.CrtBegin = append(depPaths.CrtBegin, android.OutputFileForModule(ctx, dep, ""))
+ case depTag == cc.CrtEndDepTag:
+ depPaths.CrtEnd = append(depPaths.CrtEnd, android.OutputFileForModule(ctx, dep, ""))
+ }
}
if srcDep, ok := dep.(android.SourceFileProducer); ok {
@@ -1175,15 +1256,15 @@
var rlibDepFiles RustLibraries
for _, dep := range directRlibDeps {
- rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.unstrippedOutputFile.Path(), CrateName: dep.CrateName()})
+ rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
}
var dylibDepFiles RustLibraries
for _, dep := range directDylibDeps {
- dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.unstrippedOutputFile.Path(), CrateName: dep.CrateName()})
+ dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
}
var procMacroDepFiles RustLibraries
for _, dep := range directProcMacroDeps {
- procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.unstrippedOutputFile.Path(), CrateName: dep.CrateName()})
+ procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
}
var staticLibDepFiles android.Paths
@@ -1361,13 +1442,13 @@
actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
crtVariations := cc.GetCrtVariations(ctx, mod)
- if deps.CrtBegin != "" {
+ for _, crt := range deps.CrtBegin {
actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag,
- cc.RewriteSnapshotLib(deps.CrtBegin, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+ cc.RewriteSnapshotLib(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
}
- if deps.CrtEnd != "" {
+ for _, crt := range deps.CrtEnd {
actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag,
- cc.RewriteSnapshotLib(deps.CrtEnd, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+ cc.RewriteSnapshotLib(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
}
if mod.sourceProvider != nil {
@@ -1378,6 +1459,12 @@
}
}
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "shared"},
+ }, dataLibDepTag, deps.DataLibs...)
+
+ actx.AddVariationDependencies(nil, dataBinDepTag, deps.DataBins...)
+
// 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...)
}
@@ -1426,15 +1513,13 @@
var _ android.ApexModule = (*Module)(nil)
-func (mod *Module) minSdkVersion() string {
+func (mod *Module) MinSdkVersion() string {
return String(mod.Properties.Min_sdk_version)
}
-var _ android.ApexModule = (*Module)(nil)
-
// Implements android.ApexModule
func (mod *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
- minSdkVersion := mod.minSdkVersion()
+ minSdkVersion := mod.MinSdkVersion()
if minSdkVersion == "apex_inherit" {
return nil
}
@@ -1492,7 +1577,7 @@
// Overrides ApexModule.IsInstallabeToApex()
func (mod *Module) IsInstallableToApex() bool {
if mod.compiler != nil {
- if lib, ok := mod.compiler.(*libraryDecorator); ok && (lib.shared() || lib.dylib()) {
+ if lib, ok := mod.compiler.(libraryInterface); ok && (lib.shared() || lib.dylib()) {
return true
}
if _, ok := mod.compiler.(*binaryDecorator); ok {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 80f693e..97bd541 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -79,8 +79,10 @@
}
const (
- sharedVendorVariant = "android_vendor.29_arm64_armv8-a_shared"
- rlibVendorVariant = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
+ sharedVendorVariant = "android_vendor.29_arm64_armv8-a_shared"
+ 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"
)
func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
@@ -101,7 +103,22 @@
),
).RunTestWithBp(t, bp)
return result.TestContext
+}
+func testRustRecoveryFsVersions(t *testing.T, bp string, fs android.MockFS, device_version, vndk_version, recovery_version string) *android.TestContext {
+ skipTestIfOsNotSupported(t)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ fs.AddToFixture(),
+ android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = StringPtr(device_version)
+ variables.RecoverySnapshotVersion = StringPtr(recovery_version)
+ variables.Platform_vndk_version = StringPtr(vndk_version)
+ },
+ ),
+ ).RunTestWithBp(t, bp)
+ return result.TestContext
}
// testRustCov returns a TestContext in which a basic environment has been
@@ -439,6 +456,13 @@
}`)
m := ctx.SingletonForTests("file_metrics")
+ m.Output("unstripped/libwaldo.dylib.so.bloaty.csv")
m.Output("libwaldo.dylib.so.bloaty.csv")
- m.Output("stripped/libwaldo.dylib.so.bloaty.csv")
+}
+
+func assertString(t *testing.T, got, expected string) {
+ t.Helper()
+ if got != expected {
+ t.Errorf("expected %q got %q", expected, got)
+ }
}
diff --git a/rust/sanitize.go b/rust/sanitize.go
index a4ba4bd..be9dc42 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -15,20 +15,39 @@
package rust
import (
+ "fmt"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
"android/soong/cc"
"android/soong/rust/config"
- "fmt"
- "github.com/google/blueprint"
)
+// TODO: When Rust has sanitizer-parity with CC, deduplicate this struct
type SanitizeProperties struct {
// enable AddressSanitizer, HWAddressSanitizer, and others.
Sanitize struct {
Address *bool `android:"arch_variant"`
Hwaddress *bool `android:"arch_variant"`
- Fuzzer *bool `android:"arch_variant"`
- Never *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"`
+ Fuzzer *bool `android:"arch_variant"`
+ Never *bool `android:"arch_variant"`
+
+ // Sanitizers to run in the diagnostic mode (as opposed to the release mode).
+ // Replaces abort() on error with a human-readable error message.
+ // Address and Thread sanitizers always run in diagnostic mode.
+ Diag struct {
+ // Memory-tagging, only available on arm64
+ // requires sanitizer.memtag: true
+ // if set, enables sync memory tagging
+ Memtag_heap *bool `android:"arch_variant"`
+ }
}
SanitizerEnabled bool `blueprint:"mutated"`
SanitizeDep bool `blueprint:"mutated"`
@@ -59,9 +78,18 @@
"-Z sanitizer=address",
}
+// See cc/sanitize.go's hwasanGlobalOptions for global hwasan options.
var hwasanFlags = []string{
"-Z sanitizer=hwaddress",
"-C target-feature=+tagged-globals",
+
+ // Flags from cc/sanitize.go hwasanFlags
+ "-C llvm-args=--aarch64-enable-global-isel-at-O=-1",
+ "-C llvm-args=-fast-isel=false",
+ "-C llvm-args=-instcombine-lower-dbg-declare=0",
+
+ // Additional flags for HWASAN-ified Rust/C interop
+ "-C llvm-args=--hwasan-with-ifunc",
}
func boolPtr(v bool) *bool {
@@ -79,7 +107,88 @@
}
func (sanitize *sanitize) begin(ctx BaseModuleContext) {
- s := sanitize.Properties.Sanitize
+ s := &sanitize.Properties.Sanitize
+
+ // Never always wins.
+ if Bool(s.Never) {
+ return
+ }
+
+ // rust_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {Memtag_heap}).
+ if binary, ok := ctx.RustModule().compiler.(binaryInterface); ok && binary.testBinary() {
+ if s.Memtag_heap == nil {
+ s.Memtag_heap = proptools.BoolPtr(true)
+ }
+ if s.Diag.Memtag_heap == nil {
+ s.Diag.Memtag_heap = proptools.BoolPtr(true)
+ }
+ }
+
+ var globalSanitizers []string
+ var globalSanitizersDiag []string
+
+ if ctx.Host() {
+ if !ctx.Windows() {
+ globalSanitizers = ctx.Config().SanitizeHost()
+ }
+ } else {
+ arches := ctx.Config().SanitizeDeviceArch()
+ if len(arches) == 0 || android.InList(ctx.Arch().ArchType.Name, arches) {
+ globalSanitizers = ctx.Config().SanitizeDevice()
+ globalSanitizersDiag = ctx.Config().SanitizeDeviceDiag()
+ }
+ }
+
+ if len(globalSanitizers) > 0 {
+ var found bool
+
+ // Global Sanitizers
+ if found, globalSanitizers = android.RemoveFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil {
+ // TODO(b/204776996): HWASan for static Rust binaries isn't supported yet.
+ if !ctx.RustModule().StaticExecutable() {
+ s.Hwaddress = proptools.BoolPtr(true)
+ }
+ }
+
+ if found, globalSanitizers = android.RemoveFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
+ if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
+ s.Memtag_heap = proptools.BoolPtr(true)
+ }
+ }
+
+ if found, globalSanitizers = android.RemoveFromList("address", globalSanitizers); found && s.Address == nil {
+ s.Address = proptools.BoolPtr(true)
+ }
+
+ if found, globalSanitizers = android.RemoveFromList("fuzzer", globalSanitizers); found && s.Fuzzer == nil {
+ // TODO(b/204776996): HWASan for static Rust binaries isn't supported yet, and fuzzer enables HWAsan
+ if !ctx.RustModule().StaticExecutable() {
+ s.Fuzzer = proptools.BoolPtr(true)
+ }
+ }
+
+ // Global Diag Sanitizers
+ if found, globalSanitizersDiag = android.RemoveFromList("memtag_heap", globalSanitizersDiag); found &&
+ s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) {
+ s.Diag.Memtag_heap = proptools.BoolPtr(true)
+ }
+ }
+
+ // Enable Memtag for all components in the include paths (for Aarch64 only)
+ if ctx.Arch().ArchType == android.Arm64 {
+ if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
+ if s.Memtag_heap == nil {
+ s.Memtag_heap = proptools.BoolPtr(true)
+ }
+ if s.Diag.Memtag_heap == nil {
+ s.Diag.Memtag_heap = proptools.BoolPtr(true)
+ }
+ } else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
+ if s.Memtag_heap == nil {
+ s.Memtag_heap = proptools.BoolPtr(true)
+ }
+ }
+ }
// TODO:(b/178369775)
// For now sanitizing is only supported on devices
@@ -96,7 +205,22 @@
s.Hwaddress = nil
}
- if ctx.Os() == android.Android && Bool(s.Hwaddress) {
+ // HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
+ // Keep libc instrumented so that ramdisk / vendor_ramdisk / recovery can run hwasan-instrumented code if necessary.
+ if (ctx.RustModule().InRamdisk() || ctx.RustModule().InVendorRamdisk() || ctx.RustModule().InRecovery()) && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
+ s.Hwaddress = nil
+ }
+
+ if Bool(s.Hwaddress) {
+ s.Address = nil
+ }
+
+ // Memtag_heap is only implemented on AArch64.
+ if ctx.Arch().ArchType != android.Arm64 {
+ s.Memtag_heap = nil
+ }
+
+ if ctx.Os() == android.Android && (Bool(s.Hwaddress) || Bool(s.Address) || Bool(s.Memtag_heap)) {
sanitize.Properties.SanitizerEnabled = true
}
}
@@ -116,12 +240,10 @@
} else {
flags.RustFlags = append(flags.RustFlags, asanFlags...)
}
- }
- if Bool(sanitize.Properties.Sanitize.Address) {
- flags.RustFlags = append(flags.RustFlags, asanFlags...)
- }
- if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ } else if Bool(sanitize.Properties.Sanitize.Hwaddress) {
flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
+ } else if Bool(sanitize.Properties.Sanitize.Address) {
+ flags.RustFlags = append(flags.RustFlags, asanFlags...)
}
return flags, deps
}
@@ -136,6 +258,26 @@
return
}
+ if Bool(mod.sanitize.Properties.Sanitize.Memtag_heap) && mod.Binary() {
+ noteDep := "note_memtag_heap_async"
+ if Bool(mod.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(cc.SnapshotInfoProvider).(cc.SnapshotInfo)
+ if lib, ok := snapshot.StaticLibs[noteDep]; ok {
+ noteDep = lib
+ }
+ depTag := cc.StaticDepTag(true)
+ variations := append(mctx.Target().Variations(),
+ blueprint.Variation{Mutator: "link", Variation: "static"})
+ if mod.Device() {
+ variations = append(variations, mod.ImageVariation())
+ }
+ mctx.AddFarVariationDependencies(variations, depTag, noteDep)
+ }
+
variations := mctx.Target().Variations()
var depTag blueprint.DependencyTag
var deps []string
@@ -148,27 +290,24 @@
deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
} else if mod.IsSanitizerEnabled(cc.Hwasan) ||
(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64) {
- // TODO(b/180495975): HWASan for static Rust binaries isn't supported yet.
- if binary, ok := mod.compiler.(*binaryDecorator); ok {
- if Bool(binary.Properties.Static_executable) {
+ // TODO(b/204776996): HWASan for static Rust binaries isn't supported yet.
+ if binary, ok := mod.compiler.(binaryInterface); ok {
+ if binary.staticallyLinked() {
mctx.ModuleErrorf("HWASan is not supported for static Rust executables yet.")
}
}
- if mod.StaticallyLinked() {
- variations = append(variations,
- blueprint.Variation{Mutator: "link", Variation: "static"})
- depTag = cc.StaticDepTag(false)
- deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan_static")}
- } else {
- variations = append(variations,
- blueprint.Variation{Mutator: "link", Variation: "shared"})
- depTag = cc.SharedDepTag()
- deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan")}
- }
+ // Always link against the shared library -- static binaries will pull in the static
+ // library during final link if necessary
+ variations = append(variations,
+ blueprint.Variation{Mutator: "link", Variation: "shared"})
+ depTag = cc.SharedDepTag()
+ deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan")}
}
- mctx.AddFarVariationDependencies(variations, depTag, deps...)
+ if len(deps) > 0 {
+ mctx.AddFarVariationDependencies(variations, depTag, deps...)
+ }
}
}
@@ -184,6 +323,9 @@
case cc.Hwasan:
sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
sanitizerSet = true
+ case cc.Memtag_heap:
+ sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
+ sanitizerSet = true
default:
panic(fmt.Errorf("setting unsupported sanitizerType %d", t))
}
@@ -243,6 +385,8 @@
return sanitize.Properties.Sanitize.Address
case cc.Hwasan:
return sanitize.Properties.Sanitize.Hwaddress
+ case cc.Memtag_heap:
+ return sanitize.Properties.Sanitize.Memtag_heap
default:
return nil
}
@@ -268,6 +412,12 @@
case cc.Asan:
return true
case cc.Hwasan:
+ // TODO(b/180495975): HWASan for static Rust binaries isn't supported yet.
+ if mod.StaticExecutable() {
+ return false
+ }
+ return true
+ case cc.Memtag_heap:
return true
default:
return false
@@ -311,8 +461,8 @@
func (mod *Module) StaticallyLinked() bool {
if lib, ok := mod.compiler.(libraryInterface); ok {
return lib.rlib() || lib.static()
- } else if binary, ok := mod.compiler.(*binaryDecorator); ok {
- return Bool(binary.Properties.Static_executable)
+ } else if binary, ok := mod.compiler.(binaryInterface); ok {
+ return binary.staticallyLinked()
}
return false
}
diff --git a/rust/sanitize_test.go b/rust/sanitize_test.go
new file mode 100644
index 0000000..d6a14b2
--- /dev/null
+++ b/rust/sanitize_test.go
@@ -0,0 +1,365 @@
+package rust
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+type MemtagNoteType int
+
+const (
+ None MemtagNoteType = iota + 1
+ Sync
+ Async
+)
+
+func (t MemtagNoteType) str() string {
+ switch t {
+ case None:
+ return "none"
+ case Sync:
+ return "sync"
+ case Async:
+ return "async"
+ default:
+ panic("type_note_invalid")
+ }
+}
+
+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("rustc").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
+ }
+ }
+
+ if found != expected {
+ t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
+ }
+}
+
+var prepareForTestWithMemtagHeap = android.GroupFixturePreparers(
+ android.FixtureModifyMockFS(func(fs android.MockFS) {
+ templateBp := `
+ rust_test {
+ name: "unset_test_%[1]s",
+ srcs: ["foo.rs"],
+ }
+
+ rust_test {
+ name: "no_memtag_test_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { memtag_heap: false },
+ }
+
+ rust_test {
+ name: "set_memtag_test_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { memtag_heap: true },
+ }
+
+ rust_test {
+ name: "set_memtag_set_async_test_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
+ }
+
+ rust_test {
+ name: "set_memtag_set_sync_test_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
+ }
+
+ rust_test {
+ name: "unset_memtag_set_sync_test_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { diag: { memtag_heap: true } },
+ }
+
+ rust_binary {
+ name: "unset_binary_%[1]s",
+ srcs: ["foo.rs"],
+ }
+
+ rust_binary {
+ name: "no_memtag_binary_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { memtag_heap: false },
+ }
+
+ rust_binary {
+ name: "set_memtag_binary_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { memtag_heap: true },
+ }
+
+ rust_binary {
+ name: "set_memtag_set_async_binary_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
+ }
+
+ rust_binary {
+ name: "set_memtag_set_sync_binary_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
+ }
+
+ rust_binary {
+ name: "unset_memtag_set_sync_binary_%[1]s",
+ srcs: ["foo.rs"],
+ sanitize: { diag: { memtag_heap: true } },
+ }
+ `
+ subdirNoOverrideBp := fmt.Sprintf(templateBp, "no_override")
+ subdirOverrideDefaultDisableBp := fmt.Sprintf(templateBp, "override_default_disable")
+ subdirSyncBp := fmt.Sprintf(templateBp, "override_default_sync")
+ subdirAsyncBp := fmt.Sprintf(templateBp, "override_default_async")
+
+ fs.Merge(android.MockFS{
+ "subdir_no_override/Android.bp": []byte(subdirNoOverrideBp),
+ "subdir_override_default_disable/Android.bp": []byte(subdirOverrideDefaultDisableBp),
+ "subdir_sync/Android.bp": []byte(subdirSyncBp),
+ "subdir_async/Android.bp": []byte(subdirAsyncBp),
+ })
+ }),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.MemtagHeapExcludePaths = []string{"subdir_override_default_disable"}
+ // "subdir_override_default_disable" is covered by both include and override_default_disable paths. override_default_disable wins.
+ variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_override_default_disable"}
+ variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_override_default_disable"}
+ }),
+)
+
+func TestSanitizeMemtagHeap(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ prepareForTestWithMemtagHeap,
+ ).RunTest(t)
+ ctx := result.TestContext
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ prepareForTestWithMemtagHeap,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.SanitizeDevice = []string{"memtag_heap"}
+ }),
+ ).RunTest(t)
+ ctx := result.TestContext
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+ // should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+ // should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ prepareForTestWithMemtagHeap,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.SanitizeDevice = []string{"memtag_heap"}
+ variables.SanitizeDeviceDiag = []string{"memtag_heap"}
+ }),
+ ).RunTest(t)
+ ctx := result.TestContext
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+ // should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+}
diff --git a/rust/snapshot_prebuilt.go b/rust/snapshot_prebuilt.go
index 79eaab3..dfbc1d1 100644
--- a/rust/snapshot_prebuilt.go
+++ b/rust/snapshot_prebuilt.go
@@ -44,6 +44,8 @@
func registerRustSnapshotModules(ctx android.RegistrationContext) {
cc.VendorSnapshotImageSingleton.RegisterAdditionalModule(ctx,
"vendor_snapshot_rlib", VendorSnapshotRlibFactory)
+ cc.RecoverySnapshotImageSingleton.RegisterAdditionalModule(ctx,
+ "recovery_snapshot_rlib", RecoverySnapshotRlibFactory)
}
func snapshotLibraryFactory(image cc.SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
@@ -85,8 +87,9 @@
if !library.MatchesWithDevice(ctx.DeviceConfig()) {
return nil
}
-
- return android.PathForModuleSrc(ctx, *library.properties.Src)
+ outputFile := android.PathForModuleSrc(ctx, *library.properties.Src)
+ library.unstrippedOutputFile = outputFile
+ return outputFile
}
func (library *snapshotLibraryDecorator) rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath {
@@ -104,6 +107,13 @@
return module.Init()
}
+func RecoverySnapshotRlibFactory() android.Module {
+ module, prebuilt := snapshotLibraryFactory(cc.RecoverySnapshotImageSingleton, cc.SnapshotRlibSuffix)
+ prebuilt.libraryDecorator.BuildOnlyRlib()
+ prebuilt.libraryDecorator.setNoStdlibs()
+ return module.Init()
+}
+
func (library *snapshotLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
arches := config.Arches()
if len(arches) == 0 || arches[0].ArchType.String() != library.Arch() {
diff --git a/rust/test.go b/rust/test.go
index 56da509..250b765 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -18,6 +18,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/cc"
"android/soong/tradefed"
)
@@ -49,6 +50,12 @@
// the test
Data []string `android:"path,arch_variant"`
+ // list of shared library modules that should be installed alongside the test
+ Data_libs []string `android:"arch_variant"`
+
+ // list of binary modules that should be installed alongside the test
+ Data_bins []string `android:"arch_variant"`
+
// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
// explicitly.
@@ -137,6 +144,32 @@
dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
+ ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ linkableDep, ok := dep.(cc.LinkableInterface)
+ if !ok {
+ ctx.ModuleErrorf("data_lib %q is not a linkable module", depName)
+ }
+ if linkableDep.OutputFile().Valid() {
+ test.data = append(test.data,
+ android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
+ RelativeInstallPath: linkableDep.RelativeInstallPath()})
+ }
+ })
+
+ ctx.VisitDirectDepsWithTag(dataBinDepTag, func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ linkableDep, ok := dep.(cc.LinkableInterface)
+ if !ok {
+ ctx.ModuleErrorf("data_bin %q is not a linkable module", depName)
+ }
+ if linkableDep.OutputFile().Valid() {
+ test.data = append(test.data,
+ android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
+ RelativeInstallPath: linkableDep.RelativeInstallPath()})
+ }
+ })
+
for _, dataSrcPath := range dataSrcPaths {
test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
}
@@ -177,6 +210,12 @@
func RustTestFactory() android.Module {
module, _ := NewRustTest(android.HostAndDeviceSupported)
+
+ // NewRustTest will set MultilibBoth true, however the host variant
+ // cannot produce the non-primary target. Therefore, add the
+ // rustTestHostMultilib load hook to set MultilibFirst for the
+ // host target.
+ android.AddLoadHook(module, rustTestHostMultilib)
return module.Init()
}
@@ -194,5 +233,25 @@
deps.Rustlibs = append(deps.Rustlibs, "libtest")
+ deps.DataLibs = append(deps.DataLibs, test.Properties.Data_libs...)
+ deps.DataBins = append(deps.DataBins, test.Properties.Data_bins...)
+
return deps
}
+
+func (test *testDecorator) testBinary() bool {
+ return true
+}
+
+func rustTestHostMultilib(ctx android.LoadHookContext) {
+ type props struct {
+ Target struct {
+ Host struct {
+ Compile_multilib *string
+ }
+ }
+ }
+ p := &props{}
+ p.Target.Host.Compile_multilib = proptools.StringPtr("first")
+ ctx.AppendProperties(p)
+}
diff --git a/rust/test_test.go b/rust/test_test.go
index 892761a..1124176 100644
--- a/rust/test_test.go
+++ b/rust/test_test.go
@@ -74,3 +74,129 @@
t.Errorf("Device rust_test module 'my_test' does not link libstd as an rlib")
}
}
+
+func TestDataLibs(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "test_lib",
+ srcs: ["test_lib.cpp"],
+ }
+
+ rust_binary {
+ name: "rusty",
+ srcs: ["foo.rs"],
+ compile_multilib: "both",
+ }
+
+ rust_ffi {
+ name: "librust_test_lib",
+ crate_name: "rust_test_lib",
+ srcs: ["test_lib.rs"],
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "64",
+ }
+
+ rust_test {
+ name: "main_test",
+ srcs: ["foo.rs"],
+ data_libs: ["test_lib"],
+ data_bins: ["rusty"],
+ }
+ `
+
+ ctx := testRust(t, bp)
+
+ module := ctx.ModuleForTests("main_test", "android_arm64_armv8-a").Module()
+ testBinary := module.(*Module).compiler.(*testDecorator)
+ outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Fatalf("Expected rust_test to produce output files, error: %s", err)
+ }
+ if len(outputFiles) != 1 {
+ t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
+ }
+ if len(testBinary.dataPaths()) != 2 {
+ t.Fatalf("expected exactly two test data files. test data files: [%s]", testBinary.dataPaths())
+ }
+
+ outputPath := outputFiles[0].String()
+ dataLibraryPath := testBinary.dataPaths()[0].SrcPath.String()
+ dataBinaryPath := testBinary.dataPaths()[1].SrcPath.String()
+
+ if !strings.HasSuffix(outputPath, "/main_test") {
+ t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
+ }
+ if !strings.HasSuffix(dataLibraryPath, "/test_lib.so") {
+ t.Errorf("expected test data file to be 'test_lib.so', but was '%s'", dataLibraryPath)
+ }
+ if !strings.HasSuffix(dataBinaryPath, "/rusty") {
+ t.Errorf("expected test data file to be 'test_lib.so', but was '%s'", dataBinaryPath)
+ }
+}
+
+func TestDataLibsRelativeInstallPath(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "test_lib",
+ srcs: ["test_lib.cpp"],
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "64",
+ }
+
+ rust_ffi {
+ name: "librust_test_lib",
+ crate_name: "rust_test_lib",
+ srcs: ["test_lib.rs"],
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "64",
+ }
+
+ rust_binary {
+ name: "rusty",
+ srcs: ["foo.rs"],
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "64",
+ }
+
+ rust_test {
+ name: "main_test",
+ srcs: ["foo.rs"],
+ data_libs: ["test_lib", "librust_test_lib"],
+ data_bins: ["rusty"],
+ compile_multilib: "64",
+ }
+ `
+
+ ctx := testRust(t, bp)
+ module := ctx.ModuleForTests("main_test", "android_arm64_armv8-a").Module()
+ testBinary := module.(*Module).compiler.(*testDecorator)
+ outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Fatalf("Expected rust_test to produce output files, error: %s", err)
+ }
+ if len(outputFiles) != 1 {
+ t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
+ }
+ if len(testBinary.dataPaths()) != 3 {
+ t.Fatalf("expected exactly two test data files. test data files: [%s]", testBinary.dataPaths())
+ }
+
+ outputPath := outputFiles[0].String()
+
+ if !strings.HasSuffix(outputPath, "/main_test") {
+ 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`,"+
+ " 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`,"+
+ " but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][1])
+ }
+ if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][2], ":rusty:foo/bar/baz") {
+ t.Errorf("expected LOCAL_TEST_DATA to end with `:rusty:foo/bar/baz`,"+
+ " but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][2])
+ }
+}
diff --git a/rust/testing.go b/rust/testing.go
index 94cdd9d..1b34dfe 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -53,74 +53,14 @@
func GatherRequiredDepsForTest() string {
bp := `
rust_prebuilt_library {
- name: "libstd_x86_64-unknown-linux-gnu",
- crate_name: "std",
- rlib: {
- srcs: ["libstd.rlib"],
- },
- dylib: {
- srcs: ["libstd.so"],
- },
- host_supported: true,
- sysroot: true,
- }
- rust_prebuilt_library {
- name: "libtest_x86_64-unknown-linux-gnu",
- crate_name: "test",
- rlib: {
- srcs: ["libtest.rlib"],
- },
- dylib: {
- srcs: ["libtest.so"],
- },
- host_supported: true,
- sysroot: true,
- }
- rust_prebuilt_library {
- name: "libstd_i686-unknown-linux-gnu",
- crate_name: "std",
- rlib: {
- srcs: ["libstd.rlib"],
- },
- dylib: {
- srcs: ["libstd.so"],
- },
- host_supported: true,
- sysroot: true,
- }
- rust_prebuilt_library {
- name: "libtest_i686-unknown-linux-gnu",
- crate_name: "test",
- rlib: {
- srcs: ["libtest.rlib"],
- },
- dylib: {
- srcs: ["libtest.so"],
- },
- host_supported: true,
- sysroot: true,
- }
- rust_prebuilt_library {
- name: "libstd_x86_64-apple-darwin",
- crate_name: "std",
- rlib: {
- srcs: ["libstd.rlib"],
- },
- dylib: {
- srcs: ["libstd.so"],
- },
- host_supported: true,
- sysroot: true,
- }
- rust_prebuilt_library {
- name: "libtest_x86_64-apple-darwin",
- crate_name: "test",
- rlib: {
- srcs: ["libtest.rlib"],
- },
- dylib: {
- srcs: ["libtest.so"],
- },
+ name: "libstd",
+ crate_name: "std",
+ rlib: {
+ srcs: ["libstd.rlib"],
+ },
+ dylib: {
+ srcs: ["libstd.so"],
+ },
host_supported: true,
sysroot: true,
}
@@ -135,6 +75,7 @@
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
min_sdk_version: "29",
vendor_available: true,
+ recovery_available: true,
llndk: {
symbol_file: "liblog.map.txt",
},
@@ -151,7 +92,12 @@
no_libcrt: true,
nocrt: true,
system_shared_libs: [],
- export_include_dirs: ["libprotobuf-cpp-full-includes"],
+ }
+ cc_library {
+ name: "libclang_rt.hwasan_static-aarch64-android",
+ no_libcrt: true,
+ nocrt: true,
+ system_shared_libs: [],
}
rust_library {
name: "libstd",
@@ -161,6 +107,7 @@
host_supported: true,
vendor_available: true,
vendor_ramdisk_available: true,
+ recovery_available: true,
native_coverage: false,
sysroot: true,
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
@@ -173,6 +120,7 @@
host_supported: true,
vendor_available: true,
vendor_ramdisk_available: true,
+ recovery_available: true,
native_coverage: false,
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
min_sdk_version: "29",
@@ -246,5 +194,8 @@
ctx.BottomUp("rust_begin", BeginMutator).Parallel()
})
ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+ ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
+ })
registerRustSnapshotModules(ctx)
}
diff --git a/rust/toolchain_library.go b/rust/toolchain_library.go
new file mode 100644
index 0000000..326d529
--- /dev/null
+++ b/rust/toolchain_library.go
@@ -0,0 +1,103 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package rust
+
+import (
+ "path"
+
+ "android/soong/android"
+ "android/soong/rust/config"
+)
+
+// This module is used to compile the rust toolchain libraries
+// When RUST_PREBUILTS_VERSION is set, the library will generated
+// from the given Rust version.
+func init() {
+ android.RegisterModuleType("rust_toolchain_library",
+ rustToolchainLibraryFactory)
+ android.RegisterModuleType("rust_toolchain_library_rlib",
+ rustToolchainLibraryRlibFactory)
+ android.RegisterModuleType("rust_toolchain_library_dylib",
+ rustToolchainLibraryDylibFactory)
+}
+
+type toolchainLibraryProperties struct {
+ // path to the toolchain source, relative to the top of the toolchain source
+ Toolchain_src *string `android:"arch_variant"`
+}
+
+type toolchainLibraryDecorator struct {
+ *libraryDecorator
+ Properties toolchainLibraryProperties
+}
+
+// rust_toolchain_library produces all rust variants.
+func rustToolchainLibraryFactory() android.Module {
+ module, library := NewRustLibrary(android.HostAndDeviceSupported)
+ library.BuildOnlyRust()
+
+ return initToolchainLibrary(module, library)
+}
+
+// rust_toolchain_library_dylib produces a dylib.
+func rustToolchainLibraryDylibFactory() android.Module {
+ module, library := NewRustLibrary(android.HostAndDeviceSupported)
+ library.BuildOnlyDylib()
+
+ return initToolchainLibrary(module, library)
+}
+
+// rust_toolchain_library_rlib produces an rlib.
+func rustToolchainLibraryRlibFactory() android.Module {
+ module, library := NewRustLibrary(android.HostAndDeviceSupported)
+ library.BuildOnlyRlib()
+
+ return initToolchainLibrary(module, library)
+}
+
+func initToolchainLibrary(module *Module, library *libraryDecorator) android.Module {
+ toolchainLibrary := &toolchainLibraryDecorator{
+ libraryDecorator: library,
+ }
+ module.compiler = toolchainLibrary
+ module.AddProperties(&toolchainLibrary.Properties)
+ android.AddLoadHook(module, rustSetToolchainSource)
+
+ return module.Init()
+}
+
+func rustSetToolchainSource(ctx android.LoadHookContext) {
+ if toolchainLib, ok := ctx.Module().(*Module).compiler.(*toolchainLibraryDecorator); ok {
+ prefix := "linux-x86/" + GetRustPrebuiltVersion(ctx)
+ newSrcs := []string{path.Join(prefix, android.String(toolchainLib.Properties.Toolchain_src))}
+
+ type props struct {
+ Srcs []string
+ }
+ p := &props{}
+ p.Srcs = newSrcs
+ ctx.AppendProperties(p)
+
+ } else {
+ ctx.ModuleErrorf("Called rustSetToolchainSource on a non-Rust Module.")
+ }
+}
+
+// GetRustPrebuiltVersion returns the RUST_PREBUILTS_VERSION env var, or the default version if it is not defined.
+func GetRustPrebuiltVersion(ctx android.LoadHookContext) string {
+ return ctx.AConfig().GetenvWithDefault("RUST_PREBUILTS_VERSION", config.RustDefaultVersion)
+}
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
index 60ddb65..03bd867 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -562,6 +562,7 @@
"libvendor",
"libvndk",
"libclang_rt.builtins-aarch64-android",
+ "note_memtag_heap_sync",
],
shared_libs: [
"libvendor_available",
@@ -853,6 +854,20 @@
},
}
+ // Test sanitizers use the snapshot libraries
+ rust_binary {
+ name: "memtag_binary",
+ srcs: ["vendor/bin.rs"],
+ vendor: true,
+ compile_multilib: "64",
+ sanitize: {
+ memtag_heap: true,
+ diag: {
+ memtag_heap: true,
+ }
+ },
+ }
+
// old snapshot module which has to be ignored
vendor_snapshot_binary {
name: "bin",
@@ -880,11 +895,25 @@
},
},
}
+
+ vendor_snapshot_static {
+ name: "note_memtag_heap_sync",
+ vendor: true,
+ target_arch: "arm64",
+ version: "30",
+ arch: {
+ arm64: {
+ src: "note_memtag_heap_sync.a",
+ },
+ },
+ }
+
`
mockFS := android.MockFS{
"framework/Android.bp": []byte(frameworkBp),
"framework/bin.rs": nil,
+ "note_memtag_heap_sync.a": nil,
"vendor/Android.bp": []byte(vendorProprietaryBp),
"vendor/bin": nil,
"vendor/bin32": nil,
@@ -993,4 +1022,360 @@
if android.InList(binaryVariant, binVariants) {
t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
}
+
+ memtagStaticLibs := ctx.ModuleForTests("memtag_binary", "android_vendor.30_arm64_armv8-a").Module().(*Module).Properties.AndroidMkStaticLibs
+ if g, w := memtagStaticLibs, []string{"libclang_rt.builtins-aarch64-android.vendor", "note_memtag_heap_sync.vendor"}; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted memtag_binary AndroidMkStaticLibs %q, got %q", w, g)
+ }
+}
+
+func TestRecoverySnapshotCapture(t *testing.T) {
+ bp := `
+ rust_ffi {
+ name: "librecovery",
+ recovery: true,
+ srcs: ["foo.rs"],
+ crate_name: "recovery",
+ }
+
+ rust_ffi {
+ name: "librecovery_available",
+ recovery_available: true,
+ srcs: ["foo.rs"],
+ crate_name: "recovery_available",
+ }
+
+ rust_library_rlib {
+ name: "librecovery_rlib",
+ recovery: true,
+ srcs: ["foo.rs"],
+ crate_name: "recovery_rlib",
+ }
+
+ rust_library_rlib {
+ name: "librecovery_available_rlib",
+ recovery_available: true,
+ srcs: ["foo.rs"],
+ crate_name: "recovery_available_rlib",
+ }
+
+ rust_binary {
+ name: "recovery_bin",
+ recovery: true,
+ srcs: ["foo.rs"],
+ }
+
+ rust_binary {
+ name: "recovery_available_bin",
+ recovery_available: true,
+ srcs: ["foo.rs"],
+ }
+
+`
+ // Check Recovery snapshot output.
+
+ ctx := testRustRecoveryFsVersions(t, bp, rustMockedFiles, "", "29", "current")
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var jsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ // For shared libraries, all recovery:true and recovery_available modules are captured.
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(sharedDir, "librecovery.so.json"),
+ filepath.Join(sharedDir, "librecovery_available.so.json"))
+
+ // For static libraries, all recovery:true and recovery_available modules are captured.
+ staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
+ staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(staticDir, "librecovery.a.json"),
+ filepath.Join(staticDir, "librecovery_available.a.json"))
+
+ // For rlib libraries, all recovery:true and recovery_available modules are captured.
+ rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+ rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rlib", "librecovery_rlib.rlib", rlibDir, rlibVariant)
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available_rlib", "librecovery_available_rlib.rlib", rlibDir, rlibVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(rlibDir, "librecovery_rlib.rlib.json"),
+ filepath.Join(rlibDir, "librecovery_available_rlib.rlib.json"))
+
+ // For binary executables, all recovery:true and recovery_available modules are captured.
+ if archType == "arm64" {
+ binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+ binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(binaryDir, "recovery_bin.json"),
+ filepath.Join(binaryDir, "recovery_available_bin.json"))
+ }
+ }
+
+ for _, jsonFile := range jsonFiles {
+ // verify all json files exist
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("%q expected but not found", jsonFile)
+ }
+ }
+}
+
+func TestRecoverySnapshotExclude(t *testing.T) {
+ // This test verifies that the exclude_from_recovery_snapshot property
+ // makes its way from the Android.bp source file into the module data
+ // structure. It also verifies that modules are correctly included or
+ // excluded in the recovery snapshot based on their path (framework or
+ // vendor) and the exclude_from_recovery_snapshot property.
+
+ frameworkBp := `
+ rust_ffi_shared {
+ name: "libinclude",
+ srcs: ["src/include.rs"],
+ recovery_available: true,
+ crate_name: "include",
+ }
+ rust_ffi_shared {
+ name: "libexclude",
+ srcs: ["src/exclude.rs"],
+ recovery: true,
+ exclude_from_recovery_snapshot: true,
+ crate_name: "exclude",
+ }
+ rust_ffi_shared {
+ name: "libavailable_exclude",
+ srcs: ["src/exclude.rs"],
+ recovery_available: true,
+ exclude_from_recovery_snapshot: true,
+ crate_name: "available_exclude",
+ }
+ rust_library_rlib {
+ name: "libinclude_rlib",
+ srcs: ["src/include.rs"],
+ recovery_available: true,
+ crate_name: "include_rlib",
+ }
+ rust_library_rlib {
+ name: "libexclude_rlib",
+ srcs: ["src/exclude.rs"],
+ recovery: true,
+ exclude_from_recovery_snapshot: true,
+ crate_name: "exclude_rlib",
+ }
+ rust_library_rlib {
+ name: "libavailable_exclude_rlib",
+ srcs: ["src/exclude.rs"],
+ recovery_available: true,
+ exclude_from_recovery_snapshot: true,
+ crate_name: "available_exclude_rlib",
+ }
+ `
+
+ vendorProprietaryBp := `
+ rust_ffi_shared {
+ name: "librecovery",
+ srcs: ["recovery.rs"],
+ recovery: true,
+ crate_name: "recovery",
+ }
+ rust_library_rlib {
+ name: "librecovery_rlib",
+ srcs: ["recovery.rs"],
+ recovery: true,
+ crate_name: "recovery_rlib",
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "framework/Android.bp": []byte(frameworkBp),
+ "framework/include.rs": nil,
+ "framework/exclude.rs": nil,
+ "device/Android.bp": []byte(vendorProprietaryBp),
+ "device/recovery.rs": nil,
+ }
+
+ ctx := testRustRecoveryFsVersions(t, "", mockFS, "", "29", "current")
+
+ // Test an include and exclude framework module.
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false, sharedRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true, sharedRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true, sharedRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude_rlib", false, rlibRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude_rlib", true, rlibRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude_rlib", true, rlibRecoveryVariant)
+
+ // A recovery module is excluded, but by its path not the exclude_from_recovery_snapshot property
+ // ('device/' and 'vendor/' are default excluded). See snapshot/recovery_snapshot.go for more detail.
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false, sharedRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery_rlib", false, rlibRecoveryVariant)
+
+ // Verify the content of the recovery snapshot.
+
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var includeJsonFiles []string
+ var excludeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+
+ // Included modules
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude_rlib", "libinclude_rlib.rlib", rlibDir, rlibVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "libinclude_rlib.rlib.json"))
+
+ // Excluded modules
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude_rlib", "libexclude_rlib.rlib", rlibDir, rlibVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libexclude_rlib.rlib.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery_rlib", "librecovery_rlib.rlib", rlibDir, rlibVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librecovery_rlib.rlib.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude_rlib", "libavailable_exclude_rlib.rlib", rlibDir, rlibVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libavailable_exclude_rlib.rlib.json"))
+ }
+
+ // Verify that each json file for an included module has a rule.
+ for _, jsonFile := range includeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("include json file %q not found", jsonFile)
+ }
+ }
+
+ // Verify that each json file for an excluded module has no rule.
+ for _, jsonFile := range excludeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+ t.Errorf("exclude json file %q found", jsonFile)
+ }
+ }
+}
+
+func TestRecoverySnapshotDirected(t *testing.T) {
+ bp := `
+ rust_ffi_shared {
+ name: "librecovery",
+ recovery: true,
+ crate_name: "recovery",
+ srcs: ["foo.rs"],
+ }
+
+ rust_ffi_shared {
+ name: "librecovery_available",
+ recovery_available: true,
+ crate_name: "recovery_available",
+ srcs: ["foo.rs"],
+ }
+
+ rust_library_rlib {
+ name: "librecovery_rlib",
+ recovery: true,
+ crate_name: "recovery",
+ srcs: ["foo.rs"],
+ }
+
+ rust_library_rlib {
+ name: "librecovery_available_rlib",
+ recovery_available: true,
+ crate_name: "recovery_available",
+ srcs: ["foo.rs"],
+ }
+
+ /* TODO: Uncomment when Rust supports the "prefer" property for prebuilts
+ rust_library_rlib {
+ name: "libfoo_rlib",
+ recovery: true,
+ crate_name: "foo",
+ }
+
+ rust_prebuilt_rlib {
+ name: "libfoo_rlib",
+ recovery: true,
+ prefer: true,
+ srcs: ["libfoo.rlib"],
+ crate_name: "foo",
+ }
+ */
+`
+ ctx := testRustRecoveryFsVersions(t, bp, rustMockedFiles, "current", "29", "current")
+ ctx.Config().TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
+ ctx.Config().TestProductVariables.RecoverySnapshotModules["librecovery"] = true
+ ctx.Config().TestProductVariables.RecoverySnapshotModules["librecovery_rlib"] = true
+ ctx.Config().TestProductVariables.DirectedRecoverySnapshot = true
+
+ // Check recovery snapshot output.
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var includeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+
+ // Included modules
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rlib", "librecovery_rlib.rlib", rlibDir, rlibVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_rlib.rlib.json"))
+
+ // TODO: When Rust supports the "prefer" property for prebuilts, perform this check.
+ /*
+ // Check that snapshot captures "prefer: true" prebuilt
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo_rlib", "libfoo_rlib.rlib", rlibDir, rlibVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo_rlib.rlib.json"))
+ */
+
+ // Excluded modules. Modules not included in the directed recovery snapshot
+ // are still included as fake modules.
+ cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
+ cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available_rlib", "librecovery_available_rlib.rlib", rlibDir, rlibVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_available_rlib.rlib.json"))
+ }
+
+ // Verify that each json file for an included module has a rule.
+ for _, jsonFile := range includeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("include json file %q not found, %#v", jsonFile, includeJsonFiles)
+ }
+ }
}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 635be10..4c847a1 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -1,5 +1,6 @@
package {
default_applicable_licenses: ["Android-Apache-2.0"],
+ default_visibility: ["//build/soong:__subpackages__"],
}
python_binary_host {
@@ -8,14 +9,6 @@
srcs: [
"check_boot_jars/check_boot_jars.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
}
python_binary_host {
@@ -24,14 +17,6 @@
srcs: [
"manifest_fixer.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
libs: [
"manifest_utils",
],
@@ -45,11 +30,8 @@
"manifest_fixer.py",
],
version: {
- py2: {
- enabled: true,
- },
py3: {
- enabled: false,
+ embedded_launcher: true,
},
},
libs: [
@@ -65,14 +47,7 @@
srcs: [
"manifest.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
+ visibility: ["//system/apex/apexer:__pkg__"],
}
python_binary_host {
@@ -81,14 +56,6 @@
srcs: [
"manifest_check.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
libs: [
"manifest_utils",
],
@@ -101,14 +68,6 @@
"manifest_check_test.py",
"manifest_check.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
libs: [
"manifest_utils",
],
@@ -123,14 +82,6 @@
srcs: [
"jsonmodify.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
}
python_binary_host {
@@ -139,14 +90,6 @@
srcs: [
"test_config_fixer.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
libs: [
"manifest_utils",
],
@@ -159,14 +102,6 @@
"test_config_fixer_test.py",
"test_config_fixer.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
libs: [
"manifest_utils",
],
@@ -179,14 +114,6 @@
srcs: [
"construct_context.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
libs: [
"manifest_utils",
],
@@ -199,14 +126,6 @@
"construct_context_test.py",
"construct_context.py",
],
- version: {
- py2: {
- enabled: true,
- },
- py3: {
- enabled: false,
- },
- },
libs: [
"manifest_utils",
],
@@ -253,11 +172,7 @@
"conv_linker_config.py",
],
version: {
- py2: {
- enabled: false,
- },
py3: {
- enabled: true,
embedded_launcher: true,
},
},
@@ -272,12 +187,4 @@
srcs: [
"get_clang_version.py",
],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- },
- },
}
diff --git a/scripts/OWNERS b/scripts/OWNERS
index 1830a18..3f4f9c0 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -1,6 +1,5 @@
per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
-per-file build-mainline-modules.sh = ngeoffray@google.com,paulduffin@google.com,mast@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 = sophiez@google.com, allenhair@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
index 8a5513e..1a16f7c 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -23,10 +23,10 @@
export OUT_DIR=${OUT_DIR:-out}
if [ -e ${OUT_DIR}/soong/.soong.kati_enabled ]; then
- # If ${OUT_DIR} has been created without --skip-make, Soong will create an
+ # 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 --skip-make" below. We therefore default to a different out dir
+ # "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"
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
deleted file mode 100755
index b05861b..0000000
--- a/scripts/build-mainline-modules.sh
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/bin/bash -e
-
-# Non exhaustive list of modules where we want prebuilts. More can be added as
-# needed.
-MAINLINE_MODULES=(
- com.android.art
- com.android.art.debug
- com.android.art.testing
- com.android.conscrypt
- com.android.i18n
- com.android.os.statsd
- com.android.runtime
- com.android.tzdata
-)
-
-# List of SDKs and module exports we know of.
-MODULES_SDK_AND_EXPORTS=(
- art-module-sdk
- art-module-test-exports
- conscrypt-module-host-exports
- conscrypt-module-sdk
- conscrypt-module-test-exports
- i18n-module-host-exports
- i18n-module-sdk
- i18n-module-test-exports
- platform-mainline-sdk
- platform-mainline-test-exports
- runtime-module-host-exports
- runtime-module-sdk
- statsd-module-sdk
- tzdata-module-test-exports
-)
-
-# List of libraries installed on the platform that are needed for ART chroot
-# testing.
-PLATFORM_LIBRARIES=(
- heapprofd_client_api
- libartpalette-system
- liblog
-)
-
-# We want to create apex modules for all supported architectures.
-PRODUCTS=(
- aosp_arm
- aosp_arm64
- aosp_x86
- aosp_x86_64
-)
-
-if [ ! -e "build/make/core/Makefile" ]; then
- echo "$0 must be run from the top of the tree"
- exit 1
-fi
-
-echo_and_run() {
- echo "$*"
- "$@"
-}
-
-lib_dir() {
- case $1 in
- (aosp_arm|aosp_x86) echo "lib";;
- (aosp_arm64|aosp_x86_64) echo "lib64";;
- esac
-}
-
-# Make sure this build builds from source, regardless of the default.
-export SOONG_CONFIG_art_module_source_build=true
-
-# This script does not intend to handle compressed APEX
-export OVERRIDE_PRODUCT_COMPRESSED_APEX=false
-
-OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR)
-DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR)
-
-for product in "${PRODUCTS[@]}"; do
- echo_and_run build/soong/soong_ui.bash --make-mode $@ \
- TARGET_PRODUCT=${product} \
- ${MAINLINE_MODULES[@]} \
- ${PLATFORM_LIBRARIES[@]}
-
- PRODUCT_OUT=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var PRODUCT_OUT)
- TARGET_ARCH=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var TARGET_ARCH)
- rm -rf ${DIST_DIR}/${TARGET_ARCH}/
- mkdir -p ${DIST_DIR}/${TARGET_ARCH}/
- for module in "${MAINLINE_MODULES[@]}"; do
- echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/apex/${module}.apex ${DIST_DIR}/${TARGET_ARCH}/
- done
- for library in "${PLATFORM_LIBRARIES[@]}"; do
- libdir=$(lib_dir $product)
- echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/${libdir}/${library}.so ${DIST_DIR}/${TARGET_ARCH}/
- done
-done
-
-# We use force building LLVM components flag (even though we actually don't
-# compile them) because we don't have bionic host prebuilts
-# for them.
-export FORCE_BUILD_LLVM_COMPONENTS=true
-
-# Create multi-archs SDKs in a different out directory. The multi-arch script
-# uses Soong in --skip-make mode which cannot use the same directory as normal
-# mode with make.
-export OUT_DIR=${OUT_DIR}/aml
-echo_and_run build/soong/scripts/build-aml-prebuilts.sh \
- TARGET_PRODUCT=mainline_sdk ${MODULES_SDK_AND_EXPORTS[@]}
-
-rm -rf ${DIST_DIR}/mainline-sdks
-echo_and_run cp -R ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR}
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index a1fa48d..4783037 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -64,7 +64,7 @@
"MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
}
EOF
-m --skip-make ${SOONG_OUT}/ndk.timestamp
+m --soong-only --skip-config ${SOONG_OUT}/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 b1b1e7e..a02c195 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -69,8 +69,10 @@
javax\.xml\.transform\.stream
javax\.xml\.validation
javax\.xml\.xpath
+jdk\.internal
jdk\.internal\.math
jdk\.internal\.misc
+jdk\.internal\.ref
jdk\.internal\.reflect
jdk\.internal\.util
jdk\.internal\.vm\.annotation
diff --git a/scripts/diff_build_graphs.sh b/scripts/diff_build_graphs.sh
index 81010f3..8d01124 100755
--- a/scripts/diff_build_graphs.sh
+++ b/scripts/diff_build_graphs.sh
@@ -98,7 +98,7 @@
# or in case it is affected by some of the changes we're testing
make blueprint_tools
# find multiproduct_kati and have it build the ninja files for each product
- builder="$(echo $OUT_DIR/soong/host/*/bin/multiproduct_kati)"
+ builder="$(echo $OUT_DIR/host/*/bin/multiproduct_kati)"
BUILD_NUMBER=sample "$builder" $PRODUCTS_ARG --keep --out "$OUT_DIR_TEMP" || true
echo
}
diff --git a/scripts/gen_java_usedby_apex.sh b/scripts/gen_java_usedby_apex.sh
new file mode 100755
index 0000000..e398541
--- /dev/null
+++ b/scripts/gen_java_usedby_apex.sh
@@ -0,0 +1,48 @@
+#!/bin/bash -e
+
+# 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.
+
+printHelp() {
+ echo "**************************** Usage Instructions ****************************"
+ echo "This script is used to generate the Mainline modules used-by Java symbols."
+ echo ""
+ echo "To run this script use: ./gen_java_usedby_apex.sh \$BINARY_DEXDEPS_PATH \$OUTPUT_FILE_PATH \$JAR_AND_APK_LIST"
+ echo "For example: If all jar and apk files are '/myJar.jar /myApk.apk' and output write to /myModule.txt then the command would be:"
+ echo "./gen_java_usedby_apex.sh \$BINARY_DEXDEPS_PATH /myModule.txt /myJar.jar /myApk.apk"
+}
+
+genUsedByList() {
+ dexdeps="$1"
+ shift
+ out="$1"
+ shift
+ rm -f "$out"
+ touch "$out"
+ echo "<externals>" >> "$out"
+ for x in "$@"; do
+ "$dexdeps" "$x" >> "$out" || echo "</external>" >> "$out"
+ done
+ echo "</externals>" >> "$out"
+}
+
+if [[ "$1" == "help" ]]
+then
+ printHelp
+elif [[ "$#" -lt 2 ]]
+then
+ echo "Wrong argument length. Expecting at least 2 argument representing dexdeps path, output path, followed by a list of jar or apk files in the Mainline module."
+else
+ genUsedByList "$@"
+fi
\ No newline at end of file
diff --git a/scripts/gen_ndk_backedby_apex.sh b/scripts/gen_ndk_backedby_apex.sh
index 4abaaba..212362e 100755
--- a/scripts/gen_ndk_backedby_apex.sh
+++ b/scripts/gen_ndk_backedby_apex.sh
@@ -21,52 +21,29 @@
# 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 backed-by NDK symbols."
+ echo "This script is used to generate the native libraries backed by Mainline modules."
echo ""
- echo "To run this script use: ./gen_ndk_backed_by_apex.sh \$OUTPUT_FILE_PATH \$NDK_LIB_NAME_LIST \$MODULE_LIB1 \$MODULE_LIB2..."
+ echo "To run this script use: ./gen_ndk_backed_by_apex.sh \$OUTPUT_FILE_PATH \$MODULE_LIB1 \$MODULE_LIB2..."
echo "For example: If output write to /backedby.txt then the command would be:"
- echo "./gen_ndk_backed_by_apex.sh /backedby.txt /ndkLibList.txt lib1.so lib2.so"
+ echo "./gen_ndk_backed_by_apex.sh /backedby.txt lib1.so lib2.so"
echo "If the module1 is backing lib1 then the backedby.txt would contains: "
- echo "lib1"
+ echo "lib1.so lib2.so"
}
-contains() {
- val="$1"
- shift
- for x in "$@"; do
- if [ "$x" = "$val" ]; then
- return 0
- fi
- done
- return 1
-}
-
-
-genBackedByList() {
+genAllBackedByList() {
out="$1"
shift
- ndk_list="$1"
- shift
rm -f "$out"
touch "$out"
- while IFS= read -r line
- do
- soFileName=$(echo "$line" | sed 's/\(.*so\).*/\1/')
- if [[ ! -z "$soFileName" && "$soFileName" != *"#"* ]]
- then
- if contains "$soFileName" "$@"; then
- echo "$soFileName" >> "$out"
- fi
- fi
- done < "$ndk_list"
+ echo "$@" >> "$out"
}
if [[ "$1" == "help" ]]
then
printHelp
-elif [[ "$#" -lt 2 ]]
+elif [[ "$#" -lt 1 ]]
then
- echo "Wrong argument length. Expecting at least 2 argument representing output path, path to ndk library list, followed by a list of libraries in the Mainline module."
+ echo "Wrong argument length. Expecting at least 1 argument representing output path, followed by a list of libraries in the Mainline module."
else
- genBackedByList "$@"
+ genAllBackedByList "$@"
fi
diff --git a/scripts/get_clang_version.py b/scripts/get_clang_version.py
index 64d922a..691c45d 100755
--- a/scripts/get_clang_version.py
+++ b/scripts/get_clang_version.py
@@ -34,7 +34,7 @@
with open(global_go) as infile:
contents = infile.read()
- regex_rev = r'\tClangDefaultVersion\s+= "(?P<rev>clang-r\d+[a-z]?\d?)"'
+ regex_rev = r'\tClangDefaultVersion\s+= "(?P<rev>clang-.*)"'
match_rev = re.search(regex_rev, contents)
if match_rev is None:
raise RuntimeError('Parsing clang info failed')
diff --git a/scripts/hiddenapi/signature_patterns.py b/scripts/hiddenapi/signature_patterns.py
index 0acb2a0..e75ee95 100755
--- a/scripts/hiddenapi/signature_patterns.py
+++ b/scripts/hiddenapi/signature_patterns.py
@@ -13,8 +13,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Generate a set of signature patterns from the modular flags generated by a
-bootclasspath_fragment that can be used to select a subset of monolithic flags
+"""Generate a set of signature patterns for a bootclasspath_fragment.
+
+The patterns are generated from the modular flags produced by the
+bootclasspath_fragment and are used to select a subset of the monolithic flags
against which the modular flags can be compared.
"""
@@ -22,33 +24,122 @@
import csv
import sys
+
def dict_reader(csvfile):
return csv.DictReader(
- csvfile, delimiter=',', quotechar='|', fieldnames=['signature']
- )
+ csvfile, delimiter=',', quotechar='|', fieldnames=['signature'])
-def produce_patterns_from_file(file):
+def dotPackageToSlashPackage(pkg):
+ return pkg.replace('.', '/')
+
+
+def slashPackageToDotPackage(pkg):
+ return pkg.replace('/', '.')
+
+
+def isSplitPackage(splitPackages, pkg):
+ return splitPackages and (pkg in splitPackages or '*' in splitPackages)
+
+
+def matchedByPackagePrefixPattern(packagePrefixes, prefix):
+ for packagePrefix in packagePrefixes:
+ if prefix == packagePrefix:
+ return packagePrefix
+ elif prefix.startswith(packagePrefix) and prefix[len(
+ packagePrefix)] == '/':
+ return packagePrefix
+ return False
+
+
+def validate_package_prefixes(splitPackages, packagePrefixes):
+ # If there are no package prefixes then there is no possible conflict
+ # between them and the split packages.
+ if len(packagePrefixes) == 0:
+ return
+
+ # Check to make sure that the split packages and package prefixes do not
+ # overlap.
+ errors = []
+ for splitPackage in splitPackages:
+ if splitPackage == '*':
+ # A package prefix matches a split package.
+ packagePrefixesForOutput = ', '.join(
+ map(slashPackageToDotPackage, packagePrefixes))
+ errors.append(
+ 'split package "*" conflicts with all package prefixes %s\n'
+ ' add split_packages:[] to fix' % packagePrefixesForOutput)
+ else:
+ packagePrefix = matchedByPackagePrefixPattern(
+ packagePrefixes, splitPackage)
+ if packagePrefix:
+ # A package prefix matches a split package.
+ splitPackageForOutput = slashPackageToDotPackage(splitPackage)
+ packagePrefixForOutput = slashPackageToDotPackage(packagePrefix)
+ errors.append(
+ 'split package %s is matched by package prefix %s' %
+ (splitPackageForOutput, packagePrefixForOutput))
+ return errors
+
+
+def validate_split_packages(splitPackages):
+ errors = []
+ if '*' in splitPackages and len(splitPackages) > 1:
+ errors.append('split packages are invalid as they contain both the'
+ ' wildcard (*) and specific packages, use the wildcard or'
+ ' specific packages, not a mixture')
+ return errors
+
+
+def produce_patterns_from_file(file, splitPackages=None, packagePrefixes=None):
with open(file, 'r') as f:
- return produce_patterns_from_stream(f)
+ return produce_patterns_from_stream(f, splitPackages, packagePrefixes)
-def produce_patterns_from_stream(stream):
- # Read in all the signatures into a list and remove member names.
+def produce_patterns_from_stream(stream,
+ splitPackages=None,
+ packagePrefixes=None):
+ splitPackages = set(splitPackages or [])
+ packagePrefixes = list(packagePrefixes or [])
+ # Read in all the signatures into a list and remove any unnecessary class
+ # and member names.
patterns = set()
for row in dict_reader(stream):
signature = row['signature']
- text = signature.removeprefix("L")
+ text = signature.removeprefix('L')
# Remove the class specific member signature
- pieces = text.split(";->")
+ pieces = text.split(';->')
qualifiedClassName = pieces[0]
- # Remove inner class names as they cannot be separated
- # from the containing outer class.
- pieces = qualifiedClassName.split("$", maxsplit=1)
- pattern = pieces[0]
+ pieces = qualifiedClassName.rsplit('/', maxsplit=1)
+ pkg = pieces[0]
+ # If the package is split across multiple modules then it cannot be used
+ # to select the subset of the monolithic flags that this module
+ # produces. In that case we need to keep the name of the class but can
+ # discard any nested class names as an outer class cannot be split
+ # across modules.
+ #
+ # If the package is not split then every class in the package must be
+ # provided by this module so there is no need to list the classes
+ # explicitly so just use the package name instead.
+ if isSplitPackage(splitPackages, pkg):
+ # Remove inner class names.
+ pieces = qualifiedClassName.split('$', maxsplit=1)
+ pattern = pieces[0]
+ else:
+ # Add a * to ensure that the pattern matches the classes in that
+ # package.
+ pattern = pkg + '/*'
patterns.add(pattern)
- patterns = list(patterns) #pylint: disable=redefined-variable-type
+ # Remove any patterns that would be matched by a package prefix pattern.
+ patterns = list(
+ filter(lambda p: not matchedByPackagePrefixPattern(packagePrefixes, p),
+ patterns))
+ # Add the package prefix patterns to the list. Add a ** to ensure that each
+ # package prefix pattern will match the classes in that package and all
+ # sub-packages.
+ patterns = patterns + list(map(lambda x: x + '/**', packagePrefixes))
+ # Sort the patterns.
patterns.sort()
return patterns
@@ -56,24 +147,47 @@
def main(args):
args_parser = argparse.ArgumentParser(
description='Generate a set of signature patterns '
- 'that select a subset of monolithic hidden API files.'
- )
+ 'that select a subset of monolithic hidden API files.')
args_parser.add_argument(
'--flags',
help='The stub flags file which contains an entry for every dex member',
)
+ args_parser.add_argument(
+ '--split-package',
+ action='append',
+ help='A package that is split across multiple bootclasspath_fragment modules'
+ )
+ args_parser.add_argument(
+ '--package-prefix',
+ action='append',
+ help='A package prefix unique to this set of flags')
args_parser.add_argument('--output', help='Generated signature prefixes')
args = args_parser.parse_args(args)
+ splitPackages = set(map(dotPackageToSlashPackage, args.split_package or []))
+ errors = validate_split_packages(splitPackages)
+
+ packagePrefixes = list(
+ map(dotPackageToSlashPackage, args.package_prefix or []))
+
+ if not errors:
+ errors = validate_package_prefixes(splitPackages, packagePrefixes)
+
+ if errors:
+ for error in errors:
+ print(error)
+ sys.exit(1)
+
# Read in all the patterns into a list.
- patterns = produce_patterns_from_file(args.flags)
+ patterns = produce_patterns_from_file(args.flags, splitPackages,
+ packagePrefixes)
# Write out all the patterns.
with open(args.output, 'w') as outputFile:
for pattern in patterns:
outputFile.write(pattern)
- outputFile.write("\n")
+ outputFile.write('\n')
-if __name__ == "__main__":
+if __name__ == '__main__':
main(sys.argv[1:])
diff --git a/scripts/hiddenapi/signature_patterns_test.py b/scripts/hiddenapi/signature_patterns_test.py
index 3babe54..b59dfd7 100755
--- a/scripts/hiddenapi/signature_patterns_test.py
+++ b/scripts/hiddenapi/signature_patterns_test.py
@@ -13,37 +13,99 @@
# WITHOUT 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 signature_patterns.py."""
import io
import unittest
-from signature_patterns import * #pylint: disable=unused-wildcard-import,wildcard-import
+from signature_patterns import * #pylint: disable=unused-wildcard-import,wildcard-import
class TestGeneratedPatterns(unittest.TestCase):
- def produce_patterns_from_string(self, csvdata):
- with io.StringIO(csvdata) as f:
- return produce_patterns_from_stream(f)
- def test_generate(self):
- #pylint: disable=line-too-long
- patterns = self.produce_patterns_from_string(
- '''
+ csvFlags = """
Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,public-api
Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
-'''
- )
- #pylint: enable=line-too-long
+"""
+
+ def produce_patterns_from_string(self,
+ csv,
+ splitPackages=None,
+ packagePrefixes=None):
+ with io.StringIO(csv) as f:
+ return produce_patterns_from_stream(f, splitPackages,
+ packagePrefixes)
+
+ def test_generate_default(self):
+ patterns = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csvFlags)
expected = [
- "java/lang/Character",
- "java/lang/Object",
- "java/lang/ProcessBuilder",
+ 'java/lang/*',
]
self.assertEqual(expected, patterns)
+ def test_generate_split_package(self):
+ patterns = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csvFlags, splitPackages={'java/lang'})
+ expected = [
+ 'java/lang/Character',
+ 'java/lang/Object',
+ 'java/lang/ProcessBuilder',
+ ]
+ self.assertEqual(expected, patterns)
+
+ def test_generate_split_package_wildcard(self):
+ patterns = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csvFlags, splitPackages={'*'})
+ expected = [
+ 'java/lang/Character',
+ 'java/lang/Object',
+ 'java/lang/ProcessBuilder',
+ ]
+ self.assertEqual(expected, patterns)
+
+ def test_generate_package_prefix(self):
+ patterns = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csvFlags, packagePrefixes={'java/lang'})
+ expected = [
+ 'java/lang/**',
+ ]
+ self.assertEqual(expected, patterns)
+
+ def test_generate_package_prefix_top_package(self):
+ patterns = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csvFlags, packagePrefixes={'java'})
+ expected = [
+ 'java/**',
+ ]
+ self.assertEqual(expected, patterns)
+
+ def test_split_package_wildcard_conflicts_with_other_split_packages(self):
+ errors = validate_split_packages({'*', 'java'})
+ expected = [
+ 'split packages are invalid as they contain both the wildcard (*)'
+ ' and specific packages, use the wildcard or specific packages,'
+ ' not a mixture'
+ ]
+ self.assertEqual(expected, errors)
+
+ def test_split_package_wildcard_conflicts_with_package_prefixes(self):
+ errors = validate_package_prefixes({'*'}, packagePrefixes={'java'})
+ expected = [
+ 'split package "*" conflicts with all package prefixes java\n'
+ ' add split_packages:[] to fix',
+ ]
+ self.assertEqual(expected, errors)
+
+ def test_split_package_conflict(self):
+ errors = validate_package_prefixes({'java/split'},
+ packagePrefixes={'java'})
+ expected = [
+ 'split package java.split is matched by package prefix java',
+ ]
+ self.assertEqual(expected, errors)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/scripts/manifest.py b/scripts/manifest.py
index 04f7405..81f9c61 100755
--- a/scripts/manifest.py
+++ b/scripts/manifest.py
@@ -123,4 +123,4 @@
def write_xml(f, doc):
f.write('<?xml version="1.0" encoding="utf-8"?>\n')
for node in doc.childNodes:
- f.write(node.toxml(encoding='utf-8') + '\n')
+ f.write(node.toxml() + '\n')
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index b4936b8..c8d4f76 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -84,6 +84,13 @@
return parser.parse_args()
+C_RED = "\033[1;31m"
+C_GREEN = "\033[1;32m"
+C_BLUE = "\033[1;34m"
+C_OFF = "\033[0m"
+C_BOLD = "\033[1m"
+
+
def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
"""Verify that the <uses-library> tags in the manifest match those provided
@@ -119,22 +126,21 @@
errmsg = ''.join([
'mismatch in the <uses-library> tags between the build system and the '
'manifest:\n',
- '\t- required libraries in build system: [%s]\n' % ', '.join(required),
- '\t vs. in the manifest: [%s]\n' %
- ', '.join(manifest_required),
- '\t- optional libraries in build system: [%s]\n' % ', '.join(optional),
- '\t vs. in the manifest: [%s]\n' %
- ', '.join(manifest_optional),
+ '\t- required libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(required), C_OFF),
+ '\t vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_required), C_OFF),
+ '\t- optional libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(optional), C_OFF),
+ '\t vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_optional), C_OFF),
'\t- tags in the manifest (%s):\n' % path,
'\t\t%s\n' % '\t\t'.join(tags),
- 'note: the following options are available:\n',
+ '%snote:%s the following options are available:\n' % (C_BLUE, C_OFF),
'\t- to temporarily disable the check on command line, rebuild with ',
- 'RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" ',
- 'and disable AOT-compilation in dexpreopt)\n',
+ '%sRELAX_USES_LIBRARY_CHECK=true%s' % (C_BOLD, C_OFF),
+ ' (this will set compiler filter "verify" and disable AOT-compilation in dexpreopt)\n',
'\t- to temporarily disable the check for the whole product, set ',
- 'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n',
- '\t- to fix the check, make build system properties coherent with the '
- 'manifest\n', '\t- see build/make/Changes.md for details\n'
+ '%sPRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true%s in the product makefiles\n' % (C_BOLD, C_OFF),
+ '\t- to fix the check, make build system properties coherent with the manifest\n',
+ '\t- for details, see %sbuild/make/Changes.md%s' % (C_GREEN, C_OFF),
+ ' and %shttps://source.android.com/devices/tech/dalvik/art-class-loader-context%s\n' % (C_GREEN, C_OFF)
])
#pylint: enable=line-too-long
@@ -329,7 +335,7 @@
if is_apk:
aapt = args.aapt if args.aapt is not None else 'aapt'
manifest = subprocess.check_output(
- [aapt, 'dump', 'badging', args.input])
+ [aapt, 'dump', 'badging', args.input]).decode('utf-8')
else:
manifest = minidom.parse(args.input)
@@ -375,12 +381,12 @@
if is_apk:
raise RuntimeError('cannot save APK manifest as XML')
- with open(args.output, 'wb') as f:
+ with open(args.output, 'w') as f:
write_xml(f, manifest)
# pylint: disable=broad-except
except Exception as err:
- print('error: ' + str(err), file=sys.stderr)
+ print('%serror:%s ' % (C_RED, C_OFF) + str(err), file=sys.stderr)
sys.exit(-1)
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index 55d0fd1..2d3103b 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -65,6 +65,9 @@
parser.add_argument('--has-no-code', dest='has_no_code', action='store_true',
help=('adds hasCode="false" attribute to application. Ignored if application elem '
'already has a hasCode attribute.'))
+ parser.add_argument('--test-only', dest='test_only', action='store_true',
+ help=('adds testOnly="true" attribute to application. Assign true value if application elem '
+ 'already has a testOnly attribute.'))
parser.add_argument('input', help='input AndroidManifest.xml file')
parser.add_argument('output', help='output AndroidManifest.xml file')
return parser.parse_args()
@@ -318,6 +321,26 @@
attr.value = 'false'
application.setAttributeNode(attr)
+def set_test_only_flag_to_true(doc):
+ manifest = parse_manifest(doc)
+ elems = get_children_with_tag(manifest, 'application')
+ application = elems[0] if len(elems) == 1 else None
+ if len(elems) > 1:
+ raise RuntimeError('found multiple <application> tags')
+ elif not elems:
+ application = doc.createElement('application')
+ indent = get_indent(manifest.firstChild, 1)
+ first = manifest.firstChild
+ manifest.insertBefore(doc.createTextNode(indent), first)
+ manifest.insertBefore(application, first)
+
+ attr = application.getAttributeNodeNS(android_ns, 'testOnly')
+ if attr is not None:
+ # Do nothing If the application already has a testOnly attribute.
+ return
+ attr = doc.createAttributeNS(android_ns, 'android:testOnly')
+ attr.value = 'true'
+ application.setAttributeNode(attr)
def main():
"""Program entry point."""
@@ -349,10 +372,13 @@
if args.has_no_code:
set_has_code_to_false(doc)
+ if args.test_only:
+ set_test_only_flag_to_true(doc)
+
if args.extract_native_libs is not None:
add_extract_native_libs(doc, args.extract_native_libs)
- with open(args.output, 'wb') as f:
+ with open(args.output, 'w') as f:
write_xml(f, doc)
# pylint: disable=broad-except
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index 3a0a25d..199b279 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -16,16 +16,16 @@
#
"""Unit tests for manifest_fixer.py."""
-import StringIO
+import io
import sys
import unittest
from xml.dom import minidom
+import xml.etree.ElementTree as ET
import manifest_fixer
sys.dont_write_bytecode = True
-
class CompareVersionGtTest(unittest.TestCase):
"""Unit tests for compare_version_gt function."""
@@ -59,7 +59,7 @@
doc = minidom.parseString(input_manifest)
manifest_fixer.raise_min_sdk_version(doc, min_sdk_version,
target_sdk_version, library)
- output = StringIO.StringIO()
+ output = io.StringIO()
manifest_fixer.write_xml(output, doc)
return output.getvalue()
@@ -80,13 +80,16 @@
attrs += ' ' + extra
return ' <uses-sdk%s/>\n' % (attrs)
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
def test_no_uses_sdk(self):
"""Tests inserting a uses-sdk element into a manifest."""
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_no_min(self):
"""Tests inserting a minSdkVersion attribute into a uses-sdk element."""
@@ -95,7 +98,7 @@
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28',
extra='extra="foo"')
output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_raise_min(self):
"""Tests inserting a minSdkVersion attribute into a uses-sdk element."""
@@ -103,7 +106,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_raise(self):
"""Tests raising a minSdkVersion attribute."""
@@ -111,7 +114,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_no_raise_min(self):
"""Tests a minSdkVersion that doesn't need raising."""
@@ -119,7 +122,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
output = self.raise_min_sdk_version_test(manifest_input, '27', '27', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_raise_codename(self):
"""Tests raising a minSdkVersion attribute to a codename."""
@@ -127,7 +130,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
expected = self.manifest_tmpl % self.uses_sdk(min='P', target='P')
output = self.raise_min_sdk_version_test(manifest_input, 'P', 'P', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_no_raise_codename(self):
"""Tests a minSdkVersion codename that doesn't need raising."""
@@ -135,7 +138,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(min='P')
expected = self.manifest_tmpl % self.uses_sdk(min='P', target='28')
output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_target(self):
"""Tests an existing targetSdkVersion is preserved."""
@@ -143,7 +146,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(min='26', target='27')
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_no_target(self):
"""Tests inserting targetSdkVersion when minSdkVersion exists."""
@@ -151,7 +154,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_target_no_min(self):
""""Tests inserting targetSdkVersion when minSdkVersion exists."""
@@ -159,7 +162,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_no_target_no_min(self):
"""Tests inserting targetSdkVersion when minSdkVersion does not exist."""
@@ -167,7 +170,7 @@
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_library_no_target(self):
"""Tests inserting targetSdkVersion when minSdkVersion exists."""
@@ -175,7 +178,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_library_target_no_min(self):
"""Tests inserting targetSdkVersion when minSdkVersion exists."""
@@ -183,7 +186,7 @@
manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_library_no_target_no_min(self):
"""Tests inserting targetSdkVersion when minSdkVersion does not exist."""
@@ -191,7 +194,7 @@
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_extra(self):
"""Tests that extra attributes and elements are maintained."""
@@ -204,12 +207,12 @@
# pylint: disable=line-too-long
expected = self.manifest_tmpl % (
' <!-- comment -->\n'
- ' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29" extra="foo"/>\n'
+ ' <uses-sdk android:minSdkVersion="28" extra="foo" android:targetSdkVersion="29"/>\n'
' <application/>\n')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_indent(self):
"""Tests that an inserted element copies the existing indentation."""
@@ -223,17 +226,20 @@
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
class AddLoggingParentTest(unittest.TestCase):
"""Unit tests for add_logging_parent function."""
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
def add_logging_parent_test(self, input_manifest, logging_parent=None):
doc = minidom.parseString(input_manifest)
if logging_parent:
manifest_fixer.add_logging_parent(doc, logging_parent)
- output = StringIO.StringIO()
+ output = io.StringIO()
manifest_fixer.write_xml(output, doc)
return output.getvalue()
@@ -257,23 +263,26 @@
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % self.uses_logging_parent()
output = self.add_logging_parent_test(manifest_input)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_logging_parent(self):
"""Tests manifest_fixer with no logging_parent."""
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % self.uses_logging_parent('FOO')
output = self.add_logging_parent_test(manifest_input, 'FOO')
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
class AddUsesLibrariesTest(unittest.TestCase):
"""Unit tests for add_uses_libraries function."""
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
def run_test(self, input_manifest, new_uses_libraries):
doc = minidom.parseString(input_manifest)
manifest_fixer.add_uses_libraries(doc, new_uses_libraries, True)
- output = StringIO.StringIO()
+ output = io.StringIO()
manifest_fixer.write_xml(output, doc)
return output.getvalue()
@@ -301,7 +310,7 @@
('bar', 'false')])
expected = manifest_input
output = self.run_test(manifest_input, [])
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_not_overwrite(self):
"""new_uses_libraries must not overwrite existing tags."""
@@ -310,7 +319,7 @@
('bar', 'false')])
expected = manifest_input
output = self.run_test(manifest_input, ['foo', 'bar'])
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_add(self):
"""New names are added with 'required:true'."""
@@ -323,7 +332,7 @@
('baz', 'true'),
('qux', 'true')])
output = self.run_test(manifest_input, ['bar', 'baz', 'qux'])
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_no_application(self):
"""When there is no <application> tag, the tag is added."""
@@ -336,7 +345,7 @@
('foo', 'true'),
('bar', 'true')])
output = self.run_test(manifest_input, ['foo', 'bar'])
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_empty_application(self):
"""Even when here is an empty <application/> tag, the libs are added."""
@@ -350,16 +359,19 @@
('foo', 'true'),
('bar', 'true')])
output = self.run_test(manifest_input, ['foo', 'bar'])
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
class AddUsesNonSdkApiTest(unittest.TestCase):
"""Unit tests for add_uses_libraries function."""
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
def run_test(self, input_manifest):
doc = minidom.parseString(input_manifest)
manifest_fixer.add_uses_non_sdk_api(doc)
- output = StringIO.StringIO()
+ output = io.StringIO()
manifest_fixer.write_xml(output, doc)
return output.getvalue()
@@ -377,23 +389,26 @@
manifest_input = self.manifest_tmpl % self.uses_non_sdk_api(False)
expected = self.manifest_tmpl % self.uses_non_sdk_api(True)
output = self.run_test(manifest_input)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_already_set(self):
"""new_uses_libraries must not overwrite existing tags."""
manifest_input = self.manifest_tmpl % self.uses_non_sdk_api(True)
expected = manifest_input
output = self.run_test(manifest_input)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
class UseEmbeddedDexTest(unittest.TestCase):
"""Unit tests for add_use_embedded_dex function."""
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
def run_test(self, input_manifest):
doc = minidom.parseString(input_manifest)
manifest_fixer.add_use_embedded_dex(doc)
- output = StringIO.StringIO()
+ output = io.StringIO()
manifest_fixer.write_xml(output, doc)
return output.getvalue()
@@ -410,13 +425,13 @@
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % self.use_embedded_dex('true')
output = self.run_test(manifest_input)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_manifest_with_use_embedded_dex(self):
manifest_input = self.manifest_tmpl % self.use_embedded_dex('true')
expected = manifest_input
output = self.run_test(manifest_input)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_manifest_with_not_use_embedded_dex(self):
manifest_input = self.manifest_tmpl % self.use_embedded_dex('false')
@@ -426,10 +441,13 @@
class AddExtractNativeLibsTest(unittest.TestCase):
"""Unit tests for add_extract_native_libs function."""
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
def run_test(self, input_manifest, value):
doc = minidom.parseString(input_manifest)
manifest_fixer.add_extract_native_libs(doc, value)
- output = StringIO.StringIO()
+ output = io.StringIO()
manifest_fixer.write_xml(output, doc)
return output.getvalue()
@@ -446,19 +464,19 @@
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % self.extract_native_libs('true')
output = self.run_test(manifest_input, True)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_set_false(self):
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % self.extract_native_libs('false')
output = self.run_test(manifest_input, False)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_match(self):
manifest_input = self.manifest_tmpl % self.extract_native_libs('true')
expected = manifest_input
output = self.run_test(manifest_input, True)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_conflict(self):
manifest_input = self.manifest_tmpl % self.extract_native_libs('true')
@@ -468,10 +486,13 @@
class AddNoCodeApplicationTest(unittest.TestCase):
"""Unit tests for set_has_code_to_false function."""
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
def run_test(self, input_manifest):
doc = minidom.parseString(input_manifest)
manifest_fixer.set_has_code_to_false(doc)
- output = StringIO.StringIO()
+ output = io.StringIO()
manifest_fixer.write_xml(output, doc)
return output.getvalue()
@@ -485,27 +506,70 @@
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % ' <application android:hasCode="false"/>\n'
output = self.run_test(manifest_input)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_has_application_no_has_code(self):
manifest_input = self.manifest_tmpl % ' <application/>\n'
expected = self.manifest_tmpl % ' <application android:hasCode="false"/>\n'
output = self.run_test(manifest_input)
- self.assertEqual(output, expected)
+ self.assert_xml_equal(output, expected)
def test_has_application_has_code_false(self):
""" Do nothing if there's already an application elemeent. """
manifest_input = self.manifest_tmpl % ' <application android:hasCode="false"/>\n'
output = self.run_test(manifest_input)
- self.assertEqual(output, manifest_input)
+ self.assert_xml_equal(output, manifest_input)
def test_has_application_has_code_true(self):
- """ Do nothing if there's already an application elemeent even if its
+ """ Do nothing if there's already an application element even if its
hasCode attribute is true. """
manifest_input = self.manifest_tmpl % ' <application android:hasCode="true"/>\n'
output = self.run_test(manifest_input)
- self.assertEqual(output, manifest_input)
+ self.assert_xml_equal(output, manifest_input)
+class AddTestOnlyApplicationTest(unittest.TestCase):
+ """Unit tests for set_test_only_flag_to_true function."""
+
+ def assert_xml_equal(self, output, expected):
+ self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
+ def run_test(self, input_manifest):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.set_test_only_flag_to_true(doc)
+ output = io.StringIO()
+ manifest_fixer.write_xml(output, doc)
+ return output.getvalue()
+
+ manifest_tmpl = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+ '%s'
+ '</manifest>\n')
+
+ def test_no_application(self):
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % ' <application android:testOnly="true"/>\n'
+ output = self.run_test(manifest_input)
+ self.assert_xml_equal(output, expected)
+
+ def test_has_application_no_test_only(self):
+ manifest_input = self.manifest_tmpl % ' <application/>\n'
+ expected = self.manifest_tmpl % ' <application android:testOnly="true"/>\n'
+ output = self.run_test(manifest_input)
+ self.assert_xml_equal(output, expected)
+
+ def test_has_application_test_only_true(self):
+ """ If there's already an application element."""
+ manifest_input = self.manifest_tmpl % ' <application android:testOnly="true"/>\n'
+ output = self.run_test(manifest_input)
+ self.assert_xml_equal(output, manifest_input)
+
+ def test_has_application_test_only_false(self):
+ """ If there's already an application element with the testOnly attribute as false."""
+ manifest_input = self.manifest_tmpl % ' <application android:testOnly="false"/>\n'
+ output = self.run_test(manifest_input)
+ self.assert_xml_equal(output, manifest_input)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/scripts/rbc-run b/scripts/rbc-run
index a0907cf..b8a6c0c 100755
--- a/scripts/rbc-run
+++ b/scripts/rbc-run
@@ -2,14 +2,17 @@
# Convert and run one configuration
# Args: a product/board makefile optionally followed by additional arguments
# that will be passed to rbcrun.
-[[ $# -gt 0 && -f "$1" ]] || { echo "Usage: ${0##*/} product.mk [Additional rbcrun arguments]" >&2; exit 1; }
+[[ $# -gt 1 && -f "$1" && -f "$2" ]] || { echo "Usage: ${0##*/} product.mk input_variables.mk [Additional rbcrun arguments]" >&2; exit 1; }
set -eu
-declare -r output_root=${OUT_DIR:-out}
-declare -r runner="$output_root/soong/.bootstrap/bin/rbcrun"
-declare -r converter="$output_root/soong/.bootstrap/bin/mk2rbc"
-declare -r launcher=$output_root/launchers/run.rbc
-declare -r makefile=$1
-shift
-$converter -mode=write -r --outdir $output_root --launcher=$launcher $makefile
-$runner RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $@ $launcher
+
+declare -r output_root="${OUT_DIR:-out}"
+declare -r runner="${output_root}/soong/rbcrun"
+declare -r converter="${output_root}/soong/mk2rbc"
+declare -r launcher="${output_root}/rbc/launcher.rbc"
+declare -r makefile_list="${output_root}/.module_paths/configuration.list"
+declare -r makefile="$1"
+declare -r input_variables="$2"
+shift 2
+"${converter}" -mode=write -r --outdir "${output_root}/rbc" --input_variables "${input_variables}" --launcher="${launcher}" --makefile_list="${makefile_list}" "${makefile}"
+"${runner}" RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $@ "${launcher}"
diff --git a/scripts/reverse-deps.sh b/scripts/reverse-deps.sh
index 02b7dcb..410f5c0 100755
--- a/scripts/reverse-deps.sh
+++ b/scripts/reverse-deps.sh
@@ -67,6 +67,9 @@
$0 ~ /^\S\S*:$/ {
inoutput = 0
}
+ $1 == "validations:" {
+ inoutput = 0
+ }
inoutput != 0 {
print gensub(/^\s*/, "", "g")" "depth
}
diff --git a/scripts/rustfmt.toml b/scripts/rustfmt.toml
index 617d425..cefaa42 100644
--- a/scripts/rustfmt.toml
+++ b/scripts/rustfmt.toml
@@ -1,5 +1,5 @@
# Android Format Style
-edition = "2018"
+edition = "2021"
use_small_heuristics = "Max"
newline_style = "Unix"
diff --git a/scripts/test_config_fixer.py b/scripts/test_config_fixer.py
index 32d5b17..c150e8c 100644
--- a/scripts/test_config_fixer.py
+++ b/scripts/test_config_fixer.py
@@ -86,7 +86,7 @@
if args.test_file_name:
overwrite_test_file_name(doc, args.test_file_name)
- with open(args.output, 'wb') as f:
+ with open(args.output, 'w') as f:
write_xml(f, doc)
# pylint: disable=broad-except
diff --git a/scripts/test_config_fixer_test.py b/scripts/test_config_fixer_test.py
index 1272c6b..d00a593 100644
--- a/scripts/test_config_fixer_test.py
+++ b/scripts/test_config_fixer_test.py
@@ -16,7 +16,7 @@
#
"""Unit tests for test_config_fixer.py."""
-import StringIO
+import io
import sys
import unittest
from xml.dom import minidom
@@ -59,7 +59,7 @@
manifest = minidom.parseString(self.manifest)
test_config_fixer.overwrite_package_name(doc, manifest, "com.soong.foo")
- output = StringIO.StringIO()
+ output = io.StringIO()
test_config_fixer.write_xml(output, doc)
# Only the matching package name in a test node should be updated.
@@ -86,7 +86,7 @@
doc = minidom.parseString(self.test_config % ("foo.apk"))
test_config_fixer.overwrite_test_file_name(doc, "bar.apk")
- output = StringIO.StringIO()
+ output = io.StringIO()
test_config_fixer.write_xml(output, doc)
# Only the matching package name in a test node should be updated.
diff --git a/scripts/transitive-deps.sh b/scripts/transitive-deps.sh
index ba36ba4..cf9fb94 100755
--- a/scripts/transitive-deps.sh
+++ b/scripts/transitive-deps.sh
@@ -100,7 +100,7 @@
currFileName = ""
currExt = ""
}
- $1 == "outputs:" {
+ $1 == "outputs:" || $1 == "validations:" {
ininput = 0
}
ininput == 0 && $0 ~ /^\S\S*:$/ {
diff --git a/scripts/unpack-prebuilt-apex.sh b/scripts/unpack-prebuilt-apex.sh
index 1acdeb5..f34a480 100755
--- a/scripts/unpack-prebuilt-apex.sh
+++ b/scripts/unpack-prebuilt-apex.sh
@@ -29,8 +29,6 @@
shift 4
REQUIRED_PATHS=$@
-set -x 1
-
rm -fr $OUTPUT_DIR
mkdir -p $OUTPUT_DIR
diff --git a/scripts/update_out b/scripts/update_out
new file mode 100755
index 0000000..d3950cf
--- /dev/null
+++ b/scripts/update_out
@@ -0,0 +1,21 @@
+#! /bin/bash
+# Run given command application and update the contents of a given file.
+# Will not change the file if its contents has not changed.
+[[ $# -gt 1 ]] || { echo "Usage: ${0##*/} FILE COMMAND" >&2; exit 1; }
+set -u
+declare -r outfile="$1"
+shift
+if [[ ! -f $outfile ]]; then
+ $@ >$outfile
+ exit
+fi
+
+declare -r newout=${outfile}.new
+$@ >$newout
+rc=$?
+if cmp -s $newout $outfile; then
+ rm $newout
+else
+ mv -f $newout $outfile
+fi
+exit $rc
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index ff2af43..2dacdb5 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -40,6 +40,7 @@
}
`, apex, fragment)),
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),
)
}
diff --git a/sdk/build_release.go b/sdk/build_release.go
index a3f0899..4c2277e 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -85,7 +85,7 @@
// Add the build releases from oldest to newest.
buildReleaseS = initBuildRelease("S")
- buildReleaseT = initBuildRelease("T")
+ buildReleaseT = initBuildRelease("Tiramisu")
)
// initBuildRelease creates a new build release with the specified name.
@@ -230,51 +230,108 @@
return container.Field(fieldIndex)
}
- zeroValue := reflect.Zero(field.Type)
- fieldPruner := func(container reflect.Value) {
- if containingStructAccessor != nil {
- // This is an embedded structure so first access the field for the embedded
- // structure.
- container = containingStructAccessor(container)
+ fieldType := field.Type
+ if selector(name, field) {
+ zeroValue := reflect.Zero(fieldType)
+ fieldPruner := func(container reflect.Value) {
+ if containingStructAccessor != nil {
+ // This is an embedded structure so first access the field for the embedded
+ // structure.
+ container = containingStructAccessor(container)
+ }
+
+ // Skip through interface and pointer values to find the structure.
+ container = getStructValue(container)
+
+ defer func() {
+ if r := recover(); r != nil {
+ panic(fmt.Errorf("%s\n\tfor field (index %d, name %s)", r, fieldIndex, name))
+ }
+ }()
+
+ // Set the field.
+ container.Field(fieldIndex).Set(zeroValue)
}
- // Skip through interface and pointer values to find the structure.
- container = getStructValue(container)
-
- defer func() {
- if r := recover(); r != nil {
- panic(fmt.Errorf("%s for fieldIndex %d of field %s of container %#v", r, fieldIndex, name, container.Interface()))
- }
- }()
-
- // Set the field.
- container.Field(fieldIndex).Set(zeroValue)
- }
-
- if selector(name, field) {
property := prunerProperty{
name,
fieldPruner,
}
p.properties = append(p.properties, property)
- } else if field.Type.Kind() == reflect.Struct {
- // Gather fields from the nested or embedded structure.
- var subNamePrefix string
- if field.Anonymous {
- subNamePrefix = namePrefix
- } else {
- subNamePrefix = name + "."
+ } else {
+ switch fieldType.Kind() {
+ case reflect.Struct:
+ // Gather fields from the nested or embedded structure.
+ var subNamePrefix string
+ if field.Anonymous {
+ subNamePrefix = namePrefix
+ } else {
+ subNamePrefix = name + "."
+ }
+ p.gatherFields(fieldType, fieldGetter, subNamePrefix, selector)
+
+ case reflect.Map:
+ // Get the type of the values stored in the map.
+ valueType := fieldType.Elem()
+ // Skip over * types.
+ if valueType.Kind() == reflect.Ptr {
+ valueType = valueType.Elem()
+ }
+ if valueType.Kind() == reflect.Struct {
+ // If this is not referenced by a pointer then it is an error as it is impossible to
+ // modify a struct that is stored directly as a value in a map.
+ if fieldType.Elem().Kind() != reflect.Ptr {
+ panic(fmt.Errorf("Cannot prune struct %s stored by value in map %s, map values must"+
+ " be pointers to structs",
+ fieldType.Elem(), name))
+ }
+
+ // Create a new pruner for the values of the map.
+ valuePruner := newPropertyPrunerForStructType(valueType, selector)
+
+ // Create a new fieldPruner that will iterate over all the items in the map and call the
+ // pruner on them.
+ fieldPruner := func(container reflect.Value) {
+ mapValue := fieldGetter(container)
+
+ for _, keyValue := range mapValue.MapKeys() {
+ itemValue := mapValue.MapIndex(keyValue)
+
+ defer func() {
+ if r := recover(); r != nil {
+ panic(fmt.Errorf("%s\n\tfor key %q", r, keyValue))
+ }
+ }()
+
+ valuePruner.pruneProperties(itemValue.Interface())
+ }
+ }
+
+ // Add the map field pruner to the list of property pruners.
+ property := prunerProperty{
+ name + "[*]",
+ fieldPruner,
+ }
+ p.properties = append(p.properties, property)
+ }
}
- p.gatherFields(field.Type, fieldGetter, subNamePrefix, selector)
}
}
}
-// pruneProperties will prune (set to zero value) any properties in the supplied struct.
+// pruneProperties will prune (set to zero value) any properties in the struct referenced by the
+// supplied struct pointer.
//
// The struct must be of the same type as was originally passed to newPropertyPruner to create this
// propertyPruner.
func (p *propertyPruner) pruneProperties(propertiesStruct interface{}) {
+
+ defer func() {
+ if r := recover(); r != nil {
+ panic(fmt.Errorf("%s\n\tof container %#v", r, propertiesStruct))
+ }
+ }()
+
structValue := reflect.ValueOf(propertiesStruct)
for _, property := range p.properties {
property.prunerFunc(structValue)
@@ -292,6 +349,13 @@
// of properties.
func newPropertyPruner(propertiesStruct interface{}, selector fieldSelectorFunc) *propertyPruner {
structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type()
+ return newPropertyPrunerForStructType(structType, selector)
+}
+
+// newPropertyPruner creates a new property pruner for the supplied properties struct type.
+//
+// The returned pruner can be used on any properties structure of the supplied type.
+func newPropertyPrunerForStructType(structType reflect.Type, selector fieldSelectorFunc) *propertyPruner {
pruner := &propertyPruner{}
pruner.gatherFields(structType, nil, "", selector)
return pruner
diff --git a/sdk/build_release_test.go b/sdk/build_release_test.go
index dff276d..6f1ef9e 100644
--- a/sdk/build_release_test.go
+++ b/sdk/build_release_test.go
@@ -15,6 +15,7 @@
package sdk
import (
+ "encoding/json"
"fmt"
"testing"
@@ -59,7 +60,7 @@
t.Run("closed range", func(t *testing.T) {
set, err := parseBuildReleaseSet("S-F1")
android.AssertDeepEquals(t, "errors", nil, err)
- android.AssertStringEquals(t, "set", "[S,T,F1]", set.String())
+ android.AssertStringEquals(t, "set", "[S,Tiramisu,F1]", set.String())
})
invalidAReleaseMessage := `unknown release "A", expected one of ` + allBuildReleaseSet.String()
t.Run("invalid release", func(t *testing.T) {
@@ -78,7 +79,7 @@
android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
})
t.Run("invalid release in closed range end", func(t *testing.T) {
- set, err := parseBuildReleaseSet("T-A")
+ set, err := parseBuildReleaseSet("Tiramisu-A")
android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set)
android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
})
@@ -125,61 +126,102 @@
F1_only string `supported_build_releases:"F1"`
}
- type testBuildReleasePruner struct {
- Default string
- S_and_T_only string `supported_build_releases:"S-T"`
- T_later string `supported_build_releases:"T+"`
- Nested nested
+ type mapped struct {
+ Default string
+ T_only string `supported_build_releases:"Tiramisu"`
}
- input := testBuildReleasePruner{
- Default: "Default",
- S_and_T_only: "S_and_T_only",
- T_later: "T_later",
- Nested: nested{
- F1_only: "F1_only",
- },
+ type testBuildReleasePruner struct {
+ Default string
+ S_and_T_only string `supported_build_releases:"S-Tiramisu"`
+ T_later string `supported_build_releases:"Tiramisu+"`
+ Nested nested
+ Mapped map[string]*mapped
+ }
+
+ inputFactory := func() testBuildReleasePruner {
+ return testBuildReleasePruner{
+ Default: "Default",
+ S_and_T_only: "S_and_T_only",
+ T_later: "T_later",
+ Nested: nested{
+ F1_only: "F1_only",
+ },
+ Mapped: map[string]*mapped{
+ "one": {
+ Default: "one-default",
+ T_only: "one-t-only",
+ },
+ "two": {
+ Default: "two-default",
+ T_only: "two-t-only",
+ },
+ },
+ }
+ }
+
+ marshal := func(t interface{}) string {
+ bytes, err := json.MarshalIndent(t, "", " ")
+ if err != nil {
+ panic(err)
+ }
+ return string(bytes)
+ }
+
+ assertJsonEquals := func(t *testing.T, expected, actual interface{}) {
+ t.Helper()
+ expectedJson := marshal(expected)
+ actualJson := marshal(actual)
+ if actualJson != expectedJson {
+ t.Errorf("test struct: expected:\n%s\n got:\n%s", expectedJson, actualJson)
+ }
}
t.Run("target S", func(t *testing.T) {
- testStruct := input
+ testStruct := inputFactory()
pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseS)
pruner.pruneProperties(&testStruct)
- expected := input
+ expected := inputFactory()
expected.T_later = ""
expected.Nested.F1_only = ""
- android.AssertDeepEquals(t, "test struct", expected, testStruct)
+ expected.Mapped["one"].T_only = ""
+ expected.Mapped["two"].T_only = ""
+ assertJsonEquals(t, expected, testStruct)
})
t.Run("target T", func(t *testing.T) {
- testStruct := input
+ testStruct := inputFactory()
pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseT)
pruner.pruneProperties(&testStruct)
- expected := input
+ expected := inputFactory()
expected.Nested.F1_only = ""
- android.AssertDeepEquals(t, "test struct", expected, testStruct)
+ assertJsonEquals(t, expected, testStruct)
})
t.Run("target F1", func(t *testing.T) {
- testStruct := input
+ testStruct := inputFactory()
pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture1)
pruner.pruneProperties(&testStruct)
- expected := input
+ expected := inputFactory()
expected.S_and_T_only = ""
- android.AssertDeepEquals(t, "test struct", expected, testStruct)
+ expected.Mapped["one"].T_only = ""
+ expected.Mapped["two"].T_only = ""
+ assertJsonEquals(t, expected, testStruct)
})
t.Run("target F2", func(t *testing.T) {
- testStruct := input
+ testStruct := inputFactory()
pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture2)
pruner.pruneProperties(&testStruct)
- expected := input
+ expected := inputFactory()
expected.S_and_T_only = ""
expected.Nested.F1_only = ""
- android.AssertDeepEquals(t, "test struct", expected, testStruct)
+ expected.Mapped["one"].T_only = ""
+ expected.Mapped["two"].T_only = ""
+ assertJsonEquals(t, expected, testStruct)
})
}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 0d9b4a0..f0d3b35 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -1319,6 +1319,58 @@
)
}
+func TestSnapshotWithJavaSdkLibrary_AnnotationsZip_PreT(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJavaSdkLibrary,
+ android.FixtureMergeEnv(map[string]string{
+ "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
+ }),
+ ).RunTestWithBp(t, `
+ sdk {
+ name: "mysdk",
+ java_sdk_libs: ["myjavalib"],
+ }
+
+ java_sdk_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ sdk_version: "current",
+ shared_library: false,
+ annotations_enabled: true,
+ public: {
+ enabled: true,
+ },
+ }
+ `)
+
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ shared_library: false,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+}
+ `),
+ checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+ `),
+ checkMergeZips(".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip"),
+ )
+}
+
func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) {
result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index b22a5b7..4190e84 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -42,8 +42,6 @@
pctx.Import("android/soong/android")
registerShBuildComponents(android.InitRegistrationContext)
-
- android.RegisterBp2BuildMutator("sh_binary", ShBinaryBp2Build)
}
func registerShBuildComponents(ctx android.RegistrationContext) {
@@ -270,18 +268,25 @@
func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
s.generateAndroidBuildActions(ctx)
installDir := android.PathForModuleInstall(ctx, "bin", proptools.String(s.properties.Sub_dir))
+ if !s.Installable() {
+ s.SkipInstall()
+ }
s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
+ for _, symlink := range s.Symlinks() {
+ ctx.InstallSymlink(installDir, symlink, s.installedFile)
+ }
}
func (s *ShBinary) AndroidMkEntries() []android.AndroidMkEntries {
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "EXECUTABLES",
OutputFile: android.OptionalPathForPath(s.outputFilePath),
- Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
+ Include: "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
s.customAndroidMkEntries(entries)
entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !s.Installable())
},
},
}}
@@ -426,11 +431,11 @@
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "NATIVE_TESTS",
OutputFile: android.OptionalPathForPath(s.outputFilePath),
- Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
+ Include: "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
s.customAndroidMkEntries(entries)
- entries.SetPath("LOCAL_MODULE_PATH", s.installDir.ToMakePath())
+ entries.SetPath("LOCAL_MODULE_PATH", s.installDir)
entries.AddCompatibilityTestSuites(s.testProperties.Test_suites...)
if s.testConfig != nil {
entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
@@ -453,6 +458,9 @@
dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
}
+ if s.testProperties.Data_bins != nil {
+ entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
+ }
if Bool(s.testProperties.Test_options.Unit_test) {
entries.SetBool("LOCAL_IS_UNIT_TEST", true)
}
@@ -509,7 +517,9 @@
}
type bazelShBinaryAttributes struct {
- Srcs bazel.LabelListAttribute
+ Srcs bazel.LabelListAttribute
+ Filename *string
+ Sub_dir *string
// Bazel also supports the attributes below, but (so far) these are not required for Bionic
// deps
// data
@@ -531,21 +541,29 @@
// visibility
}
-func ShBinaryBp2Build(ctx android.TopDownMutatorContext) {
- m, ok := ctx.Module().(*ShBinary)
- if !ok || !m.ConvertWithBp2build(ctx) {
- return
- }
-
+func (m *ShBinary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
srcs := bazel.MakeLabelListAttribute(
android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src}))
+ var filename *string
+ if m.properties.Filename != nil {
+ filename = m.properties.Filename
+ }
+
+ var subDir *string
+ if m.properties.Sub_dir != nil {
+ subDir = m.properties.Sub_dir
+ }
+
attrs := &bazelShBinaryAttributes{
- Srcs: srcs,
+ Srcs: srcs,
+ Filename: filename,
+ Sub_dir: subDir,
}
props := bazel.BazelTargetModuleProperties{
- Rule_class: "sh_binary",
+ Rule_class: "sh_binary",
+ Bzl_load_location: "//build/bazel/rules:sh_binary.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 865d5f3..89b8126 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -4,6 +4,7 @@
"os"
"path/filepath"
"strconv"
+ "strings"
"testing"
"android/soong/android"
@@ -36,13 +37,18 @@
//
// deprecated
func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
+ bp = bp + cc.GatherRequiredDepsForTest(android.Android)
+
result := prepareForShTest.RunTestWithBp(t, bp)
return result.TestContext, result.Config
}
func TestShTestSubDir(t *testing.T) {
- ctx, config := testShBinary(t, `
+ result := android.GroupFixturePreparers(
+ prepareForShTest,
+ android.FixtureModifyConfig(android.SetKatiEnabledForTests),
+ ).RunTestWithBp(t, `
sh_test {
name: "foo",
src: "test.sh",
@@ -50,17 +56,20 @@
}
`)
- mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+ mod := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
- entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
expectedPath := "out/target/product/test_device/data/nativetest64/foo_test"
actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
- android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", config, expectedPath, actualPath)
+ android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", result.Config, expectedPath, actualPath)
}
func TestShTest(t *testing.T) {
- ctx, config := testShBinary(t, `
+ result := android.GroupFixturePreparers(
+ prepareForShTest,
+ android.FixtureModifyConfig(android.SetKatiEnabledForTests),
+ ).RunTestWithBp(t, `
sh_test {
name: "foo",
src: "test.sh",
@@ -72,13 +81,13 @@
}
`)
- mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+ mod := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
- entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
expectedPath := "out/target/product/test_device/data/nativetest64/foo"
actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
- android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", config, expectedPath, actualPath)
+ android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", result.Config, expectedPath, actualPath)
expectedData := []string{":testdata/data1", ":testdata/sub/data2"}
actualData := entries.EntryMap["LOCAL_TEST_DATA"]
@@ -209,3 +218,40 @@
actualData := entries.EntryMap["LOCAL_TEST_DATA"]
android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", config, expectedData, actualData)
}
+
+func TestShTestHost_dataDeviceModulesAutogenTradefedConfig(t *testing.T) {
+ ctx, config := testShBinary(t, `
+ sh_test_host {
+ name: "foo",
+ src: "test.sh",
+ data_device_bins: ["bar"],
+ data_device_libs: ["libbar"],
+ }
+
+ cc_binary {
+ name: "bar",
+ shared_libs: ["libbar"],
+ no_libcrt: true,
+ nocrt: true,
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libbar",
+ no_libcrt: true,
+ nocrt: true,
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ buildOS := config.BuildOS.String()
+ fooModule := ctx.ModuleForTests("foo", buildOS+"_x86_64")
+
+ expectedBinAutogenConfig := `<option name="push-file" key="bar" value="/data/local/tests/unrestricted/foo/bar" />`
+ autogen := fooModule.Rule("autogen")
+ if !strings.Contains(autogen.Args["extraConfigs"], expectedBinAutogenConfig) {
+ t.Errorf("foo extraConfings %v does not contain %q", autogen.Args["extraConfigs"], expectedBinAutogenConfig)
+ }
+}
diff --git a/shared/Android.bp b/shared/Android.bp
index deb17f8..3c84f55 100644
--- a/shared/Android.bp
+++ b/shared/Android.bp
@@ -9,11 +9,13 @@
"env.go",
"paths.go",
"debug.go",
+ "proto.go",
],
testSrcs: [
"paths_test.go",
],
deps: [
"soong-bazel",
+ "golang-protobuf-proto",
],
}
diff --git a/shared/proto.go b/shared/proto.go
new file mode 100644
index 0000000..232656b
--- /dev/null
+++ b/shared/proto.go
@@ -0,0 +1,41 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package shared
+
+import (
+ "io/ioutil"
+ "os"
+
+ "google.golang.org/protobuf/proto"
+)
+
+// Save takes a protobuf message, marshals to an array of bytes
+// and is then saved to a file.
+func Save(pb proto.Message, filepath string) (err error) {
+ data, err := proto.Marshal(pb)
+ if err != nil {
+ return err
+ }
+ tempFilepath := filepath + ".tmp"
+ if err := ioutil.WriteFile(tempFilepath, []byte(data), 0644 /* rw-r--r-- */); err != nil {
+ return err
+ }
+
+ if err := os.Rename(tempFilepath, filepath); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
index 2a25a00..09a382e 100644
--- a/snapshot/host_snapshot.go
+++ b/snapshot/host_snapshot.go
@@ -58,7 +58,7 @@
android.ModuleBase
android.PackagingBase
- zipFile android.OptionalPath
+ outputFile android.OutputPath
installDir android.InstallPath
}
@@ -141,7 +141,7 @@
// Create a zip file for the binaries, and a zip of the meta data, then merge zips
depsZipFile := android.PathForModuleOut(ctx, f.Name()+"_deps.zip").OutputPath
modsZipFile := android.PathForModuleOut(ctx, f.Name()+"_mods.zip").OutputPath
- outputFile := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
+ f.outputFile = android.PathForModuleOut(ctx, f.installFileName()).OutputPath
f.installDir = android.PathForModuleInstall(ctx)
@@ -158,29 +158,24 @@
builder.Command().
BuiltTool("merge_zips").
- Output(outputFile).
+ Output(f.outputFile).
Input(metaZipFile).
Input(modsZipFile)
builder.Build("manifest", fmt.Sprintf("Adding manifest %s", f.installFileName()))
- zip := ctx.InstallFile(f.installDir, f.installFileName(), outputFile)
- f.zipFile = android.OptionalPathForPath(zip)
+ ctx.InstallFile(f.installDir, f.installFileName(), f.outputFile)
}
// Implements android.AndroidMkEntriesProvider
func (f *hostSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
- if !f.zipFile.Valid() {
- return []android.AndroidMkEntries{}
- }
-
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "ETC",
- OutputFile: f.zipFile,
- DistFiles: android.MakeDefaultDistFiles(f.zipFile.Path()),
+ OutputFile: android.OptionalPathForPath(f.outputFile),
+ DistFiles: android.MakeDefaultDistFiles(f.outputFile),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_PATH", f.installDir.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
},
},
diff --git a/snapshot/recovery_snapshot.go b/snapshot/recovery_snapshot.go
index 9b3919c..f1e31ca 100644
--- a/snapshot/recovery_snapshot.go
+++ b/snapshot/recovery_snapshot.go
@@ -71,6 +71,10 @@
ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
}
+func (RecoverySnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
+ ctx.RegisterModuleType(name, factory)
+}
+
func (RecoverySnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
// RECOVERY_SNAPSHOT_VERSION must be set to 'current' in order to generate a
// snapshot.
diff --git a/soong.bash b/soong.bash
index 8cf2ec6..db7af7c 100755
--- a/soong.bash
+++ b/soong.bash
@@ -15,8 +15,8 @@
# limitations under the License.
echo '==== ERROR: bootstrap.bash & ./soong are obsolete ====' >&2
-echo 'Use `m --skip-make` with a standalone OUT_DIR instead.' >&2
+echo 'Use `m --soong-only` with a standalone OUT_DIR instead.' >&2
echo 'Without envsetup.sh, use:' >&2
-echo ' build/soong/soong_ui.bash --make-mode --skip-make' >&2
+echo ' build/soong/soong_ui.bash --make-mode --soong-only' >&2
echo '======================================================' >&2
exit 1
diff --git a/starlark_fmt/Android.bp b/starlark_fmt/Android.bp
new file mode 100644
index 0000000..8d80ccd
--- /dev/null
+++ b/starlark_fmt/Android.bp
@@ -0,0 +1,28 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-starlark-format",
+ pkgPath: "android/soong/starlark_fmt",
+ srcs: [
+ "format.go",
+ ],
+ testSrcs: [
+ "format_test.go",
+ ],
+}
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
new file mode 100644
index 0000000..23eee59
--- /dev/null
+++ b/starlark_fmt/format.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 starlark_fmt
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+const (
+ indent = 4
+)
+
+// Indention returns an indent string of the specified level.
+func Indention(level int) string {
+ if level < 0 {
+ panic(fmt.Errorf("indent level cannot be less than 0, but got %d", level))
+ }
+ return strings.Repeat(" ", level*indent)
+}
+
+// PrintBool returns a Starlark compatible bool string.
+func PrintBool(item bool) string {
+ return strings.Title(fmt.Sprintf("%t", item))
+}
+
+// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
+func PrintStringList(items []string, indentLevel int) string {
+ return PrintList(items, indentLevel, `"%s"`)
+}
+
+// PrintList returns a Starlark-compatible string of list formmated as requested.
+func PrintList(items []string, indentLevel int, formatString string) string {
+ if len(items) == 0 {
+ return "[]"
+ } else if len(items) == 1 {
+ return fmt.Sprintf("["+formatString+"]", items[0])
+ }
+ list := make([]string, 0, len(items)+2)
+ list = append(list, "[")
+ innerIndent := Indention(indentLevel + 1)
+ for _, item := range items {
+ list = append(list, fmt.Sprintf(`%s`+formatString+`,`, innerIndent, item))
+ }
+ list = append(list, Indention(indentLevel)+"]")
+ return strings.Join(list, "\n")
+}
+
+// PrintStringListDict returns a Starlark-compatible string formatted as dictionary with
+// string keys and list of string values.
+func PrintStringListDict(dict map[string][]string, indentLevel int) string {
+ formattedValueDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ formattedValueDict[k] = PrintStringList(v, indentLevel+1)
+ }
+ return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintBoolDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintBoolDict(dict map[string]bool, indentLevel int) string {
+ formattedValueDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ formattedValueDict[k] = PrintBool(v)
+ }
+ return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintDict(dict map[string]string, indentLevel int) string {
+ if len(dict) == 0 {
+ return "{}"
+ }
+ items := make([]string, 0, len(dict))
+ for k, v := range dict {
+ items = append(items, fmt.Sprintf(`%s"%s": %s,`, Indention(indentLevel+1), k, v))
+ }
+ sort.Strings(items)
+ return fmt.Sprintf(`{
+%s
+%s}`, strings.Join(items, "\n"), Indention(indentLevel))
+}
diff --git a/starlark_fmt/format_test.go b/starlark_fmt/format_test.go
new file mode 100644
index 0000000..90f78ef
--- /dev/null
+++ b/starlark_fmt/format_test.go
@@ -0,0 +1,169 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package starlark_fmt
+
+import (
+ "testing"
+)
+
+func TestPrintEmptyStringList(t *testing.T) {
+ in := []string{}
+ indentLevel := 0
+ out := PrintStringList(in, indentLevel)
+ expectedOut := "[]"
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintSingleElementStringList(t *testing.T) {
+ in := []string{"a"}
+ indentLevel := 0
+ out := PrintStringList(in, indentLevel)
+ expectedOut := `["a"]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintMultiElementStringList(t *testing.T) {
+ in := []string{"a", "b"}
+ indentLevel := 0
+ out := PrintStringList(in, indentLevel)
+ expectedOut := `[
+ "a",
+ "b",
+]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintEmptyList(t *testing.T) {
+ in := []string{}
+ indentLevel := 0
+ out := PrintList(in, indentLevel, "%s")
+ expectedOut := "[]"
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintSingleElementList(t *testing.T) {
+ in := []string{"1"}
+ indentLevel := 0
+ out := PrintList(in, indentLevel, "%s")
+ expectedOut := `[1]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintMultiElementList(t *testing.T) {
+ in := []string{"1", "2"}
+ indentLevel := 0
+ out := PrintList(in, indentLevel, "%s")
+ expectedOut := `[
+ 1,
+ 2,
+]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestListWithNonZeroIndent(t *testing.T) {
+ in := []string{"1", "2"}
+ indentLevel := 1
+ out := PrintList(in, indentLevel, "%s")
+ expectedOut := `[
+ 1,
+ 2,
+ ]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestStringListDictEmpty(t *testing.T) {
+ in := map[string][]string{}
+ indentLevel := 0
+ out := PrintStringListDict(in, indentLevel)
+ expectedOut := `{}`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestStringListDict(t *testing.T) {
+ in := map[string][]string{
+ "key1": []string{},
+ "key2": []string{"a"},
+ "key3": []string{"1", "2"},
+ }
+ indentLevel := 0
+ out := PrintStringListDict(in, indentLevel)
+ expectedOut := `{
+ "key1": [],
+ "key2": ["a"],
+ "key3": [
+ "1",
+ "2",
+ ],
+}`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintDict(t *testing.T) {
+ in := map[string]string{
+ "key1": `""`,
+ "key2": `"a"`,
+ "key3": `[
+ 1,
+ 2,
+ ]`,
+ }
+ indentLevel := 0
+ out := PrintDict(in, indentLevel)
+ expectedOut := `{
+ "key1": "",
+ "key2": "a",
+ "key3": [
+ 1,
+ 2,
+ ],
+}`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintDictWithIndent(t *testing.T) {
+ in := map[string]string{
+ "key1": `""`,
+ "key2": `"a"`,
+ }
+ indentLevel := 1
+ out := PrintDict(in, indentLevel)
+ expectedOut := `{
+ "key1": "",
+ "key2": "a",
+ }`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
diff --git a/tests/androidmk_test.sh b/tests/androidmk_test.sh
new file mode 100755
index 0000000..331dc77
--- /dev/null
+++ b/tests/androidmk_test.sh
@@ -0,0 +1,135 @@
+#!/bin/bash -eu
+
+set -o pipefail
+
+# 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
+
+source "$(dirname "$0")/lib.sh"
+
+# Expect to create a new license module
+function test_rewrite_license_property_inside_current_directory {
+ setup
+
+ # Create an Android.mk file
+ mkdir -p a/b
+ cat > a/b/Android.mk <<'EOF'
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_LICENSE_KINDS := license_kind1 license_kind2
+LOCAL_LICENSE_CONDITIONS := license_condition
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/license_notice1 $(LOCAL_PATH)/license_notice2
+include $(BUILD_PACKAGE)
+EOF
+
+ # Create an expected Android.bp file for the module "foo"
+ cat > a/b/Android.bp <<'EOF'
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "a_b_license",
+ ],
+}
+
+license {
+ name: "a_b_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "license_kind1",
+ "license_kind2",
+ ],
+ license_text: [
+ "license_notice1",
+ "license_notice2",
+ ],
+}
+
+android_app {
+ name: "foo",
+}
+EOF
+
+ run_androidmk_test "a/b/Android.mk" "a/b/Android.bp"
+}
+
+# Expect to reference to an existing license module
+function test_rewrite_license_property_outside_current_directory {
+ setup
+
+ # Create an Android.mk file
+ mkdir -p a/b/c/d
+ cat > a/b/c/d/Android.mk <<'EOF'
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_LICENSE_KINDS := license_kind1 license_kind2
+LOCAL_LICENSE_CONDITIONS := license_condition
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../license_notice1 $(LOCAL_PATH)/../../license_notice2
+include $(BUILD_PACKAGE)
+EOF
+
+ # Create an expected (input) Android.bp file at a/b/
+ cat > a/b/Android.bp <<'EOF'
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "a_b_license",
+ ],
+}
+
+license {
+ name: "a_b_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "license_kind1",
+ "license_kind2",
+ ],
+ license_text: [
+ "license_notice1",
+ "license_notice2",
+ ],
+}
+
+android_app {
+ name: "bar",
+}
+EOF
+
+ # Create an expected (output) Android.bp file for the module "foo"
+ cat > a/b/c/d/Android.bp <<'EOF'
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "a_b_license",
+ ],
+}
+
+android_app {
+ name: "foo",
+}
+EOF
+
+ run_androidmk_test "a/b/c/d/Android.mk" "a/b/c/d/Android.bp"
+}
+
+run_androidmk_test () {
+ export ANDROID_BUILD_TOP="$MOCK_TOP"
+
+ local out=$($REAL_TOP/*/host/*/bin/androidmk "$1")
+ local expected=$(<"$2")
+
+ if [[ "$out" != "$expected" ]]; then
+ ANDROID_BUILD_TOP="$REAL_TOP"
+ cleanup_mock_top
+ fail "The output is not the same as the expected"
+ fi
+
+ ANDROID_BUILD_TOP="$REAL_TOP"
+ cleanup_mock_top
+ echo "Succeeded"
+}
+
+test_rewrite_license_property_inside_current_directory
+
+test_rewrite_license_property_outside_current_directory
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index a22adc5..e92a561 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -17,10 +17,10 @@
function test_null_build() {
setup
run_soong
- local bootstrap_mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+ local bootstrap_mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
run_soong
- local bootstrap_mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+ local bootstrap_mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then
@@ -36,12 +36,12 @@
function test_soong_build_rebuilt_if_blueprint_changes() {
setup
run_soong
- local mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+ local 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/build.ninja)
+ local mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
if [[ "$mtime1" == "$mtime2" ]]; then
fail "Bootstrap Ninja file did not change"
@@ -541,7 +541,7 @@
function test_bp2build_smoke {
setup
run_soong bp2build
- [[ -e out/soong/.bootstrap/bp2build_workspace_marker ]] || fail "bp2build marker file not created"
+ [[ -e out/soong/bp2build_workspace_marker ]] || fail "bp2build marker file not created"
[[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
}
@@ -551,7 +551,7 @@
run_soong bp2build
- if [[ ! -f "./out/soong/.bootstrap/bp2build_workspace_marker" ]]; then
+ if [[ ! -f "./out/soong/bp2build_workspace_marker" ]]; then
fail "Marker file was not generated"
fi
}
@@ -592,10 +592,10 @@
setup
run_soong bp2build
- local mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+ local mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
run_soong bp2build
- local mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+ local mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
if [[ "$mtime1" != "$mtime2" ]]; then
fail "Output Ninja file changed on null build"
@@ -626,7 +626,7 @@
function test_multiple_soong_build_modes() {
setup
run_soong json-module-graph bp2build nothing
- if [[ ! -f "out/soong/.bootstrap/bp2build_workspace_marker" ]]; then
+ if [[ ! -f "out/soong/bp2build_workspace_marker" ]]; then
fail "bp2build marker file was not generated"
fi
@@ -780,11 +780,11 @@
fail "Output Ninja file changed when switching to bp2build"
fi
- local marker_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+ local 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/.bootstrap/bp2build_workspace_marker)
+ local 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
@@ -794,7 +794,7 @@
run_soong bp2build
local output_mtime4=$(stat -c "%y" out/soong/build.ninja)
- local marker_mtime3=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+ local 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
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 379eb65..4f37c2b 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -11,10 +11,10 @@
function test_bp2build_null_build() {
setup
run_soong bp2build
- local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+ local output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
run_soong bp2build
- local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+ local 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"
@@ -36,10 +36,10 @@
touch foo/bar/a.txt foo/bar/b.txt
run_soong bp2build
- local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+ local output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
run_soong bp2build
- local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+ local 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"
@@ -105,7 +105,7 @@
# NOTE: We don't actually use the extra BUILD file for anything here
run_bazel build --package_path=out/soong/workspace //foo/...
- local the_answer_file="bazel-out/k8-fastbuild/bin/foo/convertible_soong_module/the_answer.txt"
+ local the_answer_file="bazel-out/android_target-fastbuild/bin/foo/convertible_soong_module/the_answer.txt"
if [[ ! -f "${the_answer_file}" ]]; then
fail "Expected '${the_answer_file}' to be generated, but was missing"
fi
diff --git a/tests/lib.sh b/tests/lib.sh
index e777820..e6074f8 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -105,7 +105,7 @@
}
function run_soong() {
- build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests "$@"
+ build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
}
function create_mock_bazel() {
@@ -125,7 +125,7 @@
}
run_ninja() {
- build/soong/soong_ui.bash --make-mode --skip-make --skip-soong-tests "$@"
+ build/soong/soong_ui.bash --make-mode --skip-config --soong-only --skip-soong-tests "$@"
}
info "Starting Soong integration test suite $(basename $0)"
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 6304a11..76a918b 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -3,7 +3,9 @@
set -o pipefail
TOP="$(readlink -f "$(dirname "$0")"/../../..)"
+"$TOP/build/soong/tests/androidmk_test.sh"
"$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/soong_test.sh"
+"$TOP/build/bazel/ci/rbc_regression_test.sh" aosp_arm64-userdebug
diff --git a/third_party/zip/reader_test.go b/third_party/zip/reader_test.go
index dfaae78..11c6d6e 100644
--- a/third_party/zip/reader_test.go
+++ b/third_party/zip/reader_test.go
@@ -786,7 +786,7 @@
}
}
-// Verify the number of files is sane.
+// Verify the number of files is within expected bounds
func TestIssue10956(t *testing.T) {
data := []byte("PK\x06\x06PK\x06\a0000\x00\x00\x00\x00\x00\x00\x00\x00" +
"0000PK\x05\x06000000000000" +
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index da55829..c2429ab 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -188,20 +188,20 @@
}
func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
- testSuites []string, autoGenConfig *bool, unitTest *bool) android.Path {
+ 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(), nil, "")
+ autogenTemplate(ctx, autogenPath, templatePath.String(), config, "")
} else {
if ctx.Device() {
- autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", nil, "")
+ autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", config, "")
} else {
if Bool(unitTest) {
- autogenTemplate(ctx, autogenPath, "${JavaHostUnitTestConfigTemplate}", nil, "")
+ autogenTemplate(ctx, autogenPath, "${JavaHostUnitTestConfigTemplate}", config, "")
} else {
- autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", nil, "")
+ autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", config, "")
}
}
}
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index a3a1aaf..1c80cff 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -171,6 +171,7 @@
productOut("recovery"),
productOut("root"),
productOut("system"),
+ productOut("system_dlkm"),
productOut("system_other"),
productOut("vendor"),
productOut("vendor_dlkm"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 35dacf2..1dd948c 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -15,8 +15,12 @@
package build
import (
+ "context"
+ "encoding/json"
"fmt"
+ "io/ioutil"
"os"
+ "os/exec"
"path/filepath"
"runtime"
"strconv"
@@ -30,6 +34,14 @@
smpb "android/soong/ui/metrics/metrics_proto"
)
+const (
+ envConfigDir = "vendor/google/tools/soong_config"
+ jsonSuffix = "json"
+
+ configFetcher = "vendor/google/tools/soong/expconfigfetcher"
+ envConfigFetchTimeout = 10 * time.Second
+)
+
type Config struct{ *configImpl }
type configImpl struct {
@@ -83,6 +95,8 @@
// Set by multiproduct_kati
emptyNinjaFile bool
+
+ metricsUploader string
}
const srcDirFileCheck = "build/soong/root.bp"
@@ -126,6 +140,85 @@
}
}
+// fetchEnvConfig optionally fetches environment config from an
+// experiments system to control Soong features dynamically.
+func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
+ s, err := os.Stat(configFetcher)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return err
+ }
+ if s.Mode()&0111 == 0 {
+ return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode())
+ }
+
+ configExists := false
+ outConfigFilePath := filepath.Join(config.OutDir(), envConfigName + jsonSuffix)
+ if _, err := os.Stat(outConfigFilePath); err == nil {
+ configExists = true
+ }
+
+ tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout)
+ defer cancel()
+ cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir())
+ if err := cmd.Start(); err != nil {
+ return err
+ }
+
+ // If a config file already exists, return immediately and run the config file
+ // fetch in the background. Otherwise, wait for the config file to be fetched.
+ if configExists {
+ go cmd.Wait()
+ return nil
+ }
+ if err := cmd.Wait(); err != nil {
+ return err
+ }
+ 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", err)
+ }
+
+ configDirs := []string{
+ config.OutDir(),
+ os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR"),
+ envConfigDir,
+ }
+ for _, dir := range configDirs {
+ cfgFile := filepath.Join(os.Getenv("TOP"), dir, fmt.Sprintf("%s.%s", bc, jsonSuffix))
+ envVarsJSON, err := ioutil.ReadFile(cfgFile)
+ if err != nil {
+ continue
+ }
+ ctx.Verbosef("Loading config file %v\n", cfgFile)
+ var envVars map[string]map[string]string
+ if err := json.Unmarshal(envVarsJSON, &envVars); err != nil {
+ fmt.Fprintf(os.Stderr, "Env vars config file %s did not parse correctly: %s", cfgFile, err.Error())
+ continue
+ }
+ for k, v := range envVars["env"] {
+ if os.Getenv(k) != "" {
+ continue
+ }
+ config.environ.Set(k, v)
+ }
+ ctx.Verbosef("Finished loading config file %v\n", cfgFile)
+ break
+ }
+
+ return nil
+}
+
func NewConfig(ctx Context, args ...string) Config {
ret := &configImpl{
environ: OsEnvironment(),
@@ -155,6 +248,12 @@
ret.environ.Set("OUT_DIR", outDir)
}
+ // 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)
+ }
+
if distDir, ok := ret.environ.Get("DIST_DIR"); ok {
ret.distDir = filepath.Clean(distDir)
} else {
@@ -237,7 +336,8 @@
// Precondition: the current directory is the top of the source tree
checkTopDir(ctx)
- if srcDir := absPath(ctx, "."); strings.ContainsRune(srcDir, ' ') {
+ srcDir := absPath(ctx, ".")
+ if strings.ContainsRune(srcDir, ' ') {
ctx.Println("You are building in a directory whose absolute path contains a space character:")
ctx.Println()
ctx.Printf("%q\n", srcDir)
@@ -245,6 +345,8 @@
ctx.Fatalln("Directory names containing spaces are not supported")
}
+ ret.metricsUploader = GetMetricsUploader(srcDir, ret.environ)
+
if outDir := ret.OutDir(); strings.ContainsRune(outDir, ' ') {
ctx.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:")
ctx.Println()
@@ -355,13 +457,16 @@
}
func buildConfig(config Config) *smpb.BuildConfig {
- return &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),
}
+ c.Targets = append(c.Targets, config.arguments...)
+
+ return c
}
// getConfigArgs processes the command arguments based on the build action and creates a set of new
@@ -713,10 +818,6 @@
}
func (c *configImpl) SoongBuildInvocationNeeded() bool {
- if c.Dist() {
- return true
- }
-
if len(c.Arguments()) > 0 {
// Explicit targets requested that are not special targets like b2pbuild
// or the JSON module graph
@@ -728,6 +829,11 @@
return true
}
+ // bp2build + dist may be used to dist bp2build logs but does not require SoongBuildInvocation
+ if c.Dist() && !c.Bp2Build() {
+ return true
+ }
+
// build.ninja doesn't need to be generated
return false
}
@@ -776,24 +882,25 @@
panic("Unknown GOOS")
}
}
+
func (c *configImpl) HostToolDir() string {
- return filepath.Join(c.SoongOutDir(), "host", c.PrebuiltOS(), "bin")
+ if c.SkipKatiNinja() {
+ return filepath.Join(c.SoongOutDir(), "host", c.PrebuiltOS(), "bin")
+ } else {
+ return filepath.Join(c.OutDir(), "host", c.PrebuiltOS(), "bin")
+ }
}
func (c *configImpl) NamedGlobFile(name string) string {
- return shared.JoinPath(c.SoongOutDir(), ".bootstrap/build-globs."+name+".ninja")
+ return shared.JoinPath(c.SoongOutDir(), "globs-"+name+".ninja")
}
func (c *configImpl) UsedEnvFile(tag string) string {
return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+tag)
}
-func (c *configImpl) MainNinjaFile() string {
- return shared.JoinPath(c.SoongOutDir(), "build.ninja")
-}
-
func (c *configImpl) Bp2BuildMarkerFile() string {
- return shared.JoinPath(c.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
+ return shared.JoinPath(c.SoongOutDir(), "bp2build_workspace_marker")
}
func (c *configImpl) SoongDocsHtml() string {
@@ -808,6 +915,10 @@
return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
}
+func (c *configImpl) ModuleActionsFile() string {
+ return shared.JoinPath(c.SoongOutDir(), "module-actions.json")
+}
+
func (c *configImpl) TempDir() string {
return shared.TempDirForOutDir(c.SoongOutDir())
}
@@ -978,7 +1089,7 @@
}
func (c *configImpl) UseRBE() bool {
- if v, ok := c.environ.Get("USE_RBE"); ok {
+ if v, ok := c.Environment().Get("USE_RBE"); ok {
v = strings.TrimSpace(v)
if v != "" && v != "false" {
return true
@@ -1243,22 +1354,25 @@
}
func (c *configImpl) MetricsUploaderApp() string {
- if p, ok := c.environ.Get("ANDROID_ENABLE_METRICS_UPLOAD"); ok {
- return p
- }
- return ""
+ return c.metricsUploader
}
-// LogsDir returns the logs directory where build log and metrics
-// files are located. By default, the logs directory is the out
+// LogsDir returns the absolute path to the logs directory where build log and
+// metrics files are located. By default, the logs directory is the out
// directory. If the argument dist is specified, the logs directory
// is <dist_dir>/logs.
func (c *configImpl) LogsDir() string {
+ dir := c.OutDir()
if c.Dist() {
// Always write logs to the real dist dir, even if Bazel is using a rigged dist dir for other files
- return filepath.Join(c.RealDistDir(), "logs")
+ dir = filepath.Join(c.RealDistDir(), "logs")
}
- return c.OutDir()
+ absDir, err := filepath.Abs(dir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "\nError making log dir '%s' absolute: %s\n", dir, err.Error())
+ os.Exit(1)
+ }
+ return absDir
}
// BazelMetricsDir returns the <logs dir>/bazel_metrics directory
@@ -1274,3 +1388,14 @@
func (c *configImpl) EmptyNinjaFile() bool {
return c.emptyNinjaFile
}
+
+func GetMetricsUploader(topDir string, env *Environment) string {
+ if p, ok := env.Get("METRICS_UPLOADER"); ok {
+ metricsUploader := filepath.Join(topDir, p)
+ if _, err := os.Stat(metricsUploader); err == nil {
+ return metricsUploader
+ }
+ }
+
+ return ""
+}
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 1f2158b..e293275 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -1003,6 +1003,7 @@
tests := []struct {
name string
environ Environment
+ arguments []string
useBazel bool
expectedBuildConfig *smpb.BuildConfig
}{
@@ -1074,6 +1075,20 @@
},
},
{
+ name: "specified targets",
+ environ: Environment{},
+ 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"},
+ },
+ },
+ {
name: "all set",
environ: Environment{
"FORCE_USE_GOMA=1",
@@ -1095,8 +1110,9 @@
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
c := &configImpl{
- environ: &tc.environ,
- useBazel: tc.useBazel,
+ environ: &tc.environ,
+ useBazel: tc.useBazel,
+ arguments: tc.arguments,
}
config := Config{c}
actualBuildConfig := buildConfig(config)
@@ -1106,3 +1122,65 @@
})
}
}
+
+func TestGetMetricsUploaderApp(t *testing.T) {
+
+ metricsUploaderDir := "metrics_uploader_dir"
+ metricsUploaderBinary := "metrics_uploader_binary"
+ metricsUploaderPath := filepath.Join(metricsUploaderDir, metricsUploaderBinary)
+ tests := []struct {
+ description string
+ environ Environment
+ createFiles bool
+ expected string
+ }{{
+ description: "Uploader binary exist",
+ environ: Environment{"METRICS_UPLOADER=" + metricsUploaderPath},
+ createFiles: true,
+ expected: metricsUploaderPath,
+ }, {
+ description: "Uploader binary not exist",
+ environ: Environment{"METRICS_UPLOADER=" + metricsUploaderPath},
+ createFiles: false,
+ expected: "",
+ }, {
+ description: "Uploader binary variable not set",
+ createFiles: true,
+ expected: "",
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ defer logger.Recover(func(err error) {
+ t.Fatalf("got unexpected error: %v", err)
+ })
+
+ // Create the root source tree.
+ topDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(topDir)
+
+ expected := tt.expected
+ if len(expected) > 0 {
+ expected = filepath.Join(topDir, expected)
+ }
+
+ if tt.createFiles {
+ if err := os.MkdirAll(filepath.Join(topDir, metricsUploaderDir), 0755); err != nil {
+ t.Errorf("failed to create %s directory: %v", metricsUploaderDir, err)
+ }
+ if err := ioutil.WriteFile(filepath.Join(topDir, metricsUploaderPath), []byte{}, 0644); err != nil {
+ t.Errorf("failed to create file %s: %v", expected, err)
+ }
+ }
+
+ actual := GetMetricsUploader(topDir, &tt.environ)
+
+ if actual != expected {
+ t.Errorf("expecting: %s, actual: %s", expected, actual)
+ }
+ })
+ }
+}
diff --git a/ui/build/context.go b/ui/build/context.go
index f5e987e..4a4352c 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -71,9 +71,9 @@
realTime := end - begin
c.Metrics.SetTimeMetrics(
soong_metrics_proto.PerfInfo{
- Desc: &desc,
- Name: &name,
- StartTime: &begin,
- RealTime: &realTime})
+ Description: &desc,
+ Name: &name,
+ StartTime: &begin,
+ RealTime: &realTime})
}
}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index afec829..3f10f75 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -263,9 +263,9 @@
}, exportEnvVars...), BannerVars...)
// We need Roboleaf converter and runner in the mixed mode
- runMicrofactory(ctx, config, ".bootstrap/bin/mk2rbc", "android/soong/mk2rbc/cmd",
+ runMicrofactory(ctx, config, "mk2rbc", "android/soong/mk2rbc/cmd",
map[string]string{"android/soong": "build/soong"})
- runMicrofactory(ctx, config, ".bootstrap/bin/rbcrun", "rbcrun/cmd",
+ runMicrofactory(ctx, config, "rbcrun", "rbcrun/cmd",
map[string]string{"go.starlark.net": "external/starlark-go", "rbcrun": "build/make/tools/rbcrun"})
makeVars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "")
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 8f74969..68efe21 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -87,8 +87,8 @@
// Bazel top-level file to mark a directory as a Bazel workspace.
"WORKSPACE",
},
- // Bazel Starlark configuration files.
- IncludeSuffixes: []string{".bzl"},
+ // Bazel Starlark configuration files and all .mk files for product/board configuration.
+ IncludeSuffixes: []string{".bzl", ".mk"},
}
dumpDir := config.FileListDir()
f, err = finder.New(cacheParams, filesystem, logger.New(ioutil.Discard),
@@ -110,6 +110,19 @@
return entries.DirNames, matches
}
+func findProductAndBoardConfigFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) {
+ matches := []string{}
+ for _, foundName := range entries.FileNames {
+ if foundName != "Android.mk" &&
+ foundName != "AndroidProducts.mk" &&
+ foundName != "CleanSpec.mk" &&
+ strings.HasSuffix(foundName, ".mk") {
+ matches = append(matches, foundName)
+ }
+ }
+ return entries.DirNames, matches
+}
+
// FindSources searches for source files known to <f> and writes them to the filesystem for
// use later.
func FindSources(ctx Context, config Config, f *finder.Finder) {
@@ -172,6 +185,13 @@
ctx.Fatalf("Could not find modules: %v", err)
}
+ // Recursively look for all product/board config files.
+ configurationFiles := f.FindMatching(".", findProductAndBoardConfigFiles)
+ err = dumpListToFile(ctx, config, configurationFiles, filepath.Join(dumpDir, "configuration.list"))
+ if err != nil {
+ ctx.Fatalf("Could not export product/board configuration list: %v", err)
+ }
+
if config.Dist() {
f.WaitForDbDump()
// Dist the files.db plain text database.
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 5961c45..41de6bd 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -141,6 +141,8 @@
// RBE client
"RBE_compare",
+ "RBE_num_local_reruns",
+ "RBE_num_remote_reruns",
"RBE_exec_root",
"RBE_exec_strategy",
"RBE_invocation_id",
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index d74f262..8f9a699 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -19,6 +19,7 @@
"math/rand"
"os"
"path/filepath"
+ "runtime"
"syscall"
"time"
@@ -87,6 +88,13 @@
}
vars["RBE_server_address"] = fmt.Sprintf("unix://%v", name)
}
+
+ rf := 1.0
+ if config.Parallel() < runtime.NumCPU() {
+ rf = float64(config.Parallel()) / float64(runtime.NumCPU())
+ }
+ vars["RBE_local_resource_fraction"] = fmt.Sprintf("%.2f", rf)
+
k, v := config.rbeAuth()
vars[k] = v
return vars
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 617d293..117a2a5 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,6 +15,7 @@
package build
import (
+ "fmt"
"io/ioutil"
"os"
"path/filepath"
@@ -43,6 +44,12 @@
jsonModuleGraphTag = "modulegraph"
queryviewTag = "queryview"
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
+ // incompatible changes, for example when moving the location of the bpglob binary that is
+ // executed during bootstrap before the primary builder has had a chance to update the path.
+ bootstrapEpoch = 1
)
func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error {
@@ -121,21 +128,37 @@
}
}
-func writeEmptyGlobFile(ctx Context, path string) {
+func writeEmptyFile(ctx Context, path string) {
err := os.MkdirAll(filepath.Dir(path), 0777)
if err != nil {
- ctx.Fatalf("Failed to create parent directories of empty ninja glob file '%s': %s", path, err)
+ ctx.Fatalf("Failed to create parent directories of empty file '%s': %s", path, err)
}
- if _, err := os.Stat(path); os.IsNotExist(err) {
+ 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)
if err != nil {
- ctx.Fatalf("Failed to create empty ninja glob file '%s': %s", path, err)
+ ctx.Fatalf("Failed to create empty file '%s': %s", path, err)
}
}
}
-func primaryBuilderInvocation(config Config, name string, output string, specificArgs []string) bootstrap.PrimaryBuilderInvocation {
+func fileExists(path string) (bool, error) {
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+func primaryBuilderInvocation(
+ config Config,
+ name string,
+ output string,
+ specificArgs []string,
+ description string) bootstrap.PrimaryBuilderInvocation {
commonArgs := make([]string, 0, 0)
if !config.skipSoongTests {
@@ -143,10 +166,22 @@
}
commonArgs = append(commonArgs, "-l", filepath.Join(config.FileListDir(), "Android.bp.list"))
+ invocationEnv := make(map[string]string)
+ debugMode := os.Getenv("SOONG_DELVE") != ""
- if os.Getenv("SOONG_DELVE") != "" {
+ if debugMode {
commonArgs = append(commonArgs, "--delve_listen", os.Getenv("SOONG_DELVE"))
commonArgs = append(commonArgs, "--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
+ // work that needs to be done by Delve; it uses ptrace to debug the Go
+ // process and the tracer process must deal with every signal (it is not
+ // possible to selectively ignore SIGURG). This makes debugging slower,
+ // sometimes by an order of magnitude depending on luck.
+ // The original reason for adding async preemption to Go is here:
+ // https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md
+ invocationEnv["GODEBUG"] = "asyncpreemptoff=1"
}
allArgs := make([]string, 0, 0)
@@ -160,9 +195,48 @@
allArgs = append(allArgs, "Android.bp")
return bootstrap.PrimaryBuilderInvocation{
- Inputs: []string{"Android.bp"},
- Outputs: []string{output},
- Args: allArgs,
+ Inputs: []string{"Android.bp"},
+ Outputs: []string{output},
+ Args: allArgs,
+ Description: 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
+ // worth rebuilding for.
+ Console: os.Getenv("SOONG_UNBUFFERED_OUTPUT") == "1",
+ Env: invocationEnv,
+ }
+}
+
+// bootstrapEpochCleanup deletes files used by bootstrap during incremental builds across
+// incompatible changes. Incompatible changes are marked by incrementing the bootstrapEpoch
+// constant. A tree is considered out of date for the current epoch of the
+// .soong.bootstrap.epoch.<epoch> file doesn't exist.
+func bootstrapEpochCleanup(ctx Context, config Config) {
+ epochFile := fmt.Sprintf(".soong.bootstrap.epoch.%d", bootstrapEpoch)
+ epochPath := filepath.Join(config.SoongOutDir(), epochFile)
+ if exists, err := fileExists(epochPath); err != nil {
+ ctx.Fatalf("failed to check if bootstrap epoch file %q exists: %q", epochPath, err)
+ } else if !exists {
+ // The tree is out of date for the current epoch, delete files used by bootstrap
+ // and force the primary builder to rerun.
+ os.Remove(filepath.Join(config.SoongOutDir(), "build.ninja"))
+ for _, globFile := range bootstrapGlobFileList(config) {
+ os.Remove(globFile)
+ }
+
+ // Mark the tree as up to date with the current epoch by writing the epoch marker file.
+ writeEmptyFile(ctx, epochPath)
+ }
+}
+
+func bootstrapGlobFileList(config Config) []string {
+ return []string{
+ config.NamedGlobFile(soongBuildTag),
+ config.NamedGlobFile(bp2buildTag),
+ config.NamedGlobFile(jsonModuleGraphTag),
+ config.NamedGlobFile(queryviewTag),
+ config.NamedGlobFile(soongDocsTag),
}
}
@@ -170,7 +244,10 @@
ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
defer ctx.EndTrace()
- mainSoongBuildExtraArgs := []string{"-o", config.MainNinjaFile()}
+ // Clean up some files for incremental builds across incompatible changes.
+ bootstrapEpochCleanup(ctx, config)
+
+ mainSoongBuildExtraArgs := []string{"-o", config.SoongNinjaFile()}
if config.EmptyNinjaFile() {
mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file")
}
@@ -178,8 +255,10 @@
mainSoongBuildInvocation := primaryBuilderInvocation(
config,
soongBuildTag,
- config.MainNinjaFile(),
- mainSoongBuildExtraArgs)
+ 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
@@ -195,7 +274,9 @@
config.Bp2BuildMarkerFile(),
[]string{
"--bp2build_marker", config.Bp2BuildMarkerFile(),
- })
+ },
+ fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
+ )
jsonModuleGraphInvocation := primaryBuilderInvocation(
config,
@@ -203,15 +284,21 @@
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", filepath.Join(config.SoongOutDir(), "queryview"),
- })
+ "--bazel_queryview_dir", queryviewDir,
+ },
+ fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
+ )
soongDocsInvocation := primaryBuilderInvocation(
config,
@@ -219,7 +306,9 @@
config.SoongDocsHtml(),
[]string{
"--soong_docs", config.SoongDocsHtml(),
- })
+ },
+ fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
+ )
globFiles := []string{
config.NamedGlobFile(soongBuildTag),
@@ -232,14 +321,14 @@
// The glob .ninja files are subninja'd. However, they are generated during
// the build itself so we write an empty file if the file does not exist yet
// so that the subninja doesn't fail on clean builds
- for _, globFile := range globFiles {
- writeEmptyGlobFile(ctx, globFile)
+ for _, globFile := range bootstrapGlobFileList(config) {
+ writeEmptyFile(ctx, globFile)
}
var blueprintArgs bootstrap.Args
blueprintArgs.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
- blueprintArgs.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
+ blueprintArgs.OutFile = shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja")
blueprintArgs.EmptyNinjaFile = false
blueprintCtx := blueprint.NewContext()
@@ -261,7 +350,7 @@
}
bootstrapDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
- bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
+ 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)
@@ -289,11 +378,6 @@
// unused variables were changed?
envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
- dir := filepath.Join(config.SoongOutDir(), ".bootstrap")
- if err := os.MkdirAll(dir, 0755); err != nil {
- ctx.Fatalf("Cannot mkdir " + dir)
- }
-
buildMode := config.bazelBuildMode()
integratedBp2Build := buildMode == mixedBuild
@@ -308,6 +392,7 @@
soongBuildEnv.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
+ soongBuildEnv.Set("LOG_DIR", config.LogsDir())
// For Soong bootstrapping tests
if os.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
@@ -342,7 +427,7 @@
}
}()
- runMicrofactory(ctx, config, filepath.Join(config.HostToolDir(), "bpglob"), "github.com/google/blueprint/bootstrap/bpglob",
+ runMicrofactory(ctx, config, "bpglob", "github.com/google/blueprint/bootstrap/bpglob",
map[string]string{"github.com/google/blueprint": "build/blueprint"})
ninja := func(name, ninjaFile string, targets ...string) {
@@ -401,10 +486,10 @@
if config.SoongBuildInvocationNeeded() {
// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
- targets = append(targets, config.MainNinjaFile())
+ targets = append(targets, config.SoongNinjaFile())
}
- ninja("bootstrap", ".bootstrap/build.ninja", targets...)
+ ninja("bootstrap", "bootstrap.ninja", targets...)
var soongBuildMetrics *soong_metrics_proto.SoongBuildMetrics
if shouldCollectBuildSoongMetrics(config) {
@@ -424,8 +509,7 @@
}
}
-func runMicrofactory(ctx Context, config Config, relExePath string, pkg string, mapping map[string]string) {
- name := filepath.Base(relExePath)
+func runMicrofactory(ctx Context, config Config, name string, pkg string, mapping map[string]string) {
ctx.BeginTrace(metrics.RunSoong, name)
defer ctx.EndTrace()
cfg := microfactory.Config{TrimPath: absPath(ctx, ".")}
@@ -433,7 +517,7 @@
cfg.Map(pkgPrefix, pathPrefix)
}
- exePath := filepath.Join(config.SoongOutDir(), relExePath)
+ exePath := filepath.Join(config.SoongOutDir(), name)
dir := filepath.Dir(exePath)
if err := os.MkdirAll(dir, 0777); err != nil {
ctx.Fatalf("cannot create %s: %s", dir, err)
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index f9a60b6..86c8568 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -63,7 +63,6 @@
cmd.StartOrFatal()
outDir := config.OutDir()
- bootstrapDir := filepath.Join(outDir, "soong", ".bootstrap")
modulePathsDir := filepath.Join(outDir, ".module_paths")
variablesFilePath := filepath.Join(outDir, "soong", "soong.variables")
@@ -77,6 +76,9 @@
// out/build_date.txt is considered a "source file"
buildDatetimeFilePath := filepath.Join(outDir, "build_date.txt")
+ // bpglob is built explicitly using Microfactory
+ bpglob := filepath.Join(config.SoongOutDir(), "bpglob")
+
danglingRules := make(map[string]bool)
scanner := bufio.NewScanner(stdout)
@@ -86,11 +88,11 @@
// Leaf node is not in the out directory.
continue
}
- if strings.HasPrefix(line, bootstrapDir) ||
- strings.HasPrefix(line, modulePathsDir) ||
+ if strings.HasPrefix(line, modulePathsDir) ||
line == variablesFilePath ||
line == dexpreoptConfigFilePath ||
- line == buildDatetimeFilePath {
+ line == buildDatetimeFilePath ||
+ line == bpglob {
// Leaf node is in one of Soong's bootstrap directories, which do not have
// full build rules in the primary build.ninja file.
continue
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 55ada33..687f519 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -70,12 +70,11 @@
return metricsFiles
}
-// UploadMetrics uploads a set of metrics files to a server for analysis. An
-// uploader full path is specified in ANDROID_ENABLE_METRICS_UPLOAD environment
-// variable in order to upload the set of metrics files. 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.
+// 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) {
ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
defer ctx.EndTrace()
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index b740c11..764a1e1 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -80,13 +80,10 @@
createFiles bool
files []string
}{{
- description: "ANDROID_ENABLE_METRICS_UPLOAD not set",
- }, {
- description: "no metrics files to upload",
- uploader: "fake",
+ description: "no metrics uploader",
}, {
description: "non-existent metrics files no upload",
- uploader: "fake",
+ uploader: "echo",
files: []string{"metrics_file_1", "metrics_file_2", "metrics_file_3"},
}, {
description: "trigger upload",
@@ -137,9 +134,9 @@
config := Config{&configImpl{
environ: &Environment{
"OUT_DIR=" + outDir,
- "ANDROID_ENABLE_METRICS_UPLOAD=" + tt.uploader,
},
- buildDateTime: strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10),
+ buildDateTime: strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10),
+ metricsUploader: tt.uploader,
}}
UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
@@ -192,9 +189,10 @@
config := Config{&configImpl{
environ: &Environment{
- "ANDROID_ENABLE_METRICS_UPLOAD=fake",
"OUT_DIR=/bad",
- }}}
+ },
+ metricsUploader: "echo",
+ }}
UploadMetrics(ctx, config, true, time.Now(), metricsFile)
t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 1590ab0..3ba3907 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -23,7 +23,9 @@
"golang-protobuf-proto",
"soong-ui-metrics_upload_proto",
"soong-ui-metrics_proto",
+ "soong-ui-bp2build_metrics_proto",
"soong-ui-tracer",
+ "soong-shared",
],
srcs: [
"metrics.go",
@@ -57,3 +59,15 @@
"upload_proto/upload.pb.go",
],
}
+
+bootstrap_go_package {
+ name: "soong-ui-bp2build_metrics_proto",
+ pkgPath: "android/soong/ui/metrics/bp2build_metrics_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "bp2build_metrics_proto/bp2build_metrics.pb.go",
+ ],
+}
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
new file mode 100644
index 0000000..95f02ca
--- /dev/null
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
@@ -0,0 +1,269 @@
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.9.1
+// source: bp2build_metrics.proto
+
+package bp2build_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 Bp2BuildMetrics struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Total number of Soong modules converted to generated targets
+ GeneratedModuleCount uint64 `protobuf:"varint,1,opt,name=generatedModuleCount,proto3" json:"generatedModuleCount,omitempty"`
+ // Total number of Soong modules converted to handcrafted targets
+ 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 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
+ ConvertedModules []string `protobuf:"bytes,5,rep,name=convertedModules,proto3" json:"convertedModules,omitempty"`
+ // Counts of converted modules by module type.
+ ConvertedModuleTypeCount map[string]uint64 `protobuf:"bytes,6,rep,name=convertedModuleTypeCount,proto3" json:"convertedModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+ // Counts of total modules by module type.
+ TotalModuleTypeCount map[string]uint64 `protobuf:"bytes,7,rep,name=totalModuleTypeCount,proto3" json:"totalModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+}
+
+func (x *Bp2BuildMetrics) Reset() {
+ *x = Bp2BuildMetrics{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_bp2build_metrics_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Bp2BuildMetrics) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Bp2BuildMetrics) ProtoMessage() {}
+
+func (x *Bp2BuildMetrics) ProtoReflect() protoreflect.Message {
+ mi := &file_bp2build_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 Bp2BuildMetrics.ProtoReflect.Descriptor instead.
+func (*Bp2BuildMetrics) Descriptor() ([]byte, []int) {
+ return file_bp2build_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Bp2BuildMetrics) GetGeneratedModuleCount() uint64 {
+ if x != nil {
+ return x.GeneratedModuleCount
+ }
+ return 0
+}
+
+func (x *Bp2BuildMetrics) GetHandCraftedModuleCount() uint64 {
+ if x != nil {
+ return x.HandCraftedModuleCount
+ }
+ return 0
+}
+
+func (x *Bp2BuildMetrics) GetUnconvertedModuleCount() uint64 {
+ if x != nil {
+ return x.UnconvertedModuleCount
+ }
+ return 0
+}
+
+func (x *Bp2BuildMetrics) GetRuleClassCount() map[string]uint64 {
+ if x != nil {
+ return x.RuleClassCount
+ }
+ return nil
+}
+
+func (x *Bp2BuildMetrics) GetConvertedModules() []string {
+ if x != nil {
+ return x.ConvertedModules
+ }
+ return nil
+}
+
+func (x *Bp2BuildMetrics) GetConvertedModuleTypeCount() map[string]uint64 {
+ if x != nil {
+ return x.ConvertedModuleTypeCount
+ }
+ return nil
+}
+
+func (x *Bp2BuildMetrics) GetTotalModuleTypeCount() map[string]uint64 {
+ if x != nil {
+ return x.TotalModuleTypeCount
+ }
+ return nil
+}
+
+var File_bp2build_metrics_proto protoreflect.FileDescriptor
+
+var file_bp2build_metrics_proto_rawDesc = []byte{
+ 0x0a, 0x16, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+ 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
+ 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
+ 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xac, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
+ 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,
+ 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36,
+ 0x0a, 0x16, 0x68, 0x61, 0x6e, 0x64, 0x43, 0x72, 0x61, 0x66, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16,
+ 0x68, 0x61, 0x6e, 0x64, 0x43, 0x72, 0x61, 0x66, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+ 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, 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, 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 (
+ file_bp2build_metrics_proto_rawDescOnce sync.Once
+ file_bp2build_metrics_proto_rawDescData = file_bp2build_metrics_proto_rawDesc
+)
+
+func file_bp2build_metrics_proto_rawDescGZIP() []byte {
+ file_bp2build_metrics_proto_rawDescOnce.Do(func() {
+ file_bp2build_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_bp2build_metrics_proto_rawDescData)
+ })
+ return file_bp2build_metrics_proto_rawDescData
+}
+
+var file_bp2build_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_bp2build_metrics_proto_goTypes = []interface{}{
+ (*Bp2BuildMetrics)(nil), // 0: soong_build_bp2build_metrics.Bp2BuildMetrics
+ nil, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
+ nil, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
+ nil, // 3: soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
+}
+var file_bp2build_metrics_proto_depIdxs = []int32{
+ 1, // 0: soong_build_bp2build_metrics.Bp2BuildMetrics.ruleClassCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
+ 2, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.convertedModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
+ 3, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.totalModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
+ 3, // [3:3] is the sub-list for method output_type
+ 3, // [3:3] is the sub-list for method input_type
+ 3, // [3:3] is the sub-list for extension type_name
+ 3, // [3:3] is the sub-list for extension extendee
+ 0, // [0:3] is the sub-list for field type_name
+}
+
+func init() { file_bp2build_metrics_proto_init() }
+func file_bp2build_metrics_proto_init() {
+ if File_bp2build_metrics_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_bp2build_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Bp2BuildMetrics); 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_bp2build_metrics_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_bp2build_metrics_proto_goTypes,
+ DependencyIndexes: file_bp2build_metrics_proto_depIdxs,
+ MessageInfos: file_bp2build_metrics_proto_msgTypes,
+ }.Build()
+ File_bp2build_metrics_proto = out.File
+ file_bp2build_metrics_proto_rawDesc = nil
+ file_bp2build_metrics_proto_goTypes = nil
+ file_bp2build_metrics_proto_depIdxs = nil
+}
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
new file mode 100644
index 0000000..6d98a3d
--- /dev/null
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
@@ -0,0 +1,41 @@
+// Copyright 2021 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package soong_build_bp2build_metrics;
+option go_package = "android/soong/ui/metrics/bp2build_metrics_proto";
+
+message Bp2BuildMetrics {
+ // Total number of Soong modules converted to generated targets
+ uint64 generatedModuleCount = 1;
+
+ // Total number of Soong modules converted to handcrafted targets
+ uint64 handCraftedModuleCount = 2;
+
+ // Total number of unconverted Soong modules
+ uint64 unconvertedModuleCount = 3;
+
+ // Counts of generated Bazel targets per Bazel rule class
+ map<string, uint64> ruleClassCount = 4;
+
+ // List of converted modules
+ repeated string convertedModules = 5;
+
+ // Counts of converted modules by module type.
+ map<string, uint64> convertedModuleTypeCount = 6;
+
+ // Counts of total modules by module type.
+ map<string, uint64> totalModuleTypeCount = 7;
+}
diff --git a/ui/metrics/bp2build_metrics_proto/regen.sh b/ui/metrics/bp2build_metrics_proto/regen.sh
new file mode 100755
index 0000000..bfe4294
--- /dev/null
+++ b/ui/metrics/bp2build_metrics_proto/regen.sh
@@ -0,0 +1,29 @@
+#!/bin/bash -e
+
+# 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.
+
+# 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:. bp2build_metrics.proto; then
+ die "build failed. ${error_msg}"
+fi
diff --git a/ui/metrics/event.go b/ui/metrics/event.go
index c3367e3..ebe664f 100644
--- a/ui/metrics/event.go
+++ b/ui/metrics/event.go
@@ -69,7 +69,7 @@
func (e event) perfInfo() soong_metrics_proto.PerfInfo {
realTime := uint64(_now().Sub(e.start).Nanoseconds())
return soong_metrics_proto.PerfInfo{
- Desc: proto.String(e.desc),
+ Description: proto.String(e.desc),
Name: proto.String(e.name),
StartTime: proto.Uint64(uint64(e.start.UnixNano())),
RealTime: proto.Uint64(realTime),
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index ccf9bd8..80f8c1a 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -25,24 +25,19 @@
// that captures the metrics and is them added as a perfInfo into the set
// of the collected metrics. Finally, when soong_ui has finished the build,
// the defer Dump function is invoked to store the collected metrics to the
-// raw protobuf file in the $OUT directory.
-//
-// There is one additional step that occurs after the raw protobuf file is written.
-// If the configuration environment variable ANDROID_ENABLE_METRICS_UPLOAD is
-// set with the path, the raw protobuf file is uploaded to the destination. See
-// ui/build/upload.go for more details. The filename of the raw protobuf file
-// and the list of files to be uploaded is defined in cmd/soong_ui/main.go.
-//
-// See ui/metrics/event.go for the explanation of what an event is and how
-// the metrics system is a stack based system.
+// raw protobuf file in the $OUT directory and this raw protobuf file will be
+// uploaded to the destination. See ui/build/upload.go for more details. The
+// filename of the raw protobuf file and the list of files to be uploaded is
+// defined in cmd/soong_ui/main.go. See ui/metrics/event.go for the explanation
+// of what an event is and how the metrics system is a stack based system.
import (
- "io/ioutil"
"os"
"runtime"
"strings"
"time"
+ "android/soong/shared"
"google.golang.org/protobuf/proto"
soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
@@ -201,7 +196,7 @@
}
m.metrics.HostOs = proto.String(runtime.GOOS)
- return save(&m.metrics, out)
+ return shared.Save(&m.metrics, out)
}
// SetSoongBuildMetrics sets the metrics collected from the soong_build
@@ -233,25 +228,5 @@
// Dump saves the collected CUJs metrics to the raw protobuf file.
func (c *CriticalUserJourneysMetrics) Dump(filename string) (err error) {
- return save(&c.cujs, filename)
-}
-
-// save takes a protobuf message, marshals to an array of bytes
-// and is then saved to a file.
-func save(pb proto.Message, filename string) (err error) {
- data, err := proto.Marshal(pb)
- if err != nil {
- return err
- }
-
- tempFilename := filename + ".tmp"
- if err := ioutil.WriteFile(tempFilename, []byte(data), 0644 /* rw-r--r-- */); err != nil {
- return err
- }
-
- if err := os.Rename(tempFilename, filename); err != nil {
- return err
- }
-
- return nil
+ return shared.Save(&c.cujs, filename)
}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 697e954..2e530b0 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -14,7 +14,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.26.0
+// protoc-gen-go v1.27.1
// protoc v3.9.1
// source: metrics.proto
@@ -518,6 +518,9 @@
// Whether build is occurring in a mixed build mode, where Bazel maintains the
// definition and build of some modules in cooperation with Soong.
BazelMixedBuild *bool `protobuf:"varint,5,opt,name=bazel_mixed_build,json=bazelMixedBuild" json:"bazel_mixed_build,omitempty"`
+ // 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"`
}
func (x *BuildConfig) Reset() {
@@ -587,6 +590,13 @@
return false
}
+func (x *BuildConfig) GetTargets() []string {
+ if x != nil {
+ return x.Targets
+ }
+ return nil
+}
+
type SystemResourceInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -650,7 +660,7 @@
unknownFields protoimpl.UnknownFields
// The description for the phase/action/part while the tool running.
- Desc *string `protobuf:"bytes,1,opt,name=desc" json:"desc,omitempty"`
+ Description *string `protobuf:"bytes,1,opt,name=description" json:"description,omitempty"`
// The name for the running phase/action/part.
Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
// The absolute start time.
@@ -699,9 +709,9 @@
return file_metrics_proto_rawDescGZIP(), []int{3}
}
-func (x *PerfInfo) GetDesc() string {
- if x != nil && x.Desc != nil {
- return *x.Desc
+func (x *PerfInfo) GetDescription() string {
+ if x != nil && x.Description != nil {
+ return *x.Description
}
return ""
}
@@ -1238,7 +1248,7 @@
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,
- 0xb9, 0x01, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+ 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,
@@ -1249,100 +1259,102 @@
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, 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, 0xf3, 0x01, 0x0a,
- 0x08, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73,
- 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 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,
+ 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74,
+ 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61,
+ 0x72, 0x67, 0x65, 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52,
+ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74,
+ 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65,
+ 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61,
+ 0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12,
+ 0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75,
+ 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
+ 0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x22, 0x81, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66, 0x49,
+ 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61,
+ 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c,
+ 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61,
+ 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f,
+ 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d,
+ 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63,
+ 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69,
+ 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
- 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74,
- 0x72, 0x69, 0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
- 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72,
- 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
- 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
- 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69,
- 0x63, 0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x11, 0x53, 0x6f, 0x6f,
- 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18,
- 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
- 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69,
- 0x61, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69,
- 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c,
- 0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
- 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74,
- 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f,
- 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61,
- 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61,
- 0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
- 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x28,
- 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
- 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
- 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
+ 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xb9, 0x03, 0x0a, 0x13, 0x50,
+ 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e,
+ 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74,
+ 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
+ 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
+ 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f,
+ 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x73, 0x79,
+ 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x1c,
+ 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20, 0x01,
+ 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11,
+ 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74,
+ 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61,
+ 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f,
+ 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20,
+ 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61,
+ 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74,
+ 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70,
+ 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75,
+ 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75,
+ 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74,
+ 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74,
+ 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75,
+ 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74,
+ 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74,
+ 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74,
+ 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f,
+ 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77,
+ 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b, 0x0a, 0x0c, 0x62, 0x75, 0x69,
+ 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
+ 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+ 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65,
+ 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+ 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64,
+ 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6f,
+ 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52,
+ 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x2f, 0x0a,
+ 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x0b, 0x0a, 0x07,
+ 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x4f, 0x4f,
+ 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x4b, 0x45, 0x10, 0x02, 0x22, 0x6c,
+ 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f,
+ 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+ 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42,
+ 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b,
+ 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72,
+ 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x63,
+ 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
+ 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
+ 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72,
+ 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73,
+ 0x22, 0xc3, 0x01, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d,
+ 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
+ 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11,
+ 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
+ 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c,
+ 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61,
+ 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01,
+ 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69,
+ 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73,
+ 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65,
+ 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+ 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+ 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
}
var (
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index ef42f54..db0a14a 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -123,6 +123,10 @@
// Whether build is occurring in a mixed build mode, where Bazel maintains the
// definition and build of some modules in cooperation with Soong.
optional bool bazel_mixed_build = 5;
+
+ // 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;
}
message SystemResourceInfo {
@@ -135,7 +139,7 @@
message PerfInfo {
// The description for the phase/action/part while the tool running.
- optional string desc = 1;
+ optional string description = 1;
// The name for the running phase/action/part.
optional string name = 2;
diff --git a/ui/terminal/simple_status.go b/ui/terminal/simple_status.go
index 936b275..3157813 100644
--- a/ui/terminal/simple_status.go
+++ b/ui/terminal/simple_status.go
@@ -22,32 +22,41 @@
)
type simpleStatusOutput struct {
- writer io.Writer
- formatter formatter
- keepANSI bool
+ writer io.Writer
+ formatter formatter
+ keepANSI bool
+ outputLevel status.MsgLevel
}
// 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) status.StatusOutput {
+func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool, quietBuild bool) status.StatusOutput {
+ level := status.StatusLvl
+ if quietBuild {
+ level = status.PrintLvl
+ }
return &simpleStatusOutput{
- writer: w,
- formatter: formatter,
- keepANSI: keepANSI,
+ writer: w,
+ formatter: formatter,
+ keepANSI: keepANSI,
+ outputLevel: level,
}
}
func (s *simpleStatusOutput) Message(level status.MsgLevel, message string) {
- if level >= status.StatusLvl {
+ if level >= s.outputLevel {
fmt.Fprintln(s.writer, s.formatter.message(level, message))
}
}
-func (s *simpleStatusOutput) StartAction(action *status.Action, counts status.Counts) {
+func (s *simpleStatusOutput) StartAction(_ *status.Action, _ 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 2ad174f..ff0af47 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 && isSmartTerminal(w) {
- return NewSmartStatusOutput(w, formatter)
+ if forceSimpleOutput || quietBuild || !isSmartTerminal(w) {
+ return NewSimpleStatusOutput(w, formatter, forceKeepANSI, quietBuild)
} else {
- return NewSimpleStatusOutput(w, formatter, forceKeepANSI)
+ return NewSmartStatusOutput(w, formatter)
}
}