Handle .proto files that end up in a different package

Bazel poses a strict requirement that .proto files and proto_library
must be in the same package. This CL handles this automatically by
creating the proto_library in a separate dir/package if necessary

Implementation details
- Partition the `srcs` by package. `srcs` has been computed using
  `transformSubpackagePath`, so the information about packages is
  available at this point
- Create a proto_library in each package by using
  `CommonAttributes.Dir`. Collect all these additional libraries
  and put them in `info.Proto_libraries` so that they get added as deps
  of (cc|python|...)_proto_library
- Add an import_prefix to the proto_library in subpackages relative to
  the current directory. This relies on the assumption that every src is
  beneath the current directory (Soong will complain if a path in
  Android.bp contains ../)

filegroup module type uses a separate code-path to create proto_library.
This will be handled in the next CL in stack.

Test: bp2build unit tests
Test: TH
Test: Built the failing internal module mentioned in
b/292583584#comment1

Bug: 292583584

Change-Id: I437fc89092321b26c5f0511387cde9e84084d6f9
This commit is contained in:
Spandan Das
2023-08-03 23:02:26 +00:00
parent 3131d679f2
commit c53767e434
3 changed files with 153 additions and 41 deletions

View File

@@ -15,6 +15,7 @@
package android package android
import ( import (
"path/filepath"
"strings" "strings"
"android/soong/bazel" "android/soong/bazel"
@@ -156,12 +157,12 @@ func ProtoRule(rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
// Bp2buildProtoInfo contains information necessary to pass on to language specific conversion. // Bp2buildProtoInfo contains information necessary to pass on to language specific conversion.
type Bp2buildProtoInfo struct { type Bp2buildProtoInfo struct {
Type *string Type *string
Name string
Proto_libs bazel.LabelList Proto_libs bazel.LabelList
} }
type ProtoAttrs struct { type ProtoAttrs struct {
Srcs bazel.LabelListAttribute Srcs bazel.LabelListAttribute
Import_prefix *string
Strip_import_prefix *string Strip_import_prefix *string
Deps bazel.LabelListAttribute Deps bazel.LabelListAttribute
} }
@@ -172,6 +173,35 @@ var includeDirsToProtoDeps = map[string]string{
"external/protobuf/src": "//external/protobuf:libprotobuf-proto", "external/protobuf/src": "//external/protobuf:libprotobuf-proto",
} }
// Partitions srcs by the pkg it is in
// srcs has been created using `TransformSubpackagePaths`
// This function uses existence of Android.bp/BUILD files to create a label that is compatible with the package structure of bp2build workspace
func partitionSrcsByPackage(currentDir string, srcs bazel.LabelList) map[string]bazel.LabelList {
getPackageFromLabel := func(label string) string {
// Remove any preceding //
label = strings.TrimPrefix(label, "//")
split := strings.Split(label, ":")
if len(split) == 1 {
// e.g. foo.proto
return currentDir
} else if split[0] == "" {
// e.g. :foo.proto
return currentDir
} else {
return split[0]
}
}
pkgToSrcs := map[string]bazel.LabelList{}
for _, src := range srcs.Includes {
pkg := getPackageFromLabel(src.Label)
list := pkgToSrcs[pkg]
list.Add(&src)
pkgToSrcs[pkg] = list
}
return pkgToSrcs
}
// Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the // Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
// information necessary for language-specific handling. // information necessary for language-specific handling.
func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) { func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
@@ -197,54 +227,73 @@ func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs baz
} }
} }
info.Name = m.Name() + "_proto" name := m.Name() + "_proto"
depsFromFilegroup := protoLibraries
if len(directProtoSrcs.Includes) > 0 { if len(directProtoSrcs.Includes) > 0 {
attrs := ProtoAttrs{ pkgToSrcs := partitionSrcsByPackage(ctx.ModuleDir(), directProtoSrcs)
Srcs: bazel.MakeLabelListAttribute(directProtoSrcs), for _, pkg := range SortedStringKeys(pkgToSrcs) {
} srcs := pkgToSrcs[pkg]
attrs.Deps.Append(bazel.MakeLabelListAttribute(protoLibraries)) attrs := ProtoAttrs{
Srcs: bazel.MakeLabelListAttribute(srcs),
}
attrs.Deps.Append(bazel.MakeLabelListAttribute(depsFromFilegroup))
for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) { for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) {
for _, rawProps := range configToProps { for _, rawProps := range configToProps {
var props *ProtoProperties var props *ProtoProperties
var ok bool var ok bool
if props, ok = rawProps.(*ProtoProperties); !ok { if props, ok = rawProps.(*ProtoProperties); !ok {
ctx.ModuleErrorf("Could not cast ProtoProperties to expected type") 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
} }
if axis == bazel.NoConfigAxis {
info.Type = props.Proto.Type
for _, dir := range props.Proto.Include_dirs { if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
if dep, ok := includeDirsToProtoDeps[dir]; ok { // an empty string indicates to strips the package path
attrs.Deps.Add(bazel.MakeLabelAttribute(dep)) path := ""
} else { attrs.Strip_import_prefix = &path
ctx.PropertyErrorf("Could not find the proto_library target for include dir", dir)
} }
for _, dir := range props.Proto.Include_dirs {
if dep, ok := includeDirsToProtoDeps[dir]; ok {
attrs.Deps.Add(bazel.MakeLabelAttribute(dep))
} else {
ctx.PropertyErrorf("Could not find the proto_library target for include dir", dir)
}
}
} else if props.Proto.Type != info.Type && props.Proto.Type != nil {
ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.")
} }
} else if props.Proto.Type != info.Type && props.Proto.Type != nil {
ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.")
} }
} }
tags := ApexAvailableTagsWithoutTestApexes(ctx.(TopDownMutatorContext), ctx.Module())
// Since we are creating the proto_library in a subpackage, create an import_prefix relative to the current package
if rel, err := filepath.Rel(ctx.ModuleDir(), pkg); err != nil {
ctx.ModuleErrorf("Could not get relative path for %v %v", pkg, err)
} else if rel != "." {
attrs.Import_prefix = &rel
}
ctx.CreateBazelTargetModule(
bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
CommonAttributes{Name: name, Dir: proptools.StringPtr(pkg), Tags: tags},
&attrs,
)
l := ""
if pkg == ctx.ModuleDir() { // same package that the original module lives in
l = ":" + name
} else {
l = "//" + pkg + ":" + name
}
protoLibraries.Add(&bazel.Label{
Label: l,
})
} }
tags := ApexAvailableTagsWithoutTestApexes(ctx.(TopDownMutatorContext), ctx.Module())
ctx.CreateBazelTargetModule(
bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
CommonAttributes{Name: info.Name, Tags: tags},
&attrs,
)
protoLibraries.Add(&bazel.Label{
Label: ":" + info.Name,
})
} }
info.Proto_libs = protoLibraries info.Proto_libs = protoLibraries

View File

@@ -4903,3 +4903,67 @@ cc_library_shared {
}, },
}) })
} }
// Bazel enforces that proto_library and the .proto file are in the same bazel package
func TestGenerateProtoLibraryInSamePackage(t *testing.T) {
tc := Bp2buildTestCase{
Description: "cc_library depends on .proto files from multiple packages",
ModuleTypeUnderTest: "cc_library",
ModuleTypeUnderTestFactory: cc.LibraryFactory,
Blueprint: `
cc_library_static {
name: "foo",
srcs: [
"foo.proto",
"bar/bar.proto", // Different package because there is a bar/Android.bp
"baz/subbaz/baz.proto", // Different package because there is baz/subbaz/Android.bp
],
}
` + simpleModuleDoNotConvertBp2build("cc_library", "libprotobuf-cpp-lite"),
Filesystem: map[string]string{
"bar/Android.bp": "",
"baz/subbaz/Android.bp": "",
},
}
// We will run the test 3 times and check in the root, bar and baz/subbaz directories
// Root dir
tc.ExpectedBazelTargets = []string{
MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
"local_includes": `["."]`,
"deps": `[":libprotobuf-cpp-lite"]`,
"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
}),
MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["foo.proto"]`,
}),
MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
"deps": `[
":foo_proto",
"//bar:foo_proto",
"//baz/subbaz:foo_proto",
]`,
}),
}
runCcLibraryTestCase(t, tc)
// bar dir
tc.Dir = "bar"
tc.ExpectedBazelTargets = []string{
MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["//bar:bar.proto"]`,
"import_prefix": `"bar"`,
}),
}
runCcLibraryTestCase(t, tc)
// baz/subbaz dir
tc.Dir = "baz/subbaz"
tc.ExpectedBazelTargets = []string{
MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
"srcs": `["//baz/subbaz:baz.proto"]`,
"import_prefix": `"baz/subbaz"`,
}),
}
runCcLibraryTestCase(t, tc)
}

View File

@@ -73,7 +73,6 @@ func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownM
if !partitionedSrcs["proto"].IsEmpty() { if !partitionedSrcs["proto"].IsEmpty() {
protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"]) protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
pyProtoLibraryName := m.Name() + "_py_proto" pyProtoLibraryName := m.Name() + "_py_proto"
ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{ ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
@@ -82,7 +81,7 @@ func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownM
}, android.CommonAttributes{ }, android.CommonAttributes{
Name: pyProtoLibraryName, Name: pyProtoLibraryName,
}, &bazelPythonProtoLibraryAttributes{ }, &bazelPythonProtoLibraryAttributes{
Deps: bazel.MakeSingleLabelListAttribute(protoLabel), Deps: bazel.MakeLabelListAttribute(protoInfo.Proto_libs),
}) })
attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName)) attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))