diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go index adbe490f3..d4974600f 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -17,6 +17,9 @@ package java import ( + "fmt" + "strings" + "android/soong/android" ) @@ -47,21 +50,102 @@ type classpathFragmentProperties struct { type classpathFragment interface { android.Module - classpathFragmentBase() *classpathFragmentBase + classpathFragmentBase() *ClasspathFragmentBase } -// classpathFragmentBase is meant to be embedded in any module types that implement classpathFragment; +// ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment; // such modules are expected to call initClasspathFragment(). -type classpathFragmentBase struct { +type ClasspathFragmentBase struct { properties classpathFragmentProperties - classpathType classpathType - outputFilepath android.OutputPath + installDirPath android.InstallPath } -// Initializes classpathFragmentBase struct. Must be called by all modules that include classpathFragmentBase. +func (c *ClasspathFragmentBase) classpathFragmentBase() *ClasspathFragmentBase { + return c +} + +// Initializes ClasspathFragmentBase struct. Must be called by all modules that include ClasspathFragmentBase. func initClasspathFragment(c classpathFragment) { base := c.classpathFragmentBase() c.AddProperties(&base.properties) } + +// Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto +type classpathJar struct { + path string + classpath classpathType + // TODO(satayev): propagate min/max sdk versions for the jars + minSdkVersion int32 + maxSdkVersion int32 +} + +func (c *ClasspathFragmentBase) generateAndroidBuildActions(ctx android.ModuleContext) { + outputFilename := ctx.ModuleName() + ".pb" + c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath + c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths") + + var jars []classpathJar + jars = appendClasspathJar(jars, BOOTCLASSPATH, defaultBootclasspath(ctx)...) + jars = appendClasspathJar(jars, DEX2OATBOOTCLASSPATH, defaultBootImageConfig(ctx).getAnyAndroidVariant().dexLocationsDeps...) + jars = appendClasspathJar(jars, SYSTEMSERVERCLASSPATH, systemServerClasspath(ctx)...) + + generatedJson := android.PathForModuleOut(ctx, outputFilename+".json") + writeClasspathsJson(ctx, generatedJson, jars) + + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("conv_classpaths_proto"). + Flag("encode"). + Flag("--format=json"). + FlagWithInput("--input=", generatedJson). + FlagWithOutput("--output=", c.outputFilepath) + + rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) +} + +func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { + var content strings.Builder + fmt.Fprintf(&content, "{\n") + fmt.Fprintf(&content, "\"jars\": [\n") + for idx, jar := range jars { + fmt.Fprintf(&content, "{\n") + + fmt.Fprintf(&content, "\"relativePath\": \"%s\",\n", jar.path) + fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath) + + if idx < len(jars)-1 { + fmt.Fprintf(&content, "},\n") + } else { + fmt.Fprintf(&content, "}\n") + } + } + fmt.Fprintf(&content, "]\n") + fmt.Fprintf(&content, "}\n") + android.WriteFileRule(ctx, output, content.String()) +} + +func appendClasspathJar(slice []classpathJar, classpathType classpathType, paths ...string) (result []classpathJar) { + result = append(result, slice...) + for _, path := range paths { + result = append(result, classpathJar{ + path: path, + classpath: classpathType, + }) + } + return +} + +func (c *ClasspathFragmentBase) getAndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{android.AndroidMkEntries{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(c.outputFilepath), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.ToMakePath().String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base()) + }, + }, + }} +} diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index 64b265640..656f5ef71 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -26,7 +26,7 @@ import ( // systemServerClasspath returns the on-device locations of the modules in the system server classpath. It is computed // once the first time it is called for any ctx.Config(), and returns the same slice for all future calls with the same // ctx.Config(). -func systemServerClasspath(ctx android.MakeVarsContext) []string { +func systemServerClasspath(ctx android.PathContext) []string { return ctx.Config().OnceStringSlice(systemServerClasspathKey, func() []string { global := dexpreopt.GetGlobalConfig(ctx) var systemServerClasspathLocations []string diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index d98ce6719..6f242a149 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -59,6 +59,7 @@ var _ android.ExcludeFromVisibilityEnforcementTag = platformBootclasspathDepende type platformBootclasspathModule struct { android.ModuleBase + ClasspathFragmentBase properties platformBootclasspathProperties @@ -105,22 +106,23 @@ type platformBootclasspathProperties struct { func platformBootclasspathFactory() android.Module { m := &platformBootclasspathModule{} m.AddProperties(&m.properties) + // TODO(satayev): split systemserver and apex jars into separate configs. + initClasspathFragment(m) android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) return m } var _ android.OutputFileProducer = (*platformBootclasspathModule)(nil) -// A minimal AndroidMkEntries is needed in order to support the dists property. -func (b *platformBootclasspathModule) AndroidMkEntries() []android.AndroidMkEntries { - return []android.AndroidMkEntries{ - { - Class: "FAKE", - // Need at least one output file in order for this to take effect. - OutputFile: android.OptionalPathForPath(b.hiddenAPIFlagsCSV), - Include: "$(BUILD_PHONY_PACKAGE)", - }, - } +func (b *platformBootclasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) { + entries = append(entries, android.AndroidMkEntries{ + Class: "FAKE", + // Need at least one output file in order for this to take effect. + OutputFile: android.OptionalPathForPath(b.hiddenAPIFlagsCSV), + Include: "$(BUILD_PHONY_PACKAGE)", + }) + entries = append(entries, b.classpathFragmentBase().getAndroidMkEntries()...) + return } // Make the hidden API files available from the platform-bootclasspath module. @@ -222,6 +224,8 @@ func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, mod } func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + b.classpathFragmentBase().generateAndroidBuildActions(ctx) + ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) { tag := ctx.OtherModuleDependencyTag(module) if tag == platformBootclasspathModuleDepTag { diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go index c740911f1..26587c74b 100644 --- a/java/platform_bootclasspath_test.go +++ b/java/platform_bootclasspath_test.go @@ -132,6 +132,89 @@ func TestPlatformBootclasspath(t *testing.T) { }) } +func TestPlatformBootclasspathVariant(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + android.FixtureWithRootAndroidBp(` + platform_bootclasspath { + name: "platform-bootclasspath", + } + `), + ).RunTest(t) + + variants := result.ModuleVariantsForTests("platform-bootclasspath") + android.AssertIntEquals(t, "expect 1 variant", 1, len(variants)) +} + +func TestPlatformBootclasspath_ClasspathFragmentPaths(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + android.FixtureWithRootAndroidBp(` + platform_bootclasspath { + name: "platform-bootclasspath", + } + `), + ).RunTest(t) + + p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule) + android.AssertStringEquals(t, "output filepath", p.Name()+".pb", p.ClasspathFragmentBase.outputFilepath.Base()) + android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath) +} + +func TestPlatformBootclasspathModule_AndroidMkEntries(t *testing.T) { + preparer := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + android.FixtureWithRootAndroidBp(` + platform_bootclasspath { + name: "platform-bootclasspath", + } + `), + ) + + t.Run("AndroidMkEntries", func(t *testing.T) { + result := preparer.RunTest(t) + + p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule) + + entries := android.AndroidMkEntriesForTest(t, result.TestContext, p) + android.AssertIntEquals(t, "AndroidMkEntries count", 2, len(entries)) + }) + + t.Run("hiddenapi-flags-entry", func(t *testing.T) { + result := preparer.RunTest(t) + + p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule) + + entries := android.AndroidMkEntriesForTest(t, result.TestContext, p) + got := entries[0].OutputFile + android.AssertBoolEquals(t, "valid output path", true, got.Valid()) + android.AssertSame(t, "output filepath", p.hiddenAPIFlagsCSV, got.Path()) + }) + + t.Run("classpath-fragment-entry", func(t *testing.T) { + result := preparer.RunTest(t) + + want := map[string][]string{ + "LOCAL_MODULE": {"platform-bootclasspath"}, + "LOCAL_MODULE_CLASS": {"ETC"}, + "LOCAL_INSTALLED_MODULE_STEM": {"platform-bootclasspath.pb"}, + // Output and Install paths are tested separately in TestPlatformBootclasspath_ClasspathFragmentPaths + } + + p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule) + + entries := android.AndroidMkEntriesForTest(t, result.TestContext, p) + got := entries[1] + for k, expectedValue := range want { + if value, ok := got.EntryMap[k]; ok { + android.AssertDeepEquals(t, k, expectedValue, value) + } else { + t.Errorf("No %s defined, saw %q", k, got.EntryMap) + } + } + }) +} + func TestPlatformBootclasspath_Dist(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithPlatformBootclasspath,