| /* |
| * 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.java.JavacOptions; |
| import com.facebook.buck.model.BuildTarget; |
| import com.facebook.buck.model.Flavor; |
| import com.facebook.buck.model.ImmutableFlavor; |
| 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.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.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.ImmutableSortedSet; |
| |
| public class AndroidBuildConfigDescription |
| implements Description<AndroidBuildConfigDescription.Arg> { |
| |
| public static final BuildRuleType TYPE = ImmutableBuildRuleType.of("android_build_config"); |
| |
| private static final BuildRuleType GEN_JAVA_TYPE = ImmutableBuildRuleType.of( |
| "gen_java_android_build_config"); |
| private static final Flavor GEN_JAVA_FLAVOR = ImmutableFlavor.of(GEN_JAVA_TYPE.getName()); |
| private final JavacOptions androidJavacOptions; |
| |
| public AndroidBuildConfigDescription(JavacOptions androidJavacOptions) { |
| this.androidJavacOptions = androidJavacOptions; |
| } |
| |
| @Override |
| public BuildRuleType getBuildRuleType() { |
| return TYPE; |
| } |
| |
| @Override |
| public Arg createUnpopulatedConstructorArg() { |
| return new Arg(); |
| } |
| |
| @Override |
| public <A extends Arg> BuildRule createBuildRule( |
| BuildRuleParams params, |
| BuildRuleResolver resolver, |
| A args) { |
| return createBuildRule( |
| params, |
| args.javaPackage, |
| args.values.get(), |
| args.valuesFile, |
| /* useConstantExpressions */ false, |
| 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, |
| JavacOptions javacOptions, |
| BuildRuleResolver ruleResolver) { |
| // 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. |
| SourcePathResolver pathResolver = new SourcePathResolver(ruleResolver); |
| BuildTarget buildConfigBuildTarget; |
| if (!params.getBuildTarget().isFlavored()) { |
| // android_build_config() case. |
| Preconditions.checkArgument(!useConstantExpressions); |
| buildConfigBuildTarget = |
| BuildTarget.builder(params.getBuildTarget().getUnflavoredBuildTarget()) |
| .addFlavors(GEN_JAVA_FLAVOR) |
| .build(); |
| } else { |
| // android_binary() graph enhancement case. |
| Preconditions.checkArgument(useConstantExpressions); |
| buildConfigBuildTarget = |
| BuildTarget.builder(params.getBuildTarget().getUnflavoredBuildTarget()) |
| .addFlavors( |
| ImmutableFlavor.of(GEN_JAVA_FLAVOR.getName() + '_' + javaPackage.replace('.', '_'))) |
| .build(); |
| } |
| |
| // Create one build rule to generate BuildConfig.java. |
| BuildRuleParams buildConfigParams = params.copyWithChanges( |
| GEN_JAVA_TYPE, |
| buildConfigBuildTarget, |
| Suppliers.ofInstance(params.getDeclaredDeps()), |
| /* extraDeps */ Suppliers.ofInstance( |
| ImmutableSortedSet.<BuildRule>naturalOrder() |
| .addAll(params.getExtraDeps()) |
| .addAll(pathResolver.filterBuildRuleInputs(valuesFile.asSet())) |
| .build())); |
| AndroidBuildConfig androidBuildConfig = new AndroidBuildConfig( |
| buildConfigParams, |
| pathResolver, |
| 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.copyWithChanges( |
| TYPE, |
| params.getBuildTarget(), |
| /* declaredDeps */ Suppliers.ofInstance( |
| ImmutableSortedSet.<BuildRule>of(androidBuildConfig)), |
| /* extraDeps */ Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())); |
| return new AndroidBuildConfigJavaLibrary( |
| javaLibraryParams, |
| pathResolver, |
| javacOptions, |
| androidBuildConfig); |
| } |
| |
| @SuppressFieldNotInitialized |
| public static class Arg { |
| @Hint(name = "package") |
| public String javaPackage; |
| |
| /** This will never be absent after this Arg is populated. */ |
| public Optional<BuildConfigFields> values; |
| |
| /** If present, contents of file can override those of {@link #values}. */ |
| public Optional<SourcePath> valuesFile; |
| } |
| } |