// Copyright 2018 Google Inc. All rights reserved. // // 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 ( "path/filepath" "strings" "github.com/google/blueprint" "github.com/google/blueprint/proptools" "android/soong/android" "android/soong/dexpreopt" ) type dexpreopter struct { dexpreoptProperties DexpreoptProperties installPath android.OutputPath isPrivApp bool isSDKLibrary bool isTest bool builtInstalled []string } type DexpreoptProperties struct { Dex_preopt struct { // If false, prevent dexpreopting and stripping the dex file from the final jar. Defaults to // true. Enabled *bool // If true, generate an app image (.art file) for this module. App_image *bool // If true, use a checked-in profile to guide optimization. Defaults to false unless // a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR // that matches the name of this module, in which case it is defaulted to true. Profile_guided *bool // If set, provides the path to profile relative to the Android.bp file. If not set, // defaults to searching for a file that matches the name of this module in the default // profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found. Profile *string } } func (d *dexpreopter) dexpreoptDisabled(ctx android.ModuleContext) bool { if ctx.Config().DisableDexPreopt(ctx.ModuleName()) { return true } if ctx.Config().UnbundledBuild() { return true } if d.isTest { return true } if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) { return true } // TODO: contains no java code return false } func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) android.ModuleOutPath { if d.dexpreoptDisabled(ctx) { return dexJarFile } globalConfig := ctx.Config().Once("DexpreoptGlobalConfig", func() interface{} { if f := ctx.Config().DexpreoptGlobalConfig(); f != "" { ctx.AddNinjaFileDeps(f) globalConfig, err := dexpreopt.LoadGlobalConfig(f) if err != nil { panic(err) } return globalConfig } return dexpreopt.GlobalConfig{} }).(dexpreopt.GlobalConfig) var archs []string for _, a := range ctx.MultiTargets() { archs = append(archs, a.Arch.ArchType.String()) } if len(archs) == 0 { // assume this is a java library, dexpreopt for all arches for now for _, target := range ctx.Config().Targets[android.Android] { archs = append(archs, target.Arch.ArchType.String()) } if inList(ctx.ModuleName(), globalConfig.SystemServerJars) && !d.isSDKLibrary { // If the module is not an SDK library and it's a system server jar, only preopt the primary arch. archs = archs[:1] } } if ctx.Config().SecondArchIsTranslated() { // Only preopt primary arch for translated arch since there is only an image there. archs = archs[:1] } dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath) strippedDexJarFile := android.PathForModuleOut(ctx, "dexpreopt", dexJarFile.Base()) deps := android.Paths{dexJarFile} var profileClassListing android.OptionalPath profileIsTextListing := false if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) { // If dex_preopt.profile_guided is not set, default it based on the existence of the // dexprepot.profile option or the profile class listing. if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" { profileClassListing = android.OptionalPathForPath( android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile))) profileIsTextListing = true } else { profileClassListing = android.ExistentPathForSource(ctx, ctx.Config().DexPreoptProfileDir(), ctx.ModuleName()+".prof") } } if profileClassListing.Valid() { deps = append(deps, profileClassListing.Path()) } uncompressedDex := false if ctx.Config().UncompressPrivAppDex() && (d.isPrivApp || inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules())) { uncompressedDex = true } dexpreoptConfig := dexpreopt.ModuleConfig{ Name: ctx.ModuleName(), DexLocation: dexLocation, BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").String(), DexPath: dexJarFile.String(), PreferIntegrity: false, UncompressedDex: uncompressedDex, HasApkLibraries: false, PreoptFlags: nil, ProfileClassListing: profileClassListing.String(), ProfileIsTextListing: profileIsTextListing, EnforceUsesLibraries: false, OptionalUsesLibraries: nil, UsesLibraries: nil, LibraryPaths: nil, Archs: archs, DexPreoptImageLocation: "", PreoptExtractedApk: false, NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true), ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false), StripInputPath: dexJarFile.String(), StripOutputPath: strippedDexJarFile.String(), } dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(globalConfig, dexpreoptConfig) if err != nil { ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error()) return dexJarFile } var inputs android.Paths for _, input := range dexpreoptRule.Inputs() { if input == "" { // Tests sometimes have empty configuration values that lead to empty inputs continue } rel, isRel := android.MaybeRel(ctx, android.PathForModuleOut(ctx).String(), input) if isRel { inputs = append(inputs, android.PathForModuleOut(ctx, rel)) } else { // TODO: use PathForOutput once boot image is moved to where PathForOutput can find it. inputs = append(inputs, &bootImagePath{input}) } } var outputs android.WritablePaths for _, output := range dexpreoptRule.Outputs() { rel := android.Rel(ctx, android.PathForModuleOut(ctx).String(), output) outputs = append(outputs, android.PathForModuleOut(ctx, rel)) } for _, install := range dexpreoptRule.Installs() { d.builtInstalled = append(d.builtInstalled, install.From+":"+install.To) } if len(dexpreoptRule.Commands()) > 0 { ctx.Build(pctx, android.BuildParams{ Rule: ctx.Rule(pctx, "dexpreopt", blueprint.RuleParams{ Command: strings.Join(proptools.NinjaEscape(dexpreoptRule.Commands()), " && "), CommandDeps: dexpreoptRule.Tools(), }), Implicits: inputs, Outputs: outputs, Description: "dexpreopt", }) } stripRule, err := dexpreopt.GenerateStripRule(globalConfig, dexpreoptConfig) if err != nil { ctx.ModuleErrorf("error generating dexpreopt strip rule: %s", err.Error()) return dexJarFile } ctx.Build(pctx, android.BuildParams{ Rule: ctx.Rule(pctx, "dexpreopt_strip", blueprint.RuleParams{ Command: strings.Join(proptools.NinjaEscape(stripRule.Commands()), " && "), CommandDeps: stripRule.Tools(), }), Input: dexJarFile, Output: strippedDexJarFile, Description: "dexpreopt strip", }) return strippedDexJarFile } type bootImagePath struct { path string } var _ android.Path = (*bootImagePath)(nil) func (p *bootImagePath) String() string { return p.path } func (p *bootImagePath) Ext() string { return filepath.Ext(p.path) } func (p *bootImagePath) Base() string { return filepath.Base(p.path) } func (p *bootImagePath) Rel() string { return p.path }