123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- /*
- * 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
- minSdkVersion string
- maxSdkVersion string
- }
- // 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.SortedKeys(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 {
- jar := classpathJar{
- classpath: classpathType,
- path: paths[i],
- }
- ctx.VisitDirectDepsIf(func(m android.Module) bool {
- return m.Name() == configuredJars.Jar(i)
- }, func(m android.Module) {
- if s, ok := m.(*SdkLibrary); ok {
- // TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current"
- if s.minSdkVersion.Specified() {
- if s.minSdkVersion.IsCurrent() {
- jar.minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
- } else {
- jar.minSdkVersion = s.minSdkVersion.String()
- }
- }
- if s.maxSdkVersion.Specified() {
- if s.maxSdkVersion.IsCurrent() {
- jar.maxSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
- } else {
- jar.maxSdkVersion = s.maxSdkVersion.String()
- }
- }
- }
- })
- jars = append(jars, jar)
- }
- }
- 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")
- generatedTextproto := android.PathForModuleOut(ctx, outputFilename+".textproto")
- writeClasspathsTextproto(ctx, generatedTextproto, jars)
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("conv_classpaths_proto").
- Flag("encode").
- Flag("--format=textproto").
- FlagWithInput("--input=", generatedTextproto).
- 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 writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
- var content strings.Builder
- for _, jar := range jars {
- fmt.Fprintf(&content, "jars {\n")
- fmt.Fprintf(&content, "path: \"%s\"\n", jar.path)
- fmt.Fprintf(&content, "classpath: %s\n", jar.classpath)
- fmt.Fprintf(&content, "min_sdk_version: \"%s\"\n", jar.minSdkVersion)
- fmt.Fprintf(&content, "max_sdk_version: \"%s\"\n", jar.maxSdkVersion)
- 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.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
- }
|