blob: 41e8bc569ba176b8a4f947ce4cff60189604a79a [file] [log] [blame]
/*
* 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;
}
}