| /* |
| * Copyright 2014-present Facebook, Inc. |
| * |
| * 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 com.facebook.buck.android; |
| |
| import com.facebook.buck.android.AndroidBinary.ExopackageMode; |
| import com.facebook.buck.android.AndroidBinary.PackageType; |
| import com.facebook.buck.android.AndroidBinary.TargetCpuType; |
| import com.facebook.buck.android.FilterResourcesStep.ResourceFilter; |
| import com.facebook.buck.android.ResourcesFilter.ResourceCompressionMode; |
| import com.facebook.buck.dalvik.ZipSplitter.DexSplitStrategy; |
| import com.facebook.buck.java.JavaLibrary; |
| import com.facebook.buck.java.JavacOptions; |
| import com.facebook.buck.java.Keystore; |
| import com.facebook.buck.model.BuildTarget; |
| import com.facebook.buck.model.HasBuildTarget; |
| import com.facebook.buck.parser.BuildTargetParser; |
| import com.facebook.buck.rules.BuildRule; |
| import com.facebook.buck.rules.BuildRuleParams; |
| import com.facebook.buck.rules.BuildRuleResolver; |
| import com.facebook.buck.rules.BuildRuleType; |
| import com.facebook.buck.rules.BuildRules; |
| import com.facebook.buck.rules.Description; |
| import com.facebook.buck.rules.Hint; |
| import com.facebook.buck.rules.ImmutableBuildRuleType; |
| import com.facebook.buck.rules.SourcePath; |
| import com.facebook.buck.rules.SourcePathResolver; |
| import com.facebook.buck.rules.coercer.BuildConfigFields; |
| import com.facebook.buck.rules.macros.ExecutableMacroExpander; |
| import com.facebook.buck.rules.macros.LocationMacroExpander; |
| import com.facebook.buck.rules.macros.MacroExpander; |
| import com.facebook.buck.rules.macros.MacroHandler; |
| import com.facebook.buck.util.HumanReadableException; |
| import com.facebook.infer.annotation.SuppressFieldNotInitialized; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Preconditions; |
| import com.google.common.base.Suppliers; |
| import com.google.common.collect.FluentIterable; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.ImmutableSortedSet; |
| |
| import java.util.EnumSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| public class AndroidBinaryDescription implements Description<AndroidBinaryDescription.Arg> { |
| |
| public static final BuildRuleType TYPE = ImmutableBuildRuleType.of("android_binary"); |
| |
| /** |
| * By default, assume we have 5MB of linear alloc, |
| * 1MB of which is taken up by the framework, so that leaves 4MB. |
| */ |
| private static final long DEFAULT_LINEAR_ALLOC_HARD_LIMIT = 4 * 1024 * 1024; |
| |
| private static final BuildTargetParser BUILD_TARGET_PARSER = new BuildTargetParser(); |
| private static final MacroHandler MACRO_HANDLER = |
| new MacroHandler( |
| ImmutableMap.<String, MacroExpander>of( |
| "exe", new ExecutableMacroExpander(BUILD_TARGET_PARSER), |
| "location", new LocationMacroExpander(BUILD_TARGET_PARSER))); |
| |
| private static final Pattern COUNTRY_LOCALE_PATTERN = Pattern.compile("([a-z]{2})-[A-Z]{2}"); |
| |
| private final JavacOptions javacOptions; |
| private final ProGuardConfig proGuardConfig; |
| private final ImmutableMap<TargetCpuType, NdkCxxPlatform> nativePlatforms; |
| |
| public AndroidBinaryDescription( |
| JavacOptions javacOptions, |
| ProGuardConfig proGuardConfig, |
| ImmutableMap<TargetCpuType, NdkCxxPlatform> nativePlatforms) { |
| this.javacOptions = javacOptions; |
| this.proGuardConfig = proGuardConfig; |
| this.nativePlatforms = nativePlatforms; |
| } |
| |
| @Override |
| public BuildRuleType getBuildRuleType() { |
| return TYPE; |
| } |
| |
| @Override |
| public Arg createUnpopulatedConstructorArg() { |
| return new Arg(); |
| } |
| |
| @Override |
| public <A extends Arg> AndroidBinary createBuildRule( |
| final BuildRuleParams params, |
| final BuildRuleResolver resolver, |
| A args) { |
| BuildRule keystore = resolver.getRule(args.keystore); |
| if (!(keystore instanceof Keystore)) { |
| throw new HumanReadableException( |
| "In %s, keystore='%s' must be a keystore() but was %s().", |
| params.getBuildTarget(), |
| keystore.getFullyQualifiedName(), |
| keystore.getType().getName()); |
| } |
| |
| ProGuardObfuscateStep.SdkProguardType androidSdkProguardConfig = |
| args.androidSdkProguardConfig.or(ProGuardObfuscateStep.SdkProguardType.DEFAULT); |
| |
| // If the old boolean version of this argument was specified, make sure the new form |
| // was not specified, and allow the old form to override the default. |
| if (args.useAndroidProguardConfigWithOptimizations.isPresent()) { |
| Preconditions.checkArgument( |
| !args.androidSdkProguardConfig.isPresent(), |
| "The deprecated use_android_proguard_config_with_optimizations parameter" + |
| " cannot be used with android_sdk_proguard_config."); |
| androidSdkProguardConfig = args.useAndroidProguardConfigWithOptimizations.or(false) |
| ? ProGuardObfuscateStep.SdkProguardType.OPTIMIZED |
| : ProGuardObfuscateStep.SdkProguardType.DEFAULT; |
| } |
| |
| EnumSet<ExopackageMode> exopackageModes = EnumSet.noneOf(ExopackageMode.class); |
| if (args.exopackageModes.isPresent() && !args.exopackageModes.get().isEmpty()) { |
| exopackageModes = EnumSet.copyOf(args.exopackageModes.get()); |
| } else if (args.exopackage.or(false)) { |
| exopackageModes = EnumSet.of(ExopackageMode.SECONDARY_DEX); |
| } |
| |
| DexSplitMode dexSplitMode = createDexSplitMode(args, exopackageModes); |
| |
| boolean allowNonExistentRule = |
| false; |
| ImmutableSortedSet<BuildRule> buildRulesToExcludeFromDex = BuildRules.toBuildRulesFor( |
| params.getBuildTarget(), |
| resolver, |
| args.noDx.or(ImmutableSet.<BuildTarget>of()), |
| allowNonExistentRule); |
| ImmutableSortedSet<JavaLibrary> rulesToExcludeFromDex = |
| FluentIterable.from(buildRulesToExcludeFromDex) |
| .filter(JavaLibrary.class) |
| .toSortedSet(HasBuildTarget.BUILD_TARGET_COMPARATOR); |
| |
| PackageType packageType = getPackageType(args); |
| boolean shouldPreDex = !args.disablePreDex.or(false) && |
| PackageType.DEBUG.equals(packageType) && |
| !args.preprocessJavaClassesBash.isPresent(); |
| |
| ResourceCompressionMode compressionMode = getCompressionMode(args); |
| ResourceFilter resourceFilter = |
| new ResourceFilter(args.resourceFilter.or(ImmutableList.<String>of())); |
| |
| AndroidBinaryGraphEnhancer graphEnhancer = new AndroidBinaryGraphEnhancer( |
| params, |
| resolver, |
| compressionMode, |
| resourceFilter, |
| addFallbackLocales(args.locales.or(ImmutableSet.<String>of())), |
| args.manifest, |
| packageType, |
| ImmutableSet.copyOf(args.cpuFilters.get()), |
| args.buildStringSourceMap.or(false), |
| shouldPreDex, |
| AndroidBinary.getPrimaryDexPath(params.getBuildTarget()), |
| dexSplitMode, |
| ImmutableSet.copyOf(args.noDx.or(ImmutableSet.<BuildTarget>of())), |
| /* resourcesToExclude */ ImmutableSet.<BuildTarget>of(), |
| args.skipCrunchPngs.or(false), |
| javacOptions, |
| exopackageModes, |
| (Keystore) keystore, |
| args.buildConfigValues.get(), |
| args.buildConfigValuesFile, |
| nativePlatforms); |
| AndroidGraphEnhancementResult result = |
| graphEnhancer.createAdditionalBuildables(); |
| |
| return new AndroidBinary( |
| params.copyWithExtraDeps(Suppliers.ofInstance(result.getFinalDeps())), |
| new SourcePathResolver(resolver), |
| proGuardConfig.getProguardJarOverride(), |
| proGuardConfig.getProguardMaxHeapSize(), |
| args.manifest, |
| (Keystore) keystore, |
| packageType, |
| dexSplitMode, |
| args.noDx.or(ImmutableSet.<BuildTarget>of()), |
| androidSdkProguardConfig, |
| args.optimizationPasses, |
| args.proguardConfig, |
| compressionMode, |
| args.cpuFilters.get(), |
| resourceFilter, |
| exopackageModes, |
| resolver.getAllRules( |
| args.preprocessJavaClassesDeps.or(ImmutableSortedSet.<BuildTarget>of())), |
| MACRO_HANDLER.getExpander( |
| params.getBuildTarget(), |
| resolver, |
| params.getProjectFilesystem()), |
| args.preprocessJavaClassesBash, |
| rulesToExcludeFromDex, |
| result); |
| } |
| |
| private DexSplitMode createDexSplitMode(Arg args, EnumSet<ExopackageMode> exopackageModes) { |
| // Exopackage builds default to JAR, otherwise, default to RAW. |
| DexStore defaultDexStore = ExopackageMode.enabledForSecondaryDexes(exopackageModes) |
| ? DexStore.JAR |
| : DexStore.RAW; |
| DexSplitStrategy dexSplitStrategy = args.minimizePrimaryDexSize.or(false) |
| ? DexSplitStrategy.MINIMIZE_PRIMARY_DEX_SIZE |
| : DexSplitStrategy.MAXIMIZE_PRIMARY_DEX_SIZE; |
| return new DexSplitMode( |
| args.useSplitDex.or(false), |
| dexSplitStrategy, |
| args.dexCompression.or(defaultDexStore), |
| args.useLinearAllocSplitDex.or(false), |
| args.linearAllocHardLimit.or(DEFAULT_LINEAR_ALLOC_HARD_LIMIT), |
| args.primaryDexPatterns.or(ImmutableList.<String>of()), |
| args.primaryDexClassesFile, |
| args.primaryDexScenarioFile, |
| args.primaryDexScenarioOverflowAllowed.or(false), |
| args.secondaryDexHeadClassesFile, |
| args.secondaryDexTailClassesFile); |
| } |
| |
| private PackageType getPackageType(Arg args) { |
| if (!args.packageType.isPresent()) { |
| return PackageType.DEBUG; |
| } |
| return PackageType.valueOf(args.packageType.get().toUpperCase()); |
| } |
| |
| private ResourceCompressionMode getCompressionMode(Arg args) { |
| if (!args.resourceCompression.isPresent()) { |
| return ResourceCompressionMode.DISABLED; |
| } |
| return ResourceCompressionMode.valueOf(args.resourceCompression.get().toUpperCase()); |
| } |
| |
| private ImmutableSet<String> addFallbackLocales(ImmutableSet<String> locales) { |
| ImmutableSet.Builder<String> allLocales = ImmutableSet.builder(); |
| for (String locale : locales) { |
| allLocales.add(locale); |
| Matcher matcher = COUNTRY_LOCALE_PATTERN.matcher(locale); |
| if (matcher.matches()) { |
| allLocales.add(matcher.group(1)); |
| } |
| } |
| return allLocales.build(); |
| } |
| |
| @SuppressFieldNotInitialized |
| public static class Arg { |
| public SourcePath manifest; |
| // TODO(mbolin): Support for this field will be dropped as it is no longer used. Keeping it |
| // around is misleading. A deprecation warning should be printed if it is set. |
| public Optional<String> target; |
| public BuildTarget keystore; |
| public Optional<String> packageType; |
| @Hint(isDep = false) public Optional<Set<BuildTarget>> noDx; |
| public Optional<Boolean> useSplitDex; |
| public Optional<Boolean> useLinearAllocSplitDex; |
| public Optional<Boolean> minimizePrimaryDexSize; |
| public Optional<Boolean> disablePreDex; |
| // TODO(natthu): mark this as deprecated. |
| public Optional<Boolean> exopackage; |
| public Optional<Set<ExopackageMode>> exopackageModes; |
| public Optional<DexStore> dexCompression; |
| public Optional<ProGuardObfuscateStep.SdkProguardType> androidSdkProguardConfig; |
| public Optional<Boolean> useAndroidProguardConfigWithOptimizations; |
| public Optional<Integer> optimizationPasses; |
| public Optional<SourcePath> proguardConfig; |
| public Optional<String> resourceCompression; |
| public Optional<Boolean> skipCrunchPngs; |
| public Optional<List<String>> primaryDexPatterns; |
| public Optional<SourcePath> primaryDexClassesFile; |
| public Optional<SourcePath> primaryDexScenarioFile; |
| public Optional<Boolean> primaryDexScenarioOverflowAllowed; |
| public Optional<SourcePath> secondaryDexHeadClassesFile; |
| public Optional<SourcePath> secondaryDexTailClassesFile; |
| public Optional<Long> linearAllocHardLimit; |
| public Optional<List<String>> resourceFilter; |
| public Optional<ImmutableSet<String>> locales; |
| public Optional<Boolean> buildStringSourceMap; |
| public Optional<Set<TargetCpuType>> cpuFilters; |
| public Optional<ImmutableSortedSet<BuildTarget>> preprocessJavaClassesDeps; |
| public Optional<String> preprocessJavaClassesBash; |
| |
| /** This will never be absent after this Arg is populated. */ |
| public Optional<BuildConfigFields> buildConfigValues; |
| |
| public Optional<SourcePath> buildConfigValuesFile; |
| |
| public Optional<ImmutableSortedSet<BuildTarget>> deps; |
| } |
| } |