platform_build_soong/java/classpath_fragment.go
Paul Duffin 56c93e899a Use both module name and stem name to filter updatable boot jars
Sometimes the stem property is set to change both the installed file
name and the name use to filter the configured module list, e.g. when
adding a test library to replace the standard library, e.g. test_foo
instead of foo.

Sometimes it is used just to change the installed file name.

This change uses both to filter the updatable boot jars and not just
the stem.

Bug: 180105615
Test: m nothing
Change-Id: I6c459fc3597b1e4f062bc9a4e52843305b538c5f
2021-06-29 23:35:25 +01:00

222 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/SdkExtensions/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/SdkExtensions/proto/classpaths.proto
BOOTCLASSPATH classpathType = iota
DEX2OATBOOTCLASSPATH
SYSTEMSERVERCLASSPATH
)
func (c classpathType) String() string {
return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH"}[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
}
// gatherPossibleUpdatableModuleNamesAndStems returns a set of module and stem names from the
// supplied contents that may be in the updatable 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 gatherPossibleUpdatableModuleNamesAndStems(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
}