emulate java_library static_deps with Bazel exports

In Soong, java_library can specify static_deps which are dependencies
that get aggregated into the final jar (akin to static linking). This is
useful because it allows dependencies higher up in the chain to compile
against the APIs exported by transitive dependencies. Bazel does not
support this functionality directly, but it can be emulated via the
exports attribute which makes any targets listed in the attribute public
to targets further up the chain.

Bug: 217236083
Bug: 219908977
Test: b build //external/error_prone:error_prone_core
Test: b build //external/bouncycastle:bouncycastle-host
Test: b build --platforms=//build/bazel/platforms:linux_x86
  //prebuilts/sdk/tools/jetifier/jetifier-standalone:jetifier
Change-Id: I2867e3f816de720a6f4bd9ff7a847d1b0c2da2d6
This commit is contained in:
Sam Delmerico
2022-02-25 21:34:51 +00:00
parent ad40fffa64
commit c016143602
10 changed files with 280 additions and 82 deletions

View File

@@ -485,18 +485,12 @@ var (
"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
"apex_manifest_proto_java", // b/210751803, depends on libprotobuf-java-full
"conscrypt", // b/210751803, we don't handle path property for filegroups
"conscrypt-for-host", // b/210751803, we don't handle path property for filegroups
"conscrypt", // b/210751803, we don't handle path property for filegroups
"conscrypt-for-host", // b/210751803, we don't handle path property for filegroups
"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
"mockito-robolectric-prebuilt", // 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
"libprotobuf-java-nano", // b/220869005, depends on non-public_current SDK
"host-libprotobuf-java-nano", // b/220869005, depends on libprotobuf-java-nano
"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
@@ -562,10 +556,6 @@ var (
"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?"
"libprotobuf-java-nano", // b/220869005, depends on non-public_current SDK
}
// Per-module denylist of cc_library modules to only generate the static

View File

@@ -65,6 +65,14 @@ type LabelList struct {
Excludes []Label
}
// MakeLabelList creates a LabelList from a list Label
func MakeLabelList(labels []Label) LabelList {
return LabelList{
Includes: labels,
Excludes: nil,
}
}
func (ll *LabelList) Equals(other LabelList) bool {
if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
return false
@@ -354,6 +362,15 @@ func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
return keys
}
// MakeLabelAttribute turns a string into a LabelAttribute
func MakeLabelAttribute(label string) *LabelAttribute {
return &LabelAttribute{
Value: &Label{
Label: label,
},
}
}
type configToBools map[string]bool
func (ctb configToBools) setValue(config string, value *bool) {

View File

@@ -51,7 +51,6 @@ android_app {
"srcs": `["app.java"]`,
"manifest": `"AndroidManifest.xml"`,
"resource_files": `["res/res.png"]`,
"deps": `["//prebuilts/sdk:public_current_android_sdk_java_import"]`,
}),
}})
}
@@ -87,10 +86,7 @@ android_app {
"resb/res.png",
]`,
"custom_package": `"com.google"`,
"deps": `[
"//prebuilts/sdk:public_current_android_sdk_java_import",
":static_lib_dep",
]`,
"deps": `[":static_lib_dep"]`,
}),
}})
}
@@ -129,7 +125,6 @@ android_app {
})`,
"manifest": `"AndroidManifest.xml"`,
"resource_files": `["res/res.png"]`,
"deps": `["//prebuilts/sdk:public_current_android_sdk_java_import"]`,
}),
}})
}

View File

@@ -28,6 +28,7 @@ func runJavaBinaryHostTestCase(t *testing.T, tc bp2buildTestCase) {
(&tc).moduleTypeUnderTestFactory = java.BinaryHostFactory
runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("cc_library_host_shared", cc.LibraryHostSharedFactory)
ctx.RegisterModuleType("java_library", java.LibraryFactory)
}, tc)
}
@@ -67,3 +68,33 @@ func TestJavaBinaryHost(t *testing.T) {
},
})
}
func TestJavaBinaryHostRuntimeDeps(t *testing.T) {
runJavaBinaryHostTestCase(t, bp2buildTestCase{
description: "java_binary_host with srcs, exclude_srcs, jni_libs, javacflags, and manifest.",
filesystem: fs,
blueprint: `java_binary_host {
name: "java-binary-host-1",
static_libs: ["java-dep-1"],
manifest: "test.mf",
bazel_module: { bp2build_available: true },
}
java_library {
name: "java-dep-1",
srcs: ["a.java"],
bazel_module: { bp2build_available: false },
}
`,
expectedBazelTargets: []string{
makeBazelTarget("java_binary", "java-binary-host-1", attrNameToString{
"main_class": `"com.android.test.MainClass"`,
"runtime_deps": `[":java-dep-1"]`,
"target_compatible_with": `select({
"//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
"//conditions:default": [],
})`,
}),
},
})
}

View File

@@ -15,6 +15,7 @@
package bp2build
import (
"fmt"
"testing"
"android/soong/android"
@@ -55,3 +56,76 @@ java_library {
},
})
}
func TestJavaLibraryConvertsStaticLibsToDepsAndExports(t *testing.T) {
runJavaLibraryTestCase(t, bp2buildTestCase{
blueprint: `java_library {
name: "java-lib-1",
srcs: ["a.java"],
libs: ["java-lib-2"],
static_libs: ["java-lib-3"],
bazel_module: { bp2build_available: true },
}
java_library {
name: "java-lib-2",
srcs: ["b.java"],
bazel_module: { bp2build_available: false },
}
java_library {
name: "java-lib-3",
srcs: ["c.java"],
bazel_module: { bp2build_available: false },
}`,
expectedBazelTargets: []string{
makeBazelTarget("java_library", "java-lib-1", attrNameToString{
"srcs": `["a.java"]`,
"deps": `[
":java-lib-2",
":java-lib-3",
]`,
"exports": `[":java-lib-3"]`,
}),
},
})
}
func TestJavaLibraryConvertsStaticLibsToExportsIfNoSrcs(t *testing.T) {
runJavaLibraryTestCase(t, bp2buildTestCase{
blueprint: `java_library {
name: "java-lib-1",
static_libs: ["java-lib-2"],
bazel_module: { bp2build_available: true },
}
java_library {
name: "java-lib-2",
srcs: ["a.java"],
bazel_module: { bp2build_available: false },
}`,
expectedBazelTargets: []string{
makeBazelTarget("java_library", "java-lib-1", attrNameToString{
"exports": `[":java-lib-2"]`,
}),
},
})
}
func TestJavaLibraryFailsToConvertLibsWithNoSrcs(t *testing.T) {
runJavaLibraryTestCase(t, bp2buildTestCase{
expectedErr: fmt.Errorf("Module has direct dependencies but no sources. Bazel will not allow this."),
blueprint: `java_library {
name: "java-lib-1",
libs: ["java-lib-2"],
bazel_module: { bp2build_available: true },
}
java_library {
name: "java-lib-2",
srcs: ["a.java"],
bazel_module: { bp2build_available: false },
}`,
expectedBazelTargets: []string{},
})
}

View File

@@ -70,3 +70,39 @@ java_library {
},
})
}
func TestJavaPluginNoSrcs(t *testing.T) {
runJavaPluginTestCase(t, bp2buildTestCase{
description: "java_plugin without srcs converts (static) libs to deps",
blueprint: `java_plugin {
name: "java-plug-1",
libs: ["java-lib-1"],
static_libs: ["java-lib-2"],
bazel_module: { bp2build_available: true },
}
java_library {
name: "java-lib-1",
srcs: ["b.java"],
bazel_module: { bp2build_available: false },
}
java_library {
name: "java-lib-2",
srcs: ["c.java"],
bazel_module: { bp2build_available: false },
}`,
expectedBazelTargets: []string{
makeBazelTarget("java_plugin", "java-plug-1", attrNameToString{
"target_compatible_with": `select({
"//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
"//conditions:default": [],
})`,
"deps": `[
":java-lib-1",
":java-lib-2",
]`,
}),
},
})
}

View File

@@ -89,7 +89,7 @@ func TestJavaProto(t *testing.T) {
"deps": `[":java-protos_proto"]`,
}),
makeBazelTarget("java_library", "java-protos", attrNameToString{
"deps": fmt.Sprintf(`[":%s"]`, javaLibraryName),
"exports": fmt.Sprintf(`[":%s"]`, javaLibraryName),
}),
},
})
@@ -98,7 +98,7 @@ func TestJavaProto(t *testing.T) {
func TestJavaProtoDefault(t *testing.T) {
runJavaProtoTestCase(t, bp2buildTestCase{
description: "java_proto",
description: "java_library proto default",
blueprint: `java_library_static {
name: "java-protos",
srcs: ["a.proto"],
@@ -115,7 +115,7 @@ func TestJavaProtoDefault(t *testing.T) {
"deps": `[":java-protos_proto"]`,
}),
makeBazelTarget("java_library", "java-protos", attrNameToString{
"deps": `[":java-protos_java_proto_lite"]`,
"exports": `[":java-protos_java_proto_lite"]`,
}),
},
})

View File

@@ -1450,7 +1450,8 @@ func androidAppCertificateBp2Build(ctx android.TopDownMutatorContext, module *An
}
type bazelAndroidAppAttributes struct {
*javaLibraryAttributes
*javaCommonAttributes
Deps bazel.LabelListAttribute
Manifest bazel.Label
Custom_package *string
Resource_files bazel.LabelListAttribute
@@ -1460,7 +1461,16 @@ type bazelAndroidAppAttributes struct {
// ConvertWithBp2build is used to convert android_app to Bazel.
func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
libAttrs := a.convertLibraryAttrsBp2Build(ctx)
commonAttrs, depLabels := a.convertLibraryAttrsBp2Build(ctx)
deps := depLabels.Deps
if !commonAttrs.Srcs.IsEmpty() {
deps.Append(depLabels.StaticDeps) // we should only append these if there are sources to use them
} else if !deps.IsEmpty() || !depLabels.StaticDeps.IsEmpty() {
ctx.ModuleErrorf("android_app has dynamic or static dependencies but no sources." +
" Bazel does not allow direct dependencies without sources nor exported" +
" dependencies on android_binary rule.")
}
manifest := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
@@ -1483,7 +1493,8 @@ func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
}
attrs := &bazelAndroidAppAttributes{
libAttrs,
commonAttrs,
deps,
android.BazelLabelForModuleSrcSingle(ctx, manifest),
// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
a.overridableAppProperties.Package_name,

View File

@@ -2023,13 +2023,23 @@ func addCLCFromDep(ctx android.ModuleContext, depModule android.Module,
}
}
type javaLibraryAttributes struct {
type javaCommonAttributes struct {
Srcs bazel.LabelListAttribute
Deps bazel.LabelListAttribute
Javacopts bazel.StringListAttribute
}
func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) *javaLibraryAttributes {
type javaDependencyLabels struct {
// Dependencies which DO NOT contribute to the API visible to upstream dependencies.
Deps bazel.LabelListAttribute
// Dependencies which DO contribute to the API visible to upstream dependencies.
StaticDeps bazel.LabelListAttribute
}
// convertLibraryAttrsBp2Build converts a few shared attributes from java_* modules
// and also separates dependencies into dynamic dependencies and static dependencies.
// Each corresponding Bazel target type, can have a different method for handling
// dynamic vs. static dependencies, and so these are returned to the calling function.
func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *javaDependencyLabels) {
var srcs bazel.LabelListAttribute
archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
for axis, configToProps := range archVariantProps {
@@ -2048,40 +2058,68 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext)
protoSrcPartition: android.ProtoSrcLabelPartition,
})
attrs := &javaLibraryAttributes{
commonAttrs := &javaCommonAttributes{
Srcs: srcPartitions[javaSrcPartition],
}
if m.properties.Javacflags != nil {
attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
commonAttrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
}
depLabels := &javaDependencyLabels{}
var deps bazel.LabelList
sdkVersion := m.SdkVersion(ctx)
if sdkVersion.Kind == android.SdkPublic && sdkVersion.ApiLevel == android.FutureApiLevel {
// TODO(b/220869005) remove forced dependency on current public android.jar
deps.Add(&bazel.Label{Label: "//prebuilts/sdk:public_current_android_sdk_java_import"})
}
if m.properties.Libs != nil {
deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Libs))
}
var staticDeps bazel.LabelList
if m.properties.Static_libs != nil {
//TODO(b/217236083) handle static libs similarly to Soong
deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Static_libs))
staticDeps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Static_libs))
}
protoDeps := bp2buildProto(ctx, &m.Module, srcPartitions[protoSrcPartition])
if protoDeps != nil {
deps.Add(protoDeps)
}
protoDepLabel := bp2buildProto(ctx, &m.Module, srcPartitions[protoSrcPartition])
// Soong does not differentiate between a java_library and the Bazel equivalent of
// a java_proto_library + proto_library pair. Instead, in Soong proto sources are
// listed directly in the srcs of a java_library, and the classes produced
// by protoc are included directly in the resulting JAR. Thus upstream dependencies
// that depend on a java_library with proto sources can link directly to the protobuf API,
// and so this should be a static dependency.
staticDeps.Add(protoDepLabel)
attrs.Deps = bazel.MakeLabelListAttribute(deps)
depLabels.Deps = bazel.MakeLabelListAttribute(deps)
depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps)
return attrs
return commonAttrs, depLabels
}
type javaLibraryAttributes struct {
*javaCommonAttributes
Deps bazel.LabelListAttribute
Exports bazel.LabelListAttribute
}
func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
attrs := m.convertLibraryAttrsBp2Build(ctx)
commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx)
deps := depLabels.Deps
if !commonAttrs.Srcs.IsEmpty() {
deps.Append(depLabels.StaticDeps) // we should only append these if there are sources to use them
sdkVersion := m.SdkVersion(ctx)
if sdkVersion.Kind == android.SdkPublic && sdkVersion.ApiLevel == android.FutureApiLevel {
// TODO(b/220869005) remove forced dependency on current public android.jar
deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:public_current_android_sdk_java_import"))
}
} else if !depLabels.Deps.IsEmpty() {
ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
}
attrs := &javaLibraryAttributes{
javaCommonAttributes: commonAttrs,
Deps: deps,
Exports: depLabels.StaticDeps,
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "java_library",
@@ -2092,15 +2130,30 @@ func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
}
type javaBinaryHostAttributes struct {
Srcs bazel.LabelListAttribute
Deps bazel.LabelListAttribute
Main_class string
Jvm_flags bazel.StringListAttribute
Javacopts bazel.StringListAttribute
*javaCommonAttributes
Deps bazel.LabelListAttribute
Runtime_deps bazel.LabelListAttribute
Main_class string
Jvm_flags bazel.StringListAttribute
}
// JavaBinaryHostBp2Build is for java_binary_host bp2build.
func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) {
commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx)
deps := depLabels.Deps
deps.Append(depLabels.StaticDeps)
if m.binaryProperties.Jni_libs != nil {
deps.Append(bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs)))
}
var runtimeDeps bazel.LabelListAttribute
if commonAttrs.Srcs.IsEmpty() {
// if there are no sources, then the dependencies can only be used at runtime
runtimeDeps = deps
deps = bazel.LabelListAttribute{}
}
mainClass := ""
if m.binaryProperties.Main_class != nil {
mainClass = *m.binaryProperties.Main_class
@@ -2112,26 +2165,12 @@ func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) {
}
mainClass = mainClassInManifest
}
srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs))
attrs := &javaBinaryHostAttributes{
Srcs: srcs,
Main_class: mainClass,
}
if m.properties.Javacflags != nil {
attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
}
// Attribute deps
deps := []string{}
if m.properties.Static_libs != nil {
deps = append(deps, m.properties.Static_libs...)
}
if m.binaryProperties.Jni_libs != nil {
deps = append(deps, m.binaryProperties.Jni_libs...)
}
if len(deps) > 0 {
attrs.Deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, deps))
javaCommonAttributes: commonAttrs,
Deps: deps,
Runtime_deps: runtimeDeps,
Main_class: mainClass,
}
// Attribute jvm_flags

View File

@@ -58,27 +58,32 @@ type PluginProperties struct {
}
type pluginAttributes struct {
*javaLibraryAttributes
Processor_class *string
Target_compatible_with bazel.LabelListAttribute
*javaCommonAttributes
Deps bazel.LabelListAttribute
Processor_class *string
}
// 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{},
pluginName := p.Name()
commonAttrs, depLabels := p.convertLibraryAttrsBp2Build(ctx)
deps := depLabels.Deps
deps.Append(depLabels.StaticDeps)
var processorClass *string
if p.pluginProperties.Processor_class != nil {
processorClass = p.pluginProperties.Processor_class
}
if p.pluginProperties.Processor_class != nil {
attrs.Processor_class = p.pluginProperties.Processor_class
attrs := &pluginAttributes{
javaCommonAttributes: commonAttrs,
Deps: deps,
Processor_class: processorClass,
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "java_plugin",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: p.Name()}, attrs)
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: pluginName}, attrs)
}