1/* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package java 18 19import ( 20 "fmt" 21 "github.com/google/blueprint" 22 "strings" 23 24 "android/soong/android" 25) 26 27// Build rules and utilities to generate individual packages/modules/SdkExtensions/proto/classpaths.proto 28// config files based on build configuration to embed into /system and /apex on a device. 29// 30// See `derive_classpath` service that reads the configs at runtime and defines *CLASSPATH variables 31// on the device. 32 33type classpathType int 34 35const ( 36 // Matches definition in packages/modules/SdkExtensions/proto/classpaths.proto 37 BOOTCLASSPATH classpathType = iota 38 DEX2OATBOOTCLASSPATH 39 SYSTEMSERVERCLASSPATH 40) 41 42func (c classpathType) String() string { 43 return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH"}[c] 44} 45 46type classpathFragmentProperties struct { 47} 48 49// classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH 50// variables at runtime. 51type classpathFragment interface { 52 android.Module 53 54 classpathFragmentBase() *ClasspathFragmentBase 55 56 // ClasspathFragmentToConfiguredJarList returns android.ConfiguredJarList representation of all 57 // the jars in this classpath fragment. 58 ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList 59} 60 61// ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment; 62// such modules are expected to call initClasspathFragment(). 63type ClasspathFragmentBase struct { 64 properties classpathFragmentProperties 65 66 classpathType classpathType 67 68 outputFilepath android.OutputPath 69 installDirPath android.InstallPath 70} 71 72func (c *ClasspathFragmentBase) classpathFragmentBase() *ClasspathFragmentBase { 73 return c 74} 75 76// Initializes ClasspathFragmentBase struct. Must be called by all modules that include ClasspathFragmentBase. 77func initClasspathFragment(c classpathFragment, classpathType classpathType) { 78 base := c.classpathFragmentBase() 79 base.classpathType = classpathType 80 c.AddProperties(&base.properties) 81} 82 83// Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto 84type classpathJar struct { 85 path string 86 classpath classpathType 87 // TODO(satayev): propagate min/max sdk versions for the jars 88 minSdkVersion int32 89 maxSdkVersion int32 90} 91 92// gatherPossibleUpdatableModuleNamesAndStems returns a set of module and stem names from the 93// supplied contents that may be in the updatable boot jars. 94// 95// The module names are included because sometimes the stem is set to just change the name of 96// the installed file and it expects the configuration to still use the actual module name. 97// 98// The stem names are included because sometimes the stem is set to change the effective name of the 99// module that is used in the configuration as well,e .g. when a test library is overriding an 100// actual boot jar 101func gatherPossibleUpdatableModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string { 102 set := map[string]struct{}{} 103 for _, name := range contents { 104 dep := ctx.GetDirectDepWithTag(name, tag) 105 set[name] = struct{}{} 106 if m, ok := dep.(ModuleWithStem); ok { 107 set[m.Stem()] = struct{}{} 108 } else { 109 ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name) 110 } 111 } 112 return android.SortedStringKeys(set) 113} 114 115// Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType. 116func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, classpaths ...classpathType) []classpathJar { 117 paths := configuredJars.DevicePaths(ctx.Config(), android.Android) 118 jars := make([]classpathJar, 0, len(paths)*len(classpaths)) 119 for i := 0; i < len(paths); i++ { 120 for _, classpathType := range classpaths { 121 jars = append(jars, classpathJar{ 122 classpath: classpathType, 123 path: paths[i], 124 }) 125 } 126 } 127 return jars 128} 129 130func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) { 131 outputFilename := strings.ToLower(c.classpathType.String()) + ".pb" 132 c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath 133 c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths") 134 135 generatedJson := android.PathForModuleOut(ctx, outputFilename+".json") 136 writeClasspathsJson(ctx, generatedJson, jars) 137 138 rule := android.NewRuleBuilder(pctx, ctx) 139 rule.Command(). 140 BuiltTool("conv_classpaths_proto"). 141 Flag("encode"). 142 Flag("--format=json"). 143 FlagWithInput("--input=", generatedJson). 144 FlagWithOutput("--output=", c.outputFilepath) 145 146 rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) 147 148 classpathProtoInfo := ClasspathFragmentProtoContentInfo{ 149 ClasspathFragmentProtoInstallDir: c.installDirPath, 150 ClasspathFragmentProtoOutput: c.outputFilepath, 151 } 152 ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo) 153} 154 155func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { 156 var content strings.Builder 157 fmt.Fprintf(&content, "{\n") 158 fmt.Fprintf(&content, "\"jars\": [\n") 159 for idx, jar := range jars { 160 fmt.Fprintf(&content, "{\n") 161 162 fmt.Fprintf(&content, "\"path\": \"%s\",\n", jar.path) 163 fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath) 164 165 if idx < len(jars)-1 { 166 fmt.Fprintf(&content, "},\n") 167 } else { 168 fmt.Fprintf(&content, "}\n") 169 } 170 } 171 fmt.Fprintf(&content, "]\n") 172 fmt.Fprintf(&content, "}\n") 173 android.WriteFileRule(ctx, output, content.String()) 174} 175 176// Returns AndroidMkEntries objects to install generated classpath.proto. 177// Do not use this to install into APEXes as the injection of the generated files happen separately for APEXes. 178func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries { 179 return []android.AndroidMkEntries{{ 180 Class: "ETC", 181 OutputFile: android.OptionalPathForPath(c.outputFilepath), 182 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 183 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 184 entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.ToMakePath().String()) 185 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base()) 186 }, 187 }, 188 }} 189} 190 191var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{}) 192 193type ClasspathFragmentProtoContentInfo struct { 194 // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module. 195 // 196 // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir 197 // for more details. 198 ClasspathFragmentProtoOutput android.OutputPath 199 200 // ClasspathFragmentProtoInstallDir contains information about on device location for the generated classpaths.proto file. 201 // 202 // The path encodes expected sub-location within partitions, i.e. etc/classpaths/<proto-file>, 203 // for ClasspathFragmentProtoOutput. To get sub-location, instead of the full output / make path 204 // use android.InstallPath#Rel(). 205 // 206 // This is only relevant for APEX modules as they perform their own installation; while regular 207 // system files are installed via ClasspathFragmentBase#androidMkEntries(). 208 ClasspathFragmentProtoInstallDir android.InstallPath 209} 210