Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c53767e4 authored by Spandan Das's avatar Spandan Das
Browse files

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
parent 3131d679
Loading
Loading
Loading
Loading
+87 −38
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
package android

import (
	"path/filepath"
	"strings"

	"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.
type Bp2buildProtoInfo struct {
	Type       *string
	Name       string
	Proto_libs bazel.LabelList
}

type ProtoAttrs struct {
	Srcs                bazel.LabelListAttribute
	Import_prefix       *string
	Strip_import_prefix *string
	Deps                bazel.LabelListAttribute
}
@@ -172,6 +173,35 @@ var includeDirsToProtoDeps = map[string]string{
	"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
// information necessary for language-specific handling.
func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
@@ -197,13 +227,18 @@ func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs baz
		}
	}

	info.Name = m.Name() + "_proto"
	name := m.Name() + "_proto"

	depsFromFilegroup := protoLibraries

	if len(directProtoSrcs.Includes) > 0 {
		pkgToSrcs := partitionSrcsByPackage(ctx.ModuleDir(), directProtoSrcs)
		for _, pkg := range SortedStringKeys(pkgToSrcs) {
			srcs := pkgToSrcs[pkg]
			attrs := ProtoAttrs{
			Srcs: bazel.MakeLabelListAttribute(directProtoSrcs),
				Srcs: bazel.MakeLabelListAttribute(srcs),
			}
		attrs.Deps.Append(bazel.MakeLabelListAttribute(protoLibraries))
			attrs.Deps.Append(bazel.MakeLabelListAttribute(depsFromFilegroup))

			for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) {
				for _, rawProps := range configToProps {
@@ -236,16 +271,30 @@ func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs baz

			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: info.Name, Tags: tags},
				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: ":" + info.Name,
				Label: l,
			})
		}
	}

	info.Proto_libs = protoLibraries

+64 −0
Original line number Diff line number Diff line
@@ -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)
}
+1 −2
Original line number Diff line number Diff line
@@ -73,7 +73,6 @@ func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownM

	if !partitionedSrcs["proto"].IsEmpty() {
		protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
		protoLabel := bazel.Label{Label: ":" + protoInfo.Name}

		pyProtoLibraryName := m.Name() + "_py_proto"
		ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
@@ -82,7 +81,7 @@ func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownM
		}, android.CommonAttributes{
			Name: pyProtoLibraryName,
		}, &bazelPythonProtoLibraryAttributes{
			Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
			Deps: bazel.MakeLabelListAttribute(protoInfo.Proto_libs),
		})

		attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))