From c768102bce5d64f93559a75783edb5509669f58c Mon Sep 17 00:00:00 2001 From: Sam Delmerico Date: Fri, 4 Feb 2022 21:01:20 +0000 Subject: [PATCH] convert java proto libraries with bp2build Allow java_libraries that depend on protobufs to be converted with bp2build. Bug: 215230097 Test: build/bazel/ci/bp2build.sh Change-Id: I3ce52389e7e4e82755605ee277c1e527a6aebc6b --- android/filegroup.go | 7 ++ android/proto.go | 22 ++++- bp2build/java_proto_conversion_test.go | 124 +++++++++++++++++++++++++ cc/bp2build.go | 33 ++----- cc/proto.go | 2 +- java/java.go | 16 +++- java/proto.go | 56 +++++++++++ 7 files changed, 229 insertions(+), 31 deletions(-) create mode 100644 bp2build/java_proto_conversion_test.go diff --git a/android/filegroup.go b/android/filegroup.go index c932ffad2..d2ff97dd8 100644 --- a/android/filegroup.go +++ b/android/filegroup.go @@ -18,6 +18,8 @@ import ( "strings" "android/soong/bazel" + + "github.com/google/blueprint" ) func init() { @@ -28,6 +30,11 @@ var PrepareForTestWithFilegroup = FixtureRegisterWithContext(func(ctx Registrati ctx.RegisterModuleType("filegroup", FileGroupFactory) }) +// IsFilegroup checks that a module is a filegroup type +func IsFilegroup(ctx bazel.OtherModuleContext, m blueprint.Module) bool { + return ctx.OtherModuleType(m) == "filegroup" +} + // https://docs.bazel.build/versions/master/be/general.html#filegroup type bazelFilegroupAttributes struct { Srcs bazel.LabelListAttribute diff --git a/android/proto.go b/android/proto.go index f466261d8..c3759f8ad 100644 --- a/android/proto.go +++ b/android/proto.go @@ -16,6 +16,7 @@ package android import ( "android/soong/bazel" + "regexp" "strings" "github.com/google/blueprint" @@ -26,6 +27,14 @@ const ( canonicalPathFromRootDefault = true ) +var ( + // ignoring case, checks for proto or protos as an independent word in the name, whether at the + // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match + filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)") + + ProtoSrcLabelPartition = bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup} +) + // TODO(ccross): protos are often used to communicate between multiple modules. If the only // way to convert a proto to source is to reference it as a source file, and external modules cannot // reference source files in other modules, then every module that owns a proto file will need to @@ -165,12 +174,11 @@ type protoAttrs struct { // Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the // information necessary for language-specific handling. -func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, module Module, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) { +func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) { var info Bp2buildProtoInfo if srcs.IsEmpty() { return info, false } - m := module.base() info.Name = m.Name() + "_proto" attrs := protoAttrs{ @@ -205,3 +213,13 @@ func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, module Module, srcs baz return info, true } + +func isProtoFilegroup(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) { + m, exists := ctx.ModuleFromName(label.OriginalModuleName) + labelStr := label.Label + if !exists || !IsFilegroup(ctx, m) { + return labelStr, false + } + likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName) + return labelStr, likelyProtos +} diff --git a/bp2build/java_proto_conversion_test.go b/bp2build/java_proto_conversion_test.go new file mode 100644 index 000000000..93b0677a6 --- /dev/null +++ b/bp2build/java_proto_conversion_test.go @@ -0,0 +1,124 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bp2build + +import ( + "fmt" + "testing" + + "android/soong/android" + "android/soong/java" +) + +func runJavaProtoTestCase(t *testing.T, tc bp2buildTestCase) { + t.Helper() + (&tc).moduleTypeUnderTest = "java_library_static" + (&tc).moduleTypeUnderTestFactory = java.LibraryFactory + runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc) +} + +func TestJavaProto(t *testing.T) { + testCases := []struct { + protoType string + javaLibraryType string + javaLibraryNameExtension string + }{ + { + protoType: "nano", + javaLibraryType: "java_nano_proto_library", + javaLibraryNameExtension: "java_proto_nano", + }, + { + protoType: "micro", + javaLibraryType: "java_micro_proto_library", + javaLibraryNameExtension: "java_proto_micro", + }, + { + protoType: "lite", + javaLibraryType: "java_lite_proto_library", + javaLibraryNameExtension: "java_proto_lite", + }, + { + protoType: "stream", + javaLibraryType: "java_stream_proto_library", + javaLibraryNameExtension: "java_proto_stream", + }, + { + protoType: "full", + javaLibraryType: "java_proto_library", + javaLibraryNameExtension: "java_proto", + }, + } + + bp := `java_library_static { + name: "java-protos", + proto: { + type: "%s", + }, + srcs: ["a.proto"], +}` + + protoLibrary := makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{ + "srcs": `["a.proto"]`, + "strip_import_prefix": `""`, + }) + + for _, tc := range testCases { + javaLibraryName := fmt.Sprintf("java-protos_%s", tc.javaLibraryNameExtension) + + runJavaProtoTestCase(t, bp2buildTestCase{ + description: fmt.Sprintf("java_proto %s", tc.protoType), + blueprint: fmt.Sprintf(bp, tc.protoType), + expectedBazelTargets: []string{ + protoLibrary, + makeBazelTarget( + tc.javaLibraryType, + javaLibraryName, + attrNameToString{ + "deps": `[":java-protos_proto"]`, + }), + makeBazelTarget("java_library", "java-protos", attrNameToString{ + "deps": fmt.Sprintf(`[":%s"]`, javaLibraryName), + }), + }, + }) + } +} + +func TestJavaProtoDefault(t *testing.T) { + runJavaProtoTestCase(t, bp2buildTestCase{ + description: "java_proto", + blueprint: `java_library_static { + name: "java-protos", + srcs: ["a.proto"], +} +`, + expectedBazelTargets: []string{ + makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{ + "srcs": `["a.proto"]`, + "strip_import_prefix": `""`, + }), + makeBazelTarget( + "java_lite_proto_library", + "java-protos_java_proto_lite", + attrNameToString{ + "deps": `[":java-protos_proto"]`, + }), + makeBazelTarget("java_library", "java-protos", attrNameToString{ + "deps": `[":java-protos_java_proto_lite"]`, + }), + }, + }) +} diff --git a/cc/bp2build.go b/cc/bp2build.go index 42fc0e494..379d6f246 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -16,7 +16,6 @@ package cc import ( "fmt" "path/filepath" - "regexp" "strings" "android/soong/android" @@ -34,12 +33,6 @@ const ( 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 { @@ -61,46 +54,32 @@ type staticOrSharedAttributes struct { Enabled bazel.BoolAttribute } +// groupSrcsByExtension partitions `srcs` into groups based on file extension. func groupSrcsByExtension(ctx android.BazelConversionPathContext, srcs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute { - // Check that a module is a filegroup type - isFilegroup := func(m blueprint.Module) bool { - return ctx.OtherModuleType(m) == "filegroup" - } - // Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl // macro. addSuffixForFilegroup := func(suffix string) bazel.LabelMapper { return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) { m, exists := ctx.ModuleFromName(label.OriginalModuleName) labelStr := label.Label - if !exists || !isFilegroup(m) { + if !exists || !android.IsFilegroup(ctx, m) { return labelStr, false } return labelStr + suffix, true } } - isProtoFilegroup := func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) { - m, exists := ctx.ModuleFromName(label.OriginalModuleName) - labelStr := label.Label - if !exists || !isFilegroup(m) { - return labelStr, false - } - likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName) - return labelStr, likelyProtos - } - // TODO(b/190006308): Handle language detection of sources in a Bazel rule. - partitioned := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{ - protoSrcPartition: bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup}, + labels := bazel.LabelPartitions{ + protoSrcPartition: android.ProtoSrcLabelPartition, cSrcPartition: bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")}, asSrcPartition: bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")}, // C++ is the "catch-all" group, and comprises generated sources because we don't // know the language of these sources until the genrule is executed. cppSrcPartition: bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true}, - }) + } - return partitioned + return bazel.PartitionLabelListAttribute(ctx, &srcs, labels) } // bp2BuildParseLibProps returns the attributes for a variant of a cc_library. diff --git a/cc/proto.go b/cc/proto.go index 3cf1453c8..8e6d5ed5f 100644 --- a/cc/proto.go +++ b/cc/proto.go @@ -177,7 +177,7 @@ type bp2buildProtoDeps struct { func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) bp2buildProtoDeps { var ret bp2buildProtoDeps - protoInfo, ok := android.Bp2buildProtoProperties(ctx, m, protoSrcs) + protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs) if !ok { return ret } diff --git a/java/java.go b/java/java.go index e55f04599..d0f0abc7a 100644 --- a/java/java.go +++ b/java/java.go @@ -2013,8 +2013,16 @@ type javaLibraryAttributes struct { 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)) + + javaSrcPartition := "java" + protoSrcPartition := "proto" + srcPartitions := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{ + javaSrcPartition: bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true}, + protoSrcPartition: android.ProtoSrcLabelPartition, + }) + attrs := &javaLibraryAttributes{ - Srcs: srcs, + Srcs: srcPartitions[javaSrcPartition], } if m.properties.Javacflags != nil { @@ -2029,6 +2037,12 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) //TODO(b/217236083) handle static libs similarly to Soong deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Static_libs)) } + + protoDeps := bp2buildProto(ctx, &m.Module, srcPartitions[protoSrcPartition]) + if protoDeps != nil { + deps.Add(protoDeps) + } + attrs.Deps = bazel.MakeLabelListAttribute(deps) return attrs diff --git a/java/proto.go b/java/proto.go index 8d2380322..5ba486fd6 100644 --- a/java/proto.go +++ b/java/proto.go @@ -19,6 +19,13 @@ import ( "strconv" "android/soong/android" + "android/soong/bazel" + + "github.com/google/blueprint/proptools" +) + +const ( + protoTypeDefault = "lite" ) func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android.ProtoFlags) android.Paths { @@ -134,3 +141,52 @@ func protoFlags(ctx android.ModuleContext, j *CommonProperties, p *android.Proto return flags } + +type protoAttributes struct { + Deps bazel.LabelListAttribute +} + +func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) *bazel.Label { + protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs) + if !ok { + return nil + } + + typ := proptools.StringDefault(protoInfo.Type, protoTypeDefault) + var rule_class string + suffix := "_java_proto" + switch typ { + case "nano": + suffix += "_nano" + rule_class = "java_nano_proto_library" + case "micro": + suffix += "_micro" + rule_class = "java_micro_proto_library" + case "lite": + suffix += "_lite" + rule_class = "java_lite_proto_library" + case "stream": + suffix += "_stream" + rule_class = "java_stream_proto_library" + case "full": + rule_class = "java_proto_library" + default: + ctx.PropertyErrorf("proto.type", "cannot handle conversion at this time: %q", typ) + } + + protoLabel := bazel.Label{Label: ":" + m.Name() + "_proto"} + var protoAttrs protoAttributes + protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}}) + + name := m.Name() + suffix + + ctx.CreateBazelTargetModule( + bazel.BazelTargetModuleProperties{ + Rule_class: rule_class, + Bzl_load_location: "//build/bazel/rules/java:proto.bzl", + }, + android.CommonAttributes{Name: name}, + &protoAttrs) + + return &bazel.Label{Label: ":" + name} +}