/* * 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.jvm.java.CalculateAbiFromClasses; import com.facebook.buck.jvm.java.HasJavaAbi; import com.facebook.buck.jvm.java.JavaBuckConfig; import com.facebook.buck.jvm.java.JavaLibraryRules; import com.facebook.buck.jvm.java.Javac; import com.facebook.buck.jvm.java.JavacFactory; import com.facebook.buck.jvm.java.JavacOptions; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.Flavor; import com.facebook.buck.model.InternalFlavor; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.CellPathResolver; import com.facebook.buck.rules.CommonDescriptionArg; import com.facebook.buck.rules.Description; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.coercer.BuildConfigFields; import com.facebook.buck.util.OptionalCompat; import com.facebook.buck.util.immutables.BuckStyleImmutable; import com.google.common.base.Preconditions; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableSortedSet; import java.util.Optional; import org.immutables.value.Value; public class AndroidBuildConfigDescription implements Description<AndroidBuildConfigDescriptionArg> { private static final Flavor GEN_JAVA_FLAVOR = InternalFlavor.of("gen_java_android_build_config"); private final JavaBuckConfig javaBuckConfig; private final JavacOptions androidJavacOptions; public AndroidBuildConfigDescription( JavaBuckConfig javaBuckConfig, JavacOptions androidJavacOptions) { this.javaBuckConfig = javaBuckConfig; this.androidJavacOptions = androidJavacOptions; } @Override public Class<AndroidBuildConfigDescriptionArg> getConstructorArgType() { return AndroidBuildConfigDescriptionArg.class; } @Override public BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, CellPathResolver cellRoots, AndroidBuildConfigDescriptionArg args) throws NoSuchBuildTargetException { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); if (HasJavaAbi.isClassAbiTarget(params.getBuildTarget())) { BuildTarget configTarget = HasJavaAbi.getLibraryTarget(params.getBuildTarget()); BuildRule configRule = resolver.requireRule(configTarget); return CalculateAbiFromClasses.of( params.getBuildTarget(), ruleFinder, params, Preconditions.checkNotNull(configRule.getSourcePathToOutput())); } return createBuildRule( params, args.getPackage(), args.getValues(), args.getValuesFile(), /* useConstantExpressions */ false, JavacFactory.create(ruleFinder, javaBuckConfig, null), androidJavacOptions, resolver); } /** * @param values Collection whose entries identify fields for the generated {@code BuildConfig} * class. The values for fields can be overridden by values from the {@code valuesFile} file, * if present. * @param valuesFile Path to a file with values to override those in {@code values}. * @param ruleResolver Any intermediate rules introduced by this method will be added to this * {@link BuildRuleResolver}. */ static AndroidBuildConfigJavaLibrary createBuildRule( BuildRuleParams params, String javaPackage, BuildConfigFields values, Optional<SourcePath> valuesFile, boolean useConstantExpressions, Javac javac, JavacOptions javacOptions, BuildRuleResolver ruleResolver) throws NoSuchBuildTargetException { // Normally, the build target for an intermediate rule is a flavored version of the target for // the original rule. For example, if the build target for an android_build_config() were // //foo:bar, then the build target for the intermediate AndroidBuildConfig rule created by this // method would be //foo:bar#gen_java_android_build_config. // // However, in the case of an android_binary() with multiple android_build_config() rules in its // transitive deps, it must create one intermediate AndroidBuildConfigJavaLibrary for each // android_build_config() dependency. The primary difference is that in each of the new versions // of the AndroidBuildConfigJavaLibrary, constant expressions will be used so the values can be // inlined (whereas non-constant-expressions were used in the original versions). Because there // are multiple intermediate rules based on the same android_binary(), the flavor cannot just be // #gen_java_android_build_config because that would lead to build target collisions, so the // flavor must be parameterized by the java package to ensure it is unique. // // This fixes the issue, but deviates from the common pattern where a build rule has at most // one flavored version of itself for a given flavor. SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildTarget buildConfigBuildTarget; if (!params.getBuildTarget().isFlavored()) { // android_build_config() case. Preconditions.checkArgument(!useConstantExpressions); buildConfigBuildTarget = params.getBuildTarget().withFlavors(GEN_JAVA_FLAVOR); } else { // android_binary() graph enhancement case. Preconditions.checkArgument(useConstantExpressions); buildConfigBuildTarget = params .getBuildTarget() .withFlavors( InternalFlavor.of( GEN_JAVA_FLAVOR.getName() + '_' + javaPackage.replace('.', '_'))); } // Create one build rule to generate BuildConfig.java. BuildRuleParams buildConfigParams = params .withBuildTarget(buildConfigBuildTarget) .copyAppendingExtraDeps( ruleFinder.filterBuildRuleInputs(OptionalCompat.asSet(valuesFile))); AndroidBuildConfig androidBuildConfig = new AndroidBuildConfig( buildConfigParams, javaPackage, values, valuesFile, useConstantExpressions); ruleResolver.addToIndex(androidBuildConfig); // Create a second build rule to compile BuildConfig.java and expose it as a JavaLibrary. BuildRuleParams javaLibraryParams = params.copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(ImmutableSortedSet.of(androidBuildConfig)), Suppliers.ofInstance(ImmutableSortedSet.of())); return new AndroidBuildConfigJavaLibrary( javaLibraryParams, pathResolver, ruleFinder, javac, javacOptions, JavaLibraryRules.getAbiSourcePaths(ruleResolver, javaLibraryParams.getBuildDeps()), androidBuildConfig); } @BuckStyleImmutable @Value.Immutable interface AbstractAndroidBuildConfigDescriptionArg extends CommonDescriptionArg { /** For R.java */ String getPackage(); @Value.Default default BuildConfigFields getValues() { return BuildConfigFields.empty(); } /** If present, contents of file can override those of {@link #getValues}. */ Optional<SourcePath> getValuesFile(); } }