Multi-tree API bp2build converter for ndk_library

This is a partial bp2build conversion that only emits the
cc_api_contribution target of ndk_library. We also need to convert this
to cc_stub_suite eventually to enable building vendor/product API
domains with Bazel

Also create an enum for the three known Multi-tree API surfaces (will
likely be expanded in the future)

Test: go test ./bp2build
Test: b cquery //bionic/libc:libc.ndk.contribution --output=starlark
--starlark:expr="providers(target)"

Change-Id: Idb24871ba20aae132b61eb31ef35c917cacae9e1
diff --git a/android/api_domain.go b/android/api_domain.go
index a808e32..8ff4752 100644
--- a/android/api_domain.go
+++ b/android/api_domain.go
@@ -28,6 +28,28 @@
 	ctx.RegisterModuleType("api_domain", ApiDomainFactory)
 }
 
+type ApiSurface int
+
+// TODO(b/246656800): Reconcile with android.SdkKind
+const (
+	PublicApi ApiSurface = iota
+	SystemApi
+	VendorApi
+)
+
+func (a ApiSurface) String() string {
+	switch a {
+	case PublicApi:
+		return "publicapi"
+	case SystemApi:
+		return "systemapi"
+	case VendorApi:
+		return "vendorapi"
+	default:
+		return "invalid"
+	}
+}
+
 type apiDomain struct {
 	ModuleBase
 	BazelModuleBase
diff --git a/bp2build/ndk_library_conversion_test.go b/bp2build/ndk_library_conversion_test.go
new file mode 100644
index 0000000..244ce20
--- /dev/null
+++ b/bp2build/ndk_library_conversion_test.go
@@ -0,0 +1,77 @@
+// Copyright 2022 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 (
+	"testing"
+
+	"android/soong/cc"
+)
+
+func TestNdkLibraryContributionSymbolFile(t *testing.T) {
+	bp := `
+	ndk_library {
+		name: "libfoo",
+		symbol_file: "libfoo.map.txt",
+	}
+	`
+	expectedBazelTarget := MakeBazelTargetNoRestrictions(
+		"cc_api_contribution",
+		"libfoo.ndk.contribution",
+		AttrNameToString{
+			"api":                    `"libfoo.map.txt"`,
+			"api_surfaces":           `["publicapi"]`,
+			"library_name":           `"libfoo"`,
+			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+		},
+	)
+	RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+		Blueprint:            bp,
+		ExpectedBazelTargets: []string{expectedBazelTarget},
+	})
+}
+
+func TestNdkLibraryContributionHeaders(t *testing.T) {
+	bp := `
+	ndk_library {
+		name: "libfoo",
+		symbol_file: "libfoo.map.txt",
+		export_header_libs: ["libfoo_headers"],
+	}
+	`
+	fs := map[string]string{
+		"header_directory/Android.bp": `
+		ndk_headers {
+			name: "libfoo_headers",
+		}
+		`,
+	}
+	expectedBazelTarget := MakeBazelTargetNoRestrictions(
+		"cc_api_contribution",
+		"libfoo.ndk.contribution",
+		AttrNameToString{
+			"api":                    `"libfoo.map.txt"`,
+			"api_surfaces":           `["publicapi"]`,
+			"library_name":           `"libfoo"`,
+			"hdrs":                   `["//header_directory:libfoo_headers.contribution"]`,
+			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+		},
+	)
+	RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+		Blueprint:            bp,
+		Filesystem:           fs,
+		ExpectedBazelTargets: []string{expectedBazelTarget},
+	})
+}
diff --git a/cc/cc.go b/cc/cc.go
index d42ab6d..1c845f6 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -3650,6 +3650,7 @@
 	sharedLibrary
 	headerLibrary
 	testBin // testBinary already declared
+	ndkLibrary
 )
 
 func (c *Module) typ() moduleType {
@@ -3686,6 +3687,8 @@
 			return staticLibrary
 		}
 		return sharedLibrary
+	} else if c.isNDKStubLibrary() {
+		return ndkLibrary
 	}
 	return unknownType
 }
@@ -3726,6 +3729,8 @@
 		} else {
 			sharedOrStaticLibraryBp2Build(ctx, c, false)
 		}
+	case ndkLibrary:
+		ndkLibraryBp2build(ctx, c)
 	}
 }
 
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index e2b9682..06ded3f 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -25,6 +25,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/cc/config"
 )
 
@@ -568,5 +569,43 @@
 func NdkLibraryFactory() android.Module {
 	module := newStubLibrary()
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
+	android.InitBazelModule(module)
 	return module
 }
+
+type bazelCcApiContributionAttributes struct {
+	Api          bazel.LabelAttribute
+	Api_surfaces bazel.StringListAttribute
+	Hdrs         bazel.LabelListAttribute
+	Library_name string
+}
+
+// Names of the cc_api_header targets in the bp2build workspace
+func (s *stubDecorator) apiHeaderLabels(ctx android.TopDownMutatorContext) bazel.LabelList {
+	addSuffix := func(ctx android.BazelConversionPathContext, module blueprint.Module) string {
+		label := android.BazelModuleLabel(ctx, module)
+		return android.ApiContributionTargetName(label)
+	}
+	return android.BazelLabelForModuleDepsWithFn(ctx, s.properties.Export_header_libs, addSuffix)
+}
+
+func ndkLibraryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_api_contribution",
+		Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+	}
+	stubLibrary := m.compiler.(*stubDecorator)
+	attrs := &bazelCcApiContributionAttributes{
+		Library_name: stubLibrary.implementationModuleName(m.Name()),
+		Api_surfaces: bazel.MakeStringListAttribute(
+			[]string{android.PublicApi.String()}),
+	}
+	if symbolFile := stubLibrary.properties.Symbol_file; symbolFile != nil {
+		apiLabel := android.BazelLabelForModuleSrcSingle(ctx, proptools.String(symbolFile)).Label
+		attrs.Api = *bazel.MakeLabelAttribute(apiLabel)
+	}
+	apiHeaders := stubLibrary.apiHeaderLabels(ctx)
+	attrs.Hdrs = bazel.MakeLabelListAttribute(apiHeaders)
+	apiContributionTargetName := android.ApiContributionTargetName(ctx.ModuleName())
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: apiContributionTargetName}, attrs)
+}