diff --git a/android/sdk.go b/android/sdk.go index d66816dc8..73cb2565f 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -164,6 +164,9 @@ type SnapshotBuilder interface { // to the zip CopyToSnapshot(src Path, dest string) + // Unzip the supplied zip into the snapshot relative directory destDir. + UnzipToSnapshot(zipPath Path, destDir string) + // Get the AndroidBpFile for the snapshot. AndroidBpFile() GeneratedSnapshotFile diff --git a/java/droiddoc.go b/java/droiddoc.go index 54f93fec2..83a1ad588 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -37,6 +37,8 @@ func init() { android.RegisterModuleType("droidstubs", DroidstubsFactory) android.RegisterModuleType("droidstubs_host", DroidstubsHostFactory) + + android.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory) } var ( @@ -1163,6 +1165,7 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { // type Droidstubs struct { Javadoc + android.SdkBase properties DroidstubsProperties apiFile android.WritablePath @@ -1208,6 +1211,7 @@ func DroidstubsFactory() android.Module { &module.Javadoc.properties) InitDroiddocModule(module, android.HostAndDeviceSupported) + android.InitSdkAwareModule(module) return module } @@ -1913,3 +1917,88 @@ func zipSyncCmd(ctx android.ModuleContext, rule *android.RuleBuilder, func zipSyncCleanupCmd(rule *android.RuleBuilder, srcJarDir android.ModuleOutPath) { rule.Command().Text("rm -rf").Text(srcJarDir.String()) } + +var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil) + +type PrebuiltStubsSourcesProperties struct { + Srcs []string `android:"path"` +} + +type PrebuiltStubsSources struct { + android.ModuleBase + android.DefaultableModuleBase + prebuilt android.Prebuilt + android.SdkBase + + properties PrebuiltStubsSourcesProperties + + srcs android.Paths + stubsSrcJar android.ModuleOutPath +} + +func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) { + p.srcs = android.PathsForModuleSrc(ctx, p.properties.Srcs) +} + +func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt { + return &p.prebuilt +} + +func (p *PrebuiltStubsSources) Name() string { + return p.prebuilt.Name(p.ModuleBase.Name()) +} + +func (p *PrebuiltStubsSources) Srcs() android.Paths { + return append(android.Paths{}, p.srcs...) +} + +// prebuilt_stubs_sources imports a set of java source files as if they were +// generated by droidstubs. +// +// By default, a prebuilt_stubs_sources has a single variant that expects a +// set of `.java` files generated by droidstubs. +// +// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one +// for host modules. +// +// Intended only for use by sdk snapshots. +func PrebuiltStubsSourcesFactory() android.Module { + module := &PrebuiltStubsSources{} + + module.AddProperties(&module.properties) + + android.InitPrebuiltModule(module, &module.properties.Srcs) + android.InitSdkAwareModule(module) + InitDroiddocModule(module, android.HostAndDeviceSupported) + return module +} + +func (d *Droidstubs) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder) { + stubsSrcJar := d.stubsSrcJar + + snapshotRelativeDir := filepath.Join("java", d.Name()+"_stubs_sources") + builder.UnzipToSnapshot(stubsSrcJar, snapshotRelativeDir) + + name := d.Name() + bp := builder.AndroidBpFile() + bp.Printfln("prebuilt_stubs_sources {") + bp.Indent() + bp.Printfln("name: %q,", builder.VersionedSdkMemberName(name)) + bp.Printfln("sdk_member_name: %q,", name) + bp.Printfln("srcs: [%q],", snapshotRelativeDir) + bp.Dedent() + bp.Printfln("}") + bp.Printfln("") + + // This module is for the case when the source tree for the unversioned module + // doesn't exist (i.e. building in an unbundled tree). "prefer:" is set to false + // so that this module does not eclipse the unversioned module if it exists. + bp.Printfln("prebuilt_stubs_sources {") + bp.Indent() + bp.Printfln("name: %q,", name) + bp.Printfln("srcs: [%q],", snapshotRelativeDir) + bp.Printfln("prefer: false,") + bp.Dedent() + bp.Printfln("}") + bp.Printfln("") +} diff --git a/java/java_test.go b/java/java_test.go index 0f7e6dee8..71aba3ab1 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -89,6 +89,7 @@ func testContext(bp string, fs map[string][]byte) *android.TestContext { ctx.RegisterModuleType("droiddoc", android.ModuleFactoryAdaptor(DroiddocFactory)) ctx.RegisterModuleType("droiddoc_host", android.ModuleFactoryAdaptor(DroiddocHostFactory)) ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(ExportedDroiddocDirFactory)) + ctx.RegisterModuleType("prebuilt_stubs_sources", android.ModuleFactoryAdaptor(PrebuiltStubsSourcesFactory)) ctx.RegisterModuleType("java_sdk_library", android.ModuleFactoryAdaptor(SdkLibraryFactory)) ctx.RegisterModuleType("java_sdk_library_import", android.ModuleFactoryAdaptor(sdkLibraryImportFactory)) ctx.RegisterModuleType("override_android_app", android.ModuleFactoryAdaptor(OverrideAndroidAppModuleFactory)) @@ -207,6 +208,9 @@ func testContext(bp string, fs map[string][]byte) *android.TestContext { "cert/new_cert.pk8": nil, "testdata/data": nil, + + "stubs-sources/foo/Foo.java": nil, + "stubs/sources/foo/Foo.java": nil, } for k, v := range fs { @@ -415,7 +419,7 @@ func TestPrebuilts(t *testing.T) { ctx, _ := testJava(t, ` java_library { name: "foo", - srcs: ["a.java"], + srcs: ["a.java", ":stubs-source"], libs: ["bar", "sdklib"], static_libs: ["baz"], } @@ -439,6 +443,11 @@ func TestPrebuilts(t *testing.T) { name: "sdklib", jars: ["b.jar"], } + + prebuilt_stubs_sources { + name: "stubs-source", + srcs: ["stubs/sources/**/*.java"], + } `) javac := ctx.ModuleForTests("foo", "android_common").Rule("javac") @@ -447,6 +456,19 @@ func TestPrebuilts(t *testing.T) { bazJar := ctx.ModuleForTests("baz", "android_common").Rule("combineJar").Output sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output + inputs := []string{} + for _, p := range javac.BuildParams.Inputs { + inputs = append(inputs, p.String()) + } + + expected := []string{ + "a.java", + "stubs/sources/foo/Foo.java", + } + if !reflect.DeepEqual(expected, inputs) { + t.Errorf("foo inputs incorrect: expected %q, found %q", expected, inputs) + } + if !strings.Contains(javac.Args["classpath"], barJar.String()) { t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barJar.String()) } diff --git a/sdk/sdk.go b/sdk/sdk.go index 4eb3665fb..ed2f26c91 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -16,6 +16,7 @@ package sdk import ( "fmt" + "io" "strconv" "github.com/google/blueprint" @@ -50,6 +51,8 @@ type sdkProperties struct { Java_libs []string // The list of native libraries in this SDK Native_shared_libs []string + // The list of stub sources in this SDK + Stubs_sources []string Snapshot bool `blueprint:"mutated"` } @@ -121,6 +124,13 @@ func (s *sdk) AndroidMkEntries() android.AndroidMkEntries { OutputFile: s.snapshotFile, DistFile: s.snapshotFile, Include: "$(BUILD_PHONY_PACKAGE)", + ExtraFooters: []android.AndroidMkExtraFootersFunc{ + func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) { + // Allow the sdk to be built by simply passing its name on the command line. + fmt.Fprintln(w, ".PHONY:", s.Name()) + fmt.Fprintln(w, s.Name()+":", s.snapshotFile.String()) + }, + }, } } @@ -167,6 +177,7 @@ type sdkMemberVesionedDepTag struct { func memberMutator(mctx android.BottomUpMutatorContext) { if m, ok := mctx.Module().(*sdk); ok { mctx.AddVariationDependencies(nil, sdkMemberDepTag, m.properties.Java_libs...) + mctx.AddVariationDependencies(nil, sdkMemberDepTag, m.properties.Stubs_sources...) targets := mctx.MultiTargets() for _, target := range targets { diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 3471bc9da..99192be0a 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -45,6 +45,8 @@ func testSdkContext(t *testing.T, bp string) (*android.TestContext, android.Conf ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(java.AndroidAppCertificateFactory)) ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(java.LibraryFactory)) ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(java.ImportFactory)) + ctx.RegisterModuleType("droidstubs", android.ModuleFactoryAdaptor(java.DroidstubsFactory)) + ctx.RegisterModuleType("prebuilt_stubs_sources", android.ModuleFactoryAdaptor(java.PrebuiltStubsSourcesFactory)) // from cc package ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory)) @@ -104,6 +106,8 @@ func testSdkContext(t *testing.T, bp string) (*android.TestContext, android.Conf "include/Test.h": nil, "aidl/foo/bar/Test.aidl": nil, "libfoo.so": nil, + "stubs-sources/foo/bar/Foo.java": nil, + "foo/bar/Foo.java": nil, }) return ctx, config @@ -323,6 +327,39 @@ func TestBasicSdkWithCc(t *testing.T) { ensureListContains(t, pathsToStrings(cpplibForMyApex2.Rule("ld").Implicits), sdkMemberV2.String()) } +// Note: This test does not verify that a droidstubs can be referenced, either +// directly or indirectly from an APEX as droidstubs can never be a part of an +// apex. +func TestBasicSdkWithDroidstubs(t *testing.T) { + testSdk(t, ` + sdk { + name: "mysdk", + stubs_sources: ["mystub"], + } + sdk_snapshot { + name: "mysdk@10", + stubs_sources: ["mystub_mysdk@10"], + } + prebuilt_stubs_sources { + name: "mystub_mysdk@10", + sdk_member_name: "mystub", + srcs: ["stubs-sources/foo/bar/Foo.java"], + } + droidstubs { + name: "mystub", + srcs: ["foo/bar/Foo.java"], + sdk_version: "none", + system_modules: "none", + } + java_library { + name: "myjavalib", + srcs: [":mystub"], + sdk_version: "none", + system_modules: "none", + } + `) +} + func TestDepNotInRequiredSdks(t *testing.T) { testSdkError(t, `module "myjavalib".*depends on "otherlib".*that isn't part of the required SDKs:.*`, ` sdk { @@ -417,6 +454,7 @@ func TestSnapshot(t *testing.T) { name: "mysdk", java_libs: ["myjavalib"], native_shared_libs: ["mynativelib"], + stubs_sources: ["myjavaapistubs"], } java_library { @@ -444,15 +482,26 @@ func TestSnapshot(t *testing.T) { system_shared_libs: [], stl: "none", } + + droidstubs { + name: "myjavaapistubs", + srcs: ["foo/bar/Foo.java"], + system_modules: "none", + sdk_version: "none", + } `) var copySrcs []string var copyDests []string buildParams := ctx.ModuleForTests("mysdk", "android_common").Module().BuildParamsForTests() + var zipBp android.BuildParams for _, bp := range buildParams { - if bp.Rule.String() == "android/soong/android.Cp" { + ruleString := bp.Rule.String() + if ruleString == "android/soong/android.Cp" { copySrcs = append(copySrcs, bp.Input.String()) copyDests = append(copyDests, bp.Output.Rel()) // rooted at the snapshot root + } else if ruleString == ":m.mysdk_android_common.snapshot" { + zipBp = bp } } @@ -472,6 +521,19 @@ func TestSnapshot(t *testing.T) { ensureListContains(t, copyDests, "arm64/include_gen/mynativelib/aidl/foo/bar/Test.h") ensureListContains(t, copyDests, "java/myjavalib.jar") ensureListContains(t, copyDests, "arm64/lib/mynativelib.so") + + // Ensure that the droidstubs .srcjar as repackaged into a temporary zip file + // and then merged together with the intermediate snapshot zip. + snapshotCreationInputs := zipBp.Implicits.Strings() + ensureListContains(t, snapshotCreationInputs, + filepath.Join(buildDir, ".intermediates/mysdk/android_common/tmp/java/myjavaapistubs_stubs_sources.zip")) + ensureListContains(t, snapshotCreationInputs, + filepath.Join(buildDir, ".intermediates/mysdk/android_common/mysdk-current.unmerged.zip")) + actual := zipBp.Output.String() + expected := filepath.Join(buildDir, ".intermediates/mysdk/android_common/mysdk-current.zip") + if actual != expected { + t.Errorf("Expected snapshot output to be %q but was %q", expected, actual) + } } var buildDir string diff --git a/sdk/update.go b/sdk/update.go index 9fa9e0461..7daede348 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -80,6 +80,16 @@ func (s *sdk) javaLibs(ctx android.ModuleContext) []android.SdkAware { return result } +func (s *sdk) stubsSources(ctx android.ModuleContext) []android.SdkAware { + result := []android.SdkAware{} + ctx.VisitDirectDeps(func(m android.Module) { + if j, ok := m.(*java.Droidstubs); ok { + result = append(result, j) + } + }) + return result +} + // archSpecificNativeLibInfo represents an arch-specific variant of a native lib type archSpecificNativeLibInfo struct { name string @@ -236,8 +246,8 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath { ctx: ctx, version: "current", snapshotDir: snapshotDir.OutputPath, - androidBpFile: bp, filesToZip: []android.Path{bp.path}, + androidBpFile: bp, } // copy exported AIDL files and stub jar files @@ -246,6 +256,12 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath { m.BuildSnapshot(ctx, builder) } + // copy stubs sources + stubsSources := s.stubsSources(ctx) + for _, m := range stubsSources { + m.BuildSnapshot(ctx, builder) + } + // copy exported header files and stub *.so files nativeLibInfos := s.nativeMemberInfos(ctx) for _, info := range nativeLibInfos { @@ -266,6 +282,15 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath { bp.Dedent() bp.Printfln("],") // java_libs } + if len(stubsSources) > 0 { + bp.Printfln("stubs_sources: [") + bp.Indent() + for _, m := range stubsSources { + bp.Printfln("%q,", builder.VersionedSdkMemberName(m.Name())) + } + bp.Dedent() + bp.Printfln("],") // stubs_sources + } if len(nativeLibInfos) > 0 { bp.Printfln("native_shared_libs: [") bp.Indent() @@ -284,16 +309,45 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath { filesToZip := builder.filesToZip // zip them all - zipFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.zip").OutputPath + outputZipFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.zip").OutputPath + outputRuleName := "snapshot" + outputDesc := "Building snapshot for " + ctx.ModuleName() + + // If there are no zips to merge then generate the output zip directly. + // Otherwise, generate an intermediate zip file into which other zips can be + // merged. + var zipFile android.OutputPath + var ruleName string + var desc string + if len(builder.zipsToMerge) == 0 { + zipFile = outputZipFile + ruleName = outputRuleName + desc = outputDesc + } else { + zipFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.unmerged.zip").OutputPath + ruleName = "intermediate snapshot" + desc = "Building intermediate snapshot for " + ctx.ModuleName() + } + rb := android.NewRuleBuilder() rb.Command(). BuiltTool(ctx, "soong_zip"). FlagWithArg("-C ", builder.snapshotDir.String()). FlagWithRspFileInputList("-l ", filesToZip). FlagWithOutput("-o ", zipFile) - rb.Build(pctx, ctx, "snapshot", "Building snapshot for "+ctx.ModuleName()) + rb.Build(pctx, ctx, ruleName, desc) - return zipFile + if len(builder.zipsToMerge) != 0 { + rb := android.NewRuleBuilder() + rb.Command(). + BuiltTool(ctx, "merge_zips"). + Output(outputZipFile). + Input(zipFile). + Inputs(builder.zipsToMerge) + rb.Build(pctx, ctx, outputRuleName, outputDesc) + } + + return outputZipFile } func buildSharedNativeLibSnapshot(ctx android.ModuleContext, info *nativeLibInfo, builder android.SnapshotBuilder) { @@ -404,8 +458,9 @@ type snapshotBuilder struct { ctx android.ModuleContext version string snapshotDir android.OutputPath - filesToZip android.Paths androidBpFile *generatedFile + filesToZip android.Paths + zipsToMerge android.Paths } func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) { @@ -418,6 +473,25 @@ func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) { s.filesToZip = append(s.filesToZip, path) } +func (s *snapshotBuilder) UnzipToSnapshot(zipPath android.Path, destDir string) { + ctx := s.ctx + + // Repackage the zip file so that the entries are in the destDir directory. + // This will allow the zip file to be merged into the snapshot. + tmpZipPath := android.PathForModuleOut(ctx, "tmp", destDir+".zip").OutputPath + rb := android.NewRuleBuilder() + rb.Command(). + BuiltTool(ctx, "zip2zip"). + FlagWithInput("-i ", zipPath). + FlagWithOutput("-o ", tmpZipPath). + Flag("**/*:" + destDir) + rb.Build(pctx, ctx, "repackaging "+destDir, + "Repackaging zip file "+destDir+" for snapshot "+ctx.ModuleName()) + + // Add the repackaged zip file to the files to merge. + s.zipsToMerge = append(s.zipsToMerge, tmpZipPath) +} + func (s *snapshotBuilder) AndroidBpFile() android.GeneratedSnapshotFile { return s.androidBpFile }