Merge "Handle proto.include_dirs in bp2build for CC" into main
diff --git a/android/proto.go b/android/proto.go
index b21efd6..0ffb9b6 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -156,8 +156,9 @@
 
 // Bp2buildProtoInfo contains information necessary to pass on to language specific conversion.
 type Bp2buildProtoInfo struct {
-	Type       *string
-	Proto_libs bazel.LabelList
+	Type                  *string
+	Proto_libs            bazel.LabelList
+	Transitive_proto_libs bazel.LabelList
 }
 
 type ProtoAttrs struct {
@@ -211,6 +212,7 @@
 	}
 
 	var protoLibraries bazel.LabelList
+	var transitiveProtoLibraries bazel.LabelList
 	var directProtoSrcs bazel.LabelList
 
 	// For filegroups that should be converted to proto_library just collect the
@@ -234,6 +236,7 @@
 
 	if len(directProtoSrcs.Includes) > 0 {
 		pkgToSrcs := partitionSrcsByPackage(ctx.ModuleDir(), directProtoSrcs)
+		protoIncludeDirs := []string{}
 		for _, pkg := range SortedStringKeys(pkgToSrcs) {
 			srcs := pkgToSrcs[pkg]
 			attrs := ProtoAttrs{
@@ -262,7 +265,7 @@
 							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)
+								protoIncludeDirs = append(protoIncludeDirs, dir)
 							}
 						}
 					} else if props.Proto.Type != info.Type && props.Proto.Type != nil {
@@ -308,9 +311,84 @@
 				Label: l,
 			})
 		}
+		protoLibrariesInIncludeDir := createProtoLibraryTargetsForIncludeDirs(ctx, protoIncludeDirs)
+		transitiveProtoLibraries.Append(protoLibrariesInIncludeDir)
 	}
 
 	info.Proto_libs = protoLibraries
+	info.Transitive_proto_libs = transitiveProtoLibraries
 
 	return info, true
 }
+
+var (
+	protoIncludeDirGeneratedSuffix = ".include_dir_bp2build_generated_proto"
+	protoIncludeDirsBp2buildKey    = NewOnceKey("protoIncludeDirsBp2build")
+)
+
+func getProtoIncludeDirsBp2build(config Config) *map[protoIncludeDirKey]bool {
+	return config.Once(protoIncludeDirsBp2buildKey, func() interface{} {
+		return &map[protoIncludeDirKey]bool{}
+	}).(*map[protoIncludeDirKey]bool)
+}
+
+// key for dynamically creating proto_library per proto.include_dirs
+type protoIncludeDirKey struct {
+	dir            string
+	subpackgeInDir string
+}
+
+// createProtoLibraryTargetsForIncludeDirs creates additional proto_library targets for .proto files in includeDirs
+// Since Bazel imposes a constratint that the proto_library must be in the same package as the .proto file, this function
+// might create the targets in a subdirectory of `includeDir`
+// Returns the labels of the proto_library targets
+func createProtoLibraryTargetsForIncludeDirs(ctx Bp2buildMutatorContext, includeDirs []string) bazel.LabelList {
+	var ret bazel.LabelList
+	for _, dir := range includeDirs {
+		if exists, _, _ := ctx.Config().fs.Exists(filepath.Join(dir, "Android.bp")); !exists {
+			ctx.ModuleErrorf("TODO: Add support for proto.include_dir: %v. This directory does not contain an Android.bp file", dir)
+		}
+		dirMap := getProtoIncludeDirsBp2build(ctx.Config())
+		// Find all proto file targets in this dir
+		protoLabelsInDir := BazelLabelForSrcPatternExcludes(ctx, dir, "**/*.proto", []string{})
+		// Partition the labels by package and subpackage(s)
+		protoLabelelsPartitionedByPkg := partitionSrcsByPackage(dir, protoLabelsInDir)
+		for _, pkg := range SortedStringKeys(protoLabelelsPartitionedByPkg) {
+			label := strings.ReplaceAll(dir, "/", ".") + protoIncludeDirGeneratedSuffix
+			ret.Add(&bazel.Label{
+				Label: "//" + pkg + ":" + label,
+			})
+			key := protoIncludeDirKey{dir: dir, subpackgeInDir: pkg}
+			if _, exists := (*dirMap)[key]; exists {
+				// A proto_library has already been created for this package relative to this include dir
+				continue
+			}
+			(*dirMap)[key] = true
+			srcs := protoLabelelsPartitionedByPkg[pkg]
+			rel, err := filepath.Rel(dir, pkg)
+			if err != nil {
+				ctx.ModuleErrorf("Could not create a proto_library in pkg %v due to %v\n", pkg, err)
+			}
+			// Create proto_library
+			attrs := ProtoAttrs{
+				Srcs:                bazel.MakeLabelListAttribute(srcs),
+				Strip_import_prefix: proptools.StringPtr(""),
+			}
+			if rel != "." {
+				attrs.Import_prefix = proptools.StringPtr(rel)
+			}
+			ctx.CreateBazelTargetModule(
+				bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
+				CommonAttributes{
+					Name: label,
+					Dir:  proptools.StringPtr(pkg),
+					// This proto_library is used to construct a ProtoInfo
+					// But it might not be buildable on its own
+					Tags: bazel.MakeStringListAttribute([]string{"manual"}),
+				},
+				&attrs,
+			)
+		}
+	}
+	return ret
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index e70cd10..28dbf7e 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -2394,7 +2394,7 @@
 	},
 	include_build_directory: false,
 }`,
-		ExpectedErr: fmt.Errorf("module \"foo\": Could not find the proto_library target for include dir: external/protobuf/abc"),
+		ExpectedErr: fmt.Errorf("module \"foo\": TODO: Add support for proto.include_dir: external/protobuf/abc. This directory does not contain an Android.bp file"),
 	})
 }
 
@@ -5068,3 +5068,72 @@
 	}
 	runCcLibraryTestCase(t, tc)
 }
+
+func TestProtoIncludeDirs(t *testing.T) {
+	tc := Bp2buildTestCase{
+		Description:                "cc_library depends on .proto files using proto.include_dirs",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	srcs: [
+	   "foo.proto",
+	],
+	proto: {
+		include_dirs: ["bar"],
+	}
+}
+` + simpleModuleDoNotConvertBp2build("cc_library", "libprotobuf-cpp-lite"),
+		Filesystem: map[string]string{
+			"bar/Android.bp":     "",
+			"bar/bar.proto":      "",
+			"bar/baz/Android.bp": "",
+			"bar/baz/baz.proto":  "",
+		},
+	}
+
+	// We will run the test 3 times and check in the root, bar and bar/baz 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"]`,
+			"transitive_deps": `[
+        "//bar:bar.include_dir_bp2build_generated_proto",
+        "//bar/baz:bar.include_dir_bp2build_generated_proto",
+    ]`,
+		}),
+	}
+	runCcLibraryTestCase(t, tc)
+
+	// bar dir
+	tc.Dir = "bar"
+	tc.ExpectedBazelTargets = []string{
+		MakeBazelTarget("proto_library", "bar.include_dir_bp2build_generated_proto", AttrNameToString{
+			"srcs":                `["bar.proto"]`,
+			"strip_import_prefix": `""`,
+			"tags":                `["manual"]`,
+		}),
+	}
+	runCcLibraryTestCase(t, tc)
+
+	// bar/baz dir
+	tc.Dir = "bar/baz"
+	tc.ExpectedBazelTargets = []string{
+		MakeBazelTarget("proto_library", "bar.include_dir_bp2build_generated_proto", AttrNameToString{
+			"srcs":                `["//bar/baz:baz.proto"]`,
+			"strip_import_prefix": `""`,
+			"import_prefix":       `"baz"`,
+			"tags":                `["manual"]`,
+		}),
+	}
+	runCcLibraryTestCase(t, tc)
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 0157632..7f78e28 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -960,7 +960,7 @@
 	(&linkerAttrs).wholeArchiveDeps.Append(compilerAttrs.exportXsdSrcs)
 	(&linkerAttrs).implementationWholeArchiveDeps.Append(compilerAttrs.xsdSrcs)
 
-	protoDep := bp2buildProto(ctx, module, compilerAttrs.protoSrcs)
+	protoDep := bp2buildProto(ctx, module, compilerAttrs.protoSrcs, linkerAttrs)
 
 	// 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
diff --git a/cc/proto.go b/cc/proto.go
index 5d9aef6..0ed4381 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -165,7 +165,17 @@
 }
 
 type protoAttributes struct {
-	Deps            bazel.LabelListAttribute
+	Deps bazel.LabelListAttribute
+
+	// A list of proto_library targets that that the proto_library in `deps` depends on
+	// This list is overestimation.
+	// Overestimation is necessary since Soong includes other protos via proto.include_dirs and not
+	// a specific .proto file module explicitly.
+	Transitive_deps bazel.LabelListAttribute
+
+	// A list of cc_library_* targets that the generated cpp code depends on
+	Cc_deps bazel.LabelListAttribute
+
 	Min_sdk_version *string
 }
 
@@ -175,7 +185,7 @@
 	protoDep                     *bazel.LabelAttribute
 }
 
-func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) bp2buildProtoDeps {
+func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute, la linkerAttributes) bp2buildProtoDeps {
 	var ret bp2buildProtoDeps
 
 	protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs)
@@ -204,6 +214,35 @@
 
 	var protoAttrs protoAttributes
 	protoAttrs.Deps.SetValue(protoInfo.Proto_libs)
+	protoAttrs.Transitive_deps.SetValue(protoInfo.Transitive_proto_libs)
+
+	// Add the implementation deps of the top-level cc_library_static
+	// This is necessary to compile the internal root of cc_proto_library.
+	// Without this, clang might not be able to find .h files that the generated cpp files depends on
+	protoAttrs.Cc_deps = *la.implementationDeps.Clone()
+	protoAttrs.Cc_deps.Append(la.implementationDynamicDeps)
+	protoAttrs.Cc_deps.Append(la.implementationWholeArchiveDeps)
+	protoAttrs.Cc_deps.Append(la.wholeArchiveDeps)
+	// Subtract myself to prevent possible circular dep
+	protoAttrs.Cc_deps = bazel.SubtractBazelLabelListAttribute(
+		protoAttrs.Cc_deps,
+		bazel.MakeLabelListAttribute(
+			bazel.MakeLabelList([]bazel.Label{
+				bazel.Label{Label: ":" + m.Name() + suffix},
+			}),
+		),
+	)
+	// Subtract the protobuf libraries since cc_proto_library implicitly adds them
+	protoAttrs.Cc_deps = bazel.SubtractBazelLabelListAttribute(
+		protoAttrs.Cc_deps,
+		bazel.MakeLabelListAttribute(
+			bazel.MakeLabelList([]bazel.Label{
+				bazel.Label{Label: "//external/protobuf:libprotobuf-cpp-full", OriginalModuleName: "libprotobuf-cpp-full"},
+				bazel.Label{Label: "//external/protobuf:libprotobuf-cpp-lite", OriginalModuleName: "libprotobuf-cpp-lite"},
+			}),
+		),
+	)
+
 	protoAttrs.Min_sdk_version = m.Properties.Min_sdk_version
 
 	name := m.Name() + suffix