Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 97259dc6 authored by Artur Satayev's avatar Artur Satayev
Browse files

Generate classpaths.proto config for *CLASSPATH variables.

Instead of embedding "raw" values from PRODUCT_*_CLASSPATH variables
into /etc/classpath, encode them into classpaths.proto binary format.

Existing platform_bootclasspath{} module is used to generate the whole
monolithic config to begin with. Later, the config will be split
among individual bootclasspath_fragment and
systemserverclasspath_fragment modules.

Bug: 180105615
Test: m && launch_cvd
Change-Id: Ia66f521f8976ff78a62ecf91131d26db21064de7
parent 21fb92d7
Loading
Loading
Loading
Loading
+90 −6
Original line number Diff line number Diff line
@@ -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
}

func (c *ClasspathFragmentBase) classpathFragmentBase() *ClasspathFragmentBase {
	return c
}

// Initializes classpathFragmentBase struct. Must be called by all modules that include classpathFragmentBase.
// 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())
			},
		},
	}}
}
+1 −1
Original line number Diff line number Diff line
@@ -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
+14 −10
Original line number Diff line number Diff line
@@ -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{
		{
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 {
+83 −0
Original line number Diff line number Diff line
@@ -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,