Kaynağa Gözat

Create (API) bp2build converters for droidstubs

- The converter runs for api_bp2build, not bp2build workspace
- Since droidstubs is an internal module created by java_sdk_library,
  the conversion encompasses the latter as well
- Since droidstubs do not have an api_surface attribute, this conversion
  uses naming convention to infer the api_surface represented by the api
  file e.g. *stubs.source -> publicapi, *stubs.source.system -> systemapi)
- Also adds an SdkIntraCore enum to represent the API surface provided
  by one core module to another

There is also ongoing work to check in java_api_contribution modules in
Soong. Once we have that, we can update this converter to operate on
that module type instead

Test: go test ./bp2build
Change-Id: Ia85828e04c738d9ffcc524856d7c3034ee29bbf9
Spandan Das 1 yıl önce
ebeveyn
işleme
0b555e323b

+ 11 - 3
android/api_domain.go

@@ -59,8 +59,14 @@ type apiDomain struct {
 
 type apiDomainProperties struct {
 	// cc library contributions (.h files/.map.txt) of this API domain
-	// This dependency is a no-op in Soong, but the corresponding Bazel target in the bp2build workspace will provide a `CcApiContributionInfo` provider
+	// This dependency is a no-op in Soong, but the corresponding Bazel target in the api_bp2build workspace
+	// will provide a `CcApiContributionInfo` provider
 	Cc_api_contributions []string
+
+	// java library contributions (as .txt) of this API domain
+	// This dependency is a no-op in Soong, but the corresponding Bazel target in the api_bp2build workspace
+	// will provide a `JavaApiContributionInfo` provider
+	Java_api_contributions []string
 }
 
 func ApiDomainFactory() Module {
@@ -102,7 +108,8 @@ func contributionBazelAttributes(ctx TopDownMutatorContext, contributions []stri
 }
 
 type bazelApiDomainAttributes struct {
-	Cc_api_contributions bazel.LabelListAttribute
+	Cc_api_contributions   bazel.LabelListAttribute
+	Java_api_contributions bazel.LabelListAttribute
 }
 
 var _ ApiProvider = (*apiDomain)(nil)
@@ -113,7 +120,8 @@ func (a *apiDomain) ConvertWithApiBp2build(ctx TopDownMutatorContext) {
 		Bzl_load_location: "//build/bazel/rules/apis:api_domain.bzl",
 	}
 	attrs := &bazelApiDomainAttributes{
-		Cc_api_contributions: contributionBazelAttributes(ctx, a.properties.Cc_api_contributions),
+		Cc_api_contributions:   contributionBazelAttributes(ctx, a.properties.Cc_api_contributions),
+		Java_api_contributions: contributionBazelAttributes(ctx, a.properties.Java_api_contributions),
 	}
 	ctx.CreateBazelTargetModule(props, CommonAttributes{
 		Name: ctx.ModuleName(),

+ 3 - 0
android/sdk_version.go

@@ -44,6 +44,7 @@ const (
 	SdkNone
 	SdkCore
 	SdkCorePlatform
+	SdkIntraCore // API surface provided by one core module to another
 	SdkPublic
 	SdkSystem
 	SdkTest
@@ -69,6 +70,8 @@ func (k SdkKind) String() string {
 		return "core"
 	case SdkCorePlatform:
 		return "core_platform"
+	case SdkIntraCore:
+		return "intracore"
 	case SdkModule:
 		return "module-lib"
 	case SdkSystemServer:

+ 1 - 0
bp2build/Android.bp

@@ -54,6 +54,7 @@ bootstrap_go_package {
         "cc_test_conversion_test.go",
         "cc_yasm_conversion_test.go",
         "conversion_test.go",
+        "droidstubs_conversion_test.go",
         "filegroup_conversion_test.go",
         "genrule_conversion_test.go",
         "gensrcs_conversion_test.go",

+ 104 - 0
bp2build/droidstubs_conversion_test.go

@@ -0,0 +1,104 @@
+// 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/android"
+	"android/soong/java"
+)
+
+func registerJavaApiModules(ctx android.RegistrationContext) {
+	java.RegisterSdkLibraryBuildComponents(ctx)
+	java.RegisterStubsBuildComponents(ctx)
+}
+
+func TestDroidstubsApiContributions(t *testing.T) {
+	bp := `
+	droidstubs {
+		name: "framework-stubs",
+		check_api: {
+			current: {
+				api_file: "framework.current.txt",
+			},
+		},
+	}
+
+	// Modules without check_api should not generate a Bazel API target
+	droidstubs {
+		name: "framework-docs",
+	}
+
+	// java_sdk_library is a macro that creates droidstubs
+	java_sdk_library {
+		name: "module-stubs",
+		srcs: ["A.java"],
+
+		// These api surfaces are added by default, but add them explicitly to make
+		// this test hermetic
+		public: {
+			enabled: true,
+		},
+		system: {
+			enabled: true,
+		},
+
+		// Disable other api surfaces to keep unit test scope limited
+		module_lib: {
+			enabled: false,
+		},
+		test: {
+			enabled: false,
+		},
+	}
+	`
+	expectedBazelTargets := []string{
+		MakeBazelTargetNoRestrictions(
+			"java_api_contribution",
+			"framework-stubs.contribution",
+			AttrNameToString{
+				"api":                    `"framework.current.txt"`,
+				"api_surface":            `"publicapi"`,
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+			}),
+		MakeBazelTargetNoRestrictions(
+			"java_api_contribution",
+			"module-stubs.stubs.source.contribution",
+			AttrNameToString{
+				"api":                    `"api/current.txt"`,
+				"api_surface":            `"publicapi"`,
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+			}),
+		MakeBazelTargetNoRestrictions(
+			"java_api_contribution",
+			"module-stubs.stubs.source.system.contribution",
+			AttrNameToString{
+				"api":                    `"api/system-current.txt"`,
+				"api_surface":            `"systemapi"`,
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+			}),
+	}
+	RunApiBp2BuildTestCase(t, registerJavaApiModules, Bp2buildTestCase{
+		Blueprint:            bp,
+		ExpectedBazelTargets: expectedBazelTargets,
+		Filesystem: map[string]string{
+			"api/current.txt":        "",
+			"api/removed.txt":        "",
+			"api/system-current.txt": "",
+			"api/system-removed.txt": "",
+		},
+	})
+}

+ 70 - 0
java/droidstubs.go

@@ -18,11 +18,13 @@ import (
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/java/config"
 	"android/soong/remoteexec"
 )
@@ -834,6 +836,74 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	}
 }
 
+var _ android.ApiProvider = (*Droidstubs)(nil)
+
+type bazelJavaApiContributionAttributes struct {
+	Api         bazel.LabelAttribute
+	Api_surface *string
+}
+
+func (d *Droidstubs) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "java_api_contribution",
+		Bzl_load_location: "//build/bazel/rules/apis:java_api_contribution.bzl",
+	}
+	apiFile := d.properties.Check_api.Current.Api_file
+	// Do not generate a target if check_api is not set
+	if apiFile == nil {
+		return
+	}
+	attrs := &bazelJavaApiContributionAttributes{
+		Api: *bazel.MakeLabelAttribute(
+			android.BazelLabelForModuleSrcSingle(ctx, proptools.String(apiFile)).Label,
+		),
+		Api_surface: proptools.StringPtr(bazelApiSurfaceName(d.Name())),
+	}
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: android.ApiContributionTargetName(ctx.ModuleName()),
+	}, attrs)
+}
+
+// TODO (b/262014796): Export the API contributions of CorePlatformApi
+// A map to populate the api surface of a droidstub from a substring appearing in its name
+// This map assumes that droidstubs (either checked-in or created by java_sdk_library)
+// use a strict naming convention
+var (
+	droidstubsModuleNamingToSdkKind = map[string]android.SdkKind{
+		//public is commented out since the core libraries use public in their java_sdk_library names
+		"intracore":     android.SdkIntraCore,
+		"intra.core":    android.SdkIntraCore,
+		"system_server": android.SdkSystemServer,
+		"system-server": android.SdkSystemServer,
+		"system":        android.SdkSystem,
+		"module_lib":    android.SdkModule,
+		"module-lib":    android.SdkModule,
+		"test":          android.SdkTest,
+	}
+)
+
+// A helper function that returns the api surface of the corresponding java_api_contribution Bazel target
+// The api_surface is populated using the naming convention of the droidstubs module.
+func bazelApiSurfaceName(name string) string {
+	// Sort the keys so that longer strings appear first
+	// Otherwise substrings like system will match both system and system_server
+	sortedKeys := make([]string, 0)
+	for key := range droidstubsModuleNamingToSdkKind {
+		sortedKeys = append(sortedKeys, key)
+	}
+	sort.Slice(sortedKeys, func(i, j int) bool {
+		return len(sortedKeys[i]) > len(sortedKeys[j])
+	})
+	for _, sortedKey := range sortedKeys {
+		if strings.Contains(name, sortedKey) {
+			sdkKind := droidstubsModuleNamingToSdkKind[sortedKey]
+			return sdkKind.String() + "api"
+		}
+	}
+	// Default is publicapi
+	return android.SdkPublic.String() + "api"
+}
+
 func StubsDefaultsFactory() android.Module {
 	module := &DocDefaults{}
 

+ 42 - 0
java/droidstubs_test.go

@@ -304,3 +304,45 @@ func TestDroidstubsWithSdkExtensions(t *testing.T) {
 	android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions")
 	android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt")
 }
+
+func TestApiSurfaceFromDroidStubsName(t *testing.T) {
+	testCases := []struct {
+		desc               string
+		name               string
+		expectedApiSurface string
+	}{
+		{
+			desc:               "Default is publicapi",
+			name:               "mydroidstubs",
+			expectedApiSurface: "publicapi",
+		},
+		{
+			desc:               "name contains system substring",
+			name:               "mydroidstubs.system.suffix",
+			expectedApiSurface: "systemapi",
+		},
+		{
+			desc:               "name contains system_server substring",
+			name:               "mydroidstubs.system_server.suffix",
+			expectedApiSurface: "system-serverapi",
+		},
+		{
+			desc:               "name contains module_lib substring",
+			name:               "mydroidstubs.module_lib.suffix",
+			expectedApiSurface: "module-libapi",
+		},
+		{
+			desc:               "name contains test substring",
+			name:               "mydroidstubs.test.suffix",
+			expectedApiSurface: "testapi",
+		},
+		{
+			desc:               "name contains intra.core substring",
+			name:               "mydroidstubs.intra.core.suffix",
+			expectedApiSurface: "intracoreapi",
+		},
+	}
+	for _, tc := range testCases {
+		android.AssertStringEquals(t, tc.desc, tc.expectedApiSurface, bazelApiSurfaceName(tc.name))
+	}
+}