This CL updates platform_systemserverclasspath, systemserverclasspath_fragment, and prebuilt_systemserverclasspath_fragment to write entries for STANDALONE_SYSTEMSERVER_JARS to systemserverclasspath.pb. A new property `standalone_contents` is aded to `systemserverclasspath_fragment` and `prebuilt_systemserverclasspath_fragment` to list the standalone system server jars in the APEX, and entries will be written to systemserverclasspath.pb accordingly at build time. To add more context, these entries will be consumed by derive_classpath in order to generate an environment variable PRODUCT_STANDALONE_SYSTEM_SERVER_JARS. The environment variable will then be comsumed by odrefresh to determine what jars to preopt on early boot. Note that the variable should not end with "CLASSPATH" because the list is not used by runtime as a classpath. It is just a colon-separated list of jars. System server loads the jars separately with paths hardcoded in the code. Bug: 203198541 Test: manual - 1. Add some jars to PRODUCT_STANDALONE_SYSTEM_SERVER_JARS 2. Add some other jars to PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS and standalone_contents. 3. Build an image. 4. Flash the image to a device. 5. adb shell echo \$STANDALONE_SYSTEMSERVER_JARS 6. See the correct list of jars. Change-Id: I09a6fd1d3db85c194330da9b751702a9bf069e26
224 lines
8.4 KiB
Go
224 lines
8.4 KiB
Go
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* 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 java
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/google/blueprint"
|
|
"github.com/google/blueprint/proptools"
|
|
"strings"
|
|
|
|
"android/soong/android"
|
|
)
|
|
|
|
// Build rules and utilities to generate individual packages/modules/common/proto/classpaths.proto
|
|
// config files based on build configuration to embed into /system and /apex on a device.
|
|
//
|
|
// See `derive_classpath` service that reads the configs at runtime and defines *CLASSPATH variables
|
|
// on the device.
|
|
|
|
type classpathType int
|
|
|
|
const (
|
|
// Matches definition in packages/modules/common/proto/classpaths.proto
|
|
BOOTCLASSPATH classpathType = iota
|
|
DEX2OATBOOTCLASSPATH
|
|
SYSTEMSERVERCLASSPATH
|
|
STANDALONE_SYSTEMSERVER_JARS
|
|
)
|
|
|
|
func (c classpathType) String() string {
|
|
return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"}[c]
|
|
}
|
|
|
|
type classpathFragmentProperties struct {
|
|
// Whether to generated classpaths.proto config instance for the fragment. If the config is not
|
|
// generated, then relevant boot jars are added to platform classpath, i.e. platform_bootclasspath
|
|
// or platform_systemserverclasspath. This is useful for non-updatable APEX boot jars, to keep
|
|
// them as part of dexopt on device. Defaults to true.
|
|
Generate_classpaths_proto *bool
|
|
}
|
|
|
|
// classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH
|
|
// variables at runtime.
|
|
type classpathFragment interface {
|
|
android.Module
|
|
|
|
classpathFragmentBase() *ClasspathFragmentBase
|
|
}
|
|
|
|
// ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
|
|
// such modules are expected to call initClasspathFragment().
|
|
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.
|
|
func initClasspathFragment(c classpathFragment, classpathType classpathType) {
|
|
base := c.classpathFragmentBase()
|
|
base.classpathType = classpathType
|
|
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
|
|
}
|
|
|
|
// gatherPossibleApexModuleNamesAndStems returns a set of module and stem names from the
|
|
// supplied contents that may be in the apex boot jars.
|
|
//
|
|
// The module names are included because sometimes the stem is set to just change the name of
|
|
// the installed file and it expects the configuration to still use the actual module name.
|
|
//
|
|
// The stem names are included because sometimes the stem is set to change the effective name of the
|
|
// module that is used in the configuration as well,e .g. when a test library is overriding an
|
|
// actual boot jar
|
|
func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string {
|
|
set := map[string]struct{}{}
|
|
for _, name := range contents {
|
|
dep := ctx.GetDirectDepWithTag(name, tag)
|
|
set[name] = struct{}{}
|
|
if m, ok := dep.(ModuleWithStem); ok {
|
|
set[m.Stem()] = struct{}{}
|
|
} else {
|
|
ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name)
|
|
}
|
|
}
|
|
return android.SortedStringKeys(set)
|
|
}
|
|
|
|
// Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType.
|
|
func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, classpaths ...classpathType) []classpathJar {
|
|
paths := configuredJars.DevicePaths(ctx.Config(), android.Android)
|
|
jars := make([]classpathJar, 0, len(paths)*len(classpaths))
|
|
for i := 0; i < len(paths); i++ {
|
|
for _, classpathType := range classpaths {
|
|
jars = append(jars, classpathJar{
|
|
classpath: classpathType,
|
|
path: paths[i],
|
|
})
|
|
}
|
|
}
|
|
return jars
|
|
}
|
|
|
|
func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, jars []classpathJar) {
|
|
generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true)
|
|
if generateProto {
|
|
outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
|
|
c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
|
|
c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
|
|
|
|
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())
|
|
}
|
|
|
|
classpathProtoInfo := ClasspathFragmentProtoContentInfo{
|
|
ClasspathFragmentProtoGenerated: generateProto,
|
|
ClasspathFragmentProtoContents: configuredJars,
|
|
ClasspathFragmentProtoInstallDir: c.installDirPath,
|
|
ClasspathFragmentProtoOutput: c.outputFilepath,
|
|
}
|
|
ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo)
|
|
}
|
|
|
|
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, "\"path\": \"%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())
|
|
}
|
|
|
|
// Returns AndroidMkEntries objects to install generated classpath.proto.
|
|
// Do not use this to install into APEXes as the injection of the generated files happen separately for APEXes.
|
|
func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries {
|
|
return []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())
|
|
},
|
|
},
|
|
}}
|
|
}
|
|
|
|
var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{})
|
|
|
|
type ClasspathFragmentProtoContentInfo struct {
|
|
// Whether the classpaths.proto config is generated for the fragment.
|
|
ClasspathFragmentProtoGenerated bool
|
|
|
|
// ClasspathFragmentProtoContents contains a list of jars that are part of this classpath fragment.
|
|
ClasspathFragmentProtoContents android.ConfiguredJarList
|
|
|
|
// ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module.
|
|
//
|
|
// The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir
|
|
// for more details.
|
|
ClasspathFragmentProtoOutput android.OutputPath
|
|
|
|
// ClasspathFragmentProtoInstallDir contains information about on device location for the generated classpaths.proto file.
|
|
//
|
|
// The path encodes expected sub-location within partitions, i.e. etc/classpaths/<proto-file>,
|
|
// for ClasspathFragmentProtoOutput. To get sub-location, instead of the full output / make path
|
|
// use android.InstallPath#Rel().
|
|
//
|
|
// This is only relevant for APEX modules as they perform their own installation; while regular
|
|
// system files are installed via ClasspathFragmentBase#androidMkEntries().
|
|
ClasspathFragmentProtoInstallDir android.InstallPath
|
|
}
|