blob: 3d725808646bab3b12796f3cabd986eb9e48ecfe [file] [log] [blame]
/*
* 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
}
// 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, 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,
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
// 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
}