1// Copyright 2019 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package java 16 17import ( 18 "path/filepath" 19 "strings" 20 21 "android/soong/android" 22 "android/soong/dexpreopt" 23) 24 25// dexpreoptTargets returns the list of targets that are relevant to dexpreopting, which excludes architectures 26// supported through native bridge. 27func dexpreoptTargets(ctx android.PathContext) []android.Target { 28 var targets []android.Target 29 for _, target := range ctx.Config().Targets[android.Android] { 30 if target.NativeBridge == android.NativeBridgeDisabled { 31 targets = append(targets, target) 32 } 33 } 34 // We may also need the images on host in order to run host-based tests. 35 for _, target := range ctx.Config().Targets[ctx.Config().BuildOS] { 36 targets = append(targets, target) 37 } 38 39 return targets 40} 41 42var ( 43 bootImageConfigKey = android.NewOnceKey("bootImageConfig") 44 bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw") 45 frameworkBootImageName = "boot" 46 mainlineBootImageName = "mainline" 47 bootImageStem = "boot" 48 ProfileInstallPathInApex = "etc/boot-image.prof" 49) 50 51// getImageNames returns an ordered list of image names. The order doesn't matter but needs to be 52// deterministic. The names listed here must match the map keys returned by genBootImageConfigs. 53func getImageNames() []string { 54 return []string{"art", "boot", "mainline"} 55} 56 57func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig { 58 return ctx.Config().Once(bootImageConfigRawKey, func() interface{} { 59 global := dexpreopt.GetGlobalConfig(ctx) 60 61 artBootImageName := "art" // Keep this local to avoid accidental references. 62 frameworkModules := global.BootJars // This includes `global.ArtApexJars`. 63 mainlineBcpModules := global.ApexBootJars 64 frameworkSubdir := "system/framework" 65 66 profileImports := []string{"com.android.art"} 67 68 // ART boot image for testing only. Do not rely on it to make any build-time decision. 69 artCfg := bootImageConfig{ 70 name: artBootImageName, 71 enabledIfExists: "art-bootclasspath-fragment", 72 stem: bootImageStem, 73 installDir: "apex/art_boot_images/javalib", 74 modules: global.TestOnlyArtBootImageJars, 75 preloadedClassesFile: "art/build/boot/preloaded-classes", 76 compilerFilter: "speed-profile", 77 singleImage: false, 78 profileImports: profileImports, 79 } 80 81 // Framework config for the boot image extension. 82 // It includes framework libraries and depends on the ART config. 83 frameworkCfg := bootImageConfig{ 84 name: frameworkBootImageName, 85 enabledIfExists: "platform-bootclasspath", 86 stem: bootImageStem, 87 installDir: frameworkSubdir, 88 modules: frameworkModules, 89 preloadedClassesFile: "frameworks/base/config/preloaded-classes", 90 compilerFilter: "speed-profile", 91 singleImage: false, 92 profileImports: profileImports, 93 } 94 95 mainlineCfg := bootImageConfig{ 96 extends: &frameworkCfg, 97 name: mainlineBootImageName, 98 enabledIfExists: "platform-bootclasspath", 99 stem: bootImageStem, 100 installDir: frameworkSubdir, 101 modules: mainlineBcpModules, 102 compilerFilter: "verify", 103 singleImage: true, 104 } 105 106 return map[string]*bootImageConfig{ 107 artBootImageName: &artCfg, 108 frameworkBootImageName: &frameworkCfg, 109 mainlineBootImageName: &mainlineCfg, 110 } 111 }).(map[string]*bootImageConfig) 112} 113 114// Construct the global boot image configs. 115func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { 116 return ctx.Config().Once(bootImageConfigKey, func() interface{} { 117 targets := dexpreoptTargets(ctx) 118 deviceDir := android.PathForOutput(ctx, dexpreopt.GetDexpreoptDirName(ctx)) 119 120 configs := genBootImageConfigRaw(ctx) 121 122 for _, c := range configs { 123 c.dir = deviceDir.Join(ctx, "dex_"+c.name+"jars") 124 c.symbolsDir = deviceDir.Join(ctx, "dex_"+c.name+"jars_unstripped") 125 126 // expands to <stem>.art for primary image and <stem>-<1st module>.art for extension 127 imageName := c.firstModuleNameOrStem(ctx) + ".art" 128 129 // The path to bootclasspath dex files needs to be known at module 130 // GenerateAndroidBuildAction time, before the bootclasspath modules have been compiled. 131 // Set up known paths for them, the singleton rules will copy them there. 132 // TODO(b/143682396): use module dependencies instead 133 inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input") 134 c.dexPaths = c.modules.BuildPaths(ctx, inputDir) 135 c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir) 136 c.dexPathsDeps = c.dexPaths 137 138 // Create target-specific variants. 139 for _, target := range targets { 140 arch := target.Arch.ArchType 141 imageDir := c.dir.Join(ctx, target.Os.String(), c.installDir, arch.String()) 142 variant := &bootImageVariant{ 143 bootImageConfig: c, 144 target: target, 145 imagePathOnHost: imageDir.Join(ctx, imageName), 146 imagePathOnDevice: filepath.Join("/", c.installDir, arch.String(), imageName), 147 imagesDeps: c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"), 148 dexLocations: c.modules.DevicePaths(ctx.Config(), target.Os), 149 } 150 variant.dexLocationsDeps = variant.dexLocations 151 c.variants = append(c.variants, variant) 152 } 153 154 c.zip = c.dir.Join(ctx, c.name+".zip") 155 } 156 157 visited := make(map[string]bool) 158 for _, c := range configs { 159 calculateDepsRecursive(c, targets, visited) 160 } 161 162 return configs 163 }).(map[string]*bootImageConfig) 164} 165 166// calculateDepsRecursive calculates the dependencies of the given boot image config and all its 167// ancestors, if they are not visited. 168// The boot images are supposed to form a tree, where the root is the primary boot image. We do not 169// expect loops (e.g., A extends B, B extends C, C extends A), and we let them crash soong with a 170// stack overflow. 171// Note that a boot image config only has a pointer to the parent, not to children. Therefore, we 172// first go up through the parent chain, and then go back down to visit every code along the path. 173// `visited` is a map where a key is a boot image name and the value indicates whether the boot 174// image config is visited. The boot image names are guaranteed to be unique because they come from 175// `genBootImageConfigRaw` above, which also returns a map and would fail in the first place if the 176// names were not unique. 177func calculateDepsRecursive(c *bootImageConfig, targets []android.Target, visited map[string]bool) { 178 if c.extends == nil || visited[c.name] { 179 return 180 } 181 if c.extends.extends != nil { 182 calculateDepsRecursive(c.extends, targets, visited) 183 } 184 visited[c.name] = true 185 c.dexPathsDeps = android.Concat(c.extends.dexPathsDeps, c.dexPathsDeps) 186 for i := range targets { 187 c.variants[i].baseImages = android.Concat(c.extends.variants[i].baseImages, android.OutputPaths{c.extends.variants[i].imagePathOnHost}) 188 c.variants[i].baseImagesDeps = android.Concat(c.extends.variants[i].baseImagesDeps, c.extends.variants[i].imagesDeps.Paths()) 189 c.variants[i].dexLocationsDeps = android.Concat(c.extends.variants[i].dexLocationsDeps, c.variants[i].dexLocationsDeps) 190 } 191} 192 193func defaultBootImageConfig(ctx android.PathContext) *bootImageConfig { 194 return genBootImageConfigs(ctx)[frameworkBootImageName] 195} 196 197func mainlineBootImageConfig(ctx android.PathContext) *bootImageConfig { 198 return genBootImageConfigs(ctx)[mainlineBootImageName] 199} 200 201// isProfileProviderApex returns true if this apex provides a boot image profile. 202func isProfileProviderApex(ctx android.PathContext, apexName string) bool { 203 for _, config := range genBootImageConfigs(ctx) { 204 for _, profileImport := range config.profileImports { 205 if profileImport == apexName { 206 return true 207 } 208 } 209 } 210 return false 211} 212 213// Returns a list of paths and a list of locations for the boot jars used in dexpreopt (to be 214// passed in -Xbootclasspath and -Xbootclasspath-locations arguments for dex2oat). 215func bcpForDexpreopt(ctx android.PathContext, withUpdatable bool) (android.WritablePaths, []string) { 216 bootImage := defaultBootImageConfig(ctx) 217 if withUpdatable { 218 bootImage = mainlineBootImageConfig(ctx) 219 } 220 221 dexPaths := bootImage.dexPathsDeps 222 // The dex locations for all Android variants are identical. 223 dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps 224 225 return dexPaths, dexLocations 226} 227 228var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath") 229 230func init() { 231 android.RegisterMakeVarsProvider(pctx, dexpreoptConfigMakevars) 232} 233 234func dexpreoptConfigMakevars(ctx android.MakeVarsContext) { 235 ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":")) 236} 237