blob: e8c8b73b86b261a160495d448d4dcec763ffb49e [file] [log] [blame]
/*
* Copyright 2012-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.java;
import com.facebook.buck.android.HasAndroidResourceDeps;
import com.facebook.buck.android.UberRDotJavaUtil;
import com.facebook.buck.graph.TopologicalSort;
import com.facebook.buck.model.AnnotationProcessingData;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetPattern;
import com.facebook.buck.rules.AbstractCachingBuildRule;
import com.facebook.buck.rules.AbstractCachingBuildRuleBuilder;
import com.facebook.buck.rules.ArtifactCache;
import com.facebook.buck.rules.BuildContext;
import com.facebook.buck.rules.BuildDependencies;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleType;
import com.facebook.buck.rules.CachingBuildRuleParams;
import com.facebook.buck.rules.JavaPackageFinder;
import com.facebook.buck.rules.ResourcesAttributeBuilder;
import com.facebook.buck.rules.RuleKey;
import com.facebook.buck.rules.SrcsAttributeBuilder;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.facebook.buck.step.fs.MkdirAndSymlinkFileStep;
import com.facebook.buck.util.BuckConstant;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.reflect.ClassPath;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* Suppose this were a rule defined in <code>src/com/facebook/feed/BUILD</code>:
* <pre>
* java_library(
* name = 'feed',
* srcs = [
* 'FeedStoryRenderer.java',
* ],
* deps = [
* '//src/com/facebook/feed/model:model',
* ],
* )
* </pre>
* Then this would compile {@code FeedStoryRenderer.java} against Guava and the classes generated
* from the {@code //src/com/facebook/feed/model:model} rule.
*/
public class DefaultJavaLibraryRule extends AbstractCachingBuildRule
implements JavaLibraryRule, HasJavaSrcs, HasClasspathEntries {
private final ImmutableSortedSet<String> srcs;
private final ImmutableSortedSet<String> resources;
private final Optional<File> outputJar;
private final List<String> inputsToConsiderForCachingPurposes;
private final AnnotationProcessingParams annotationProcessingParams;
@Nullable private final String proguardConfig;
private final String sourceLevel;
private final String targetLevel;
private final boolean exportDeps;
private final Supplier<ImmutableSet<String>> outputClasspathEntriesSupplier;
private final Supplier<ImmutableSetMultimap<BuildRule, String>>
transitiveClasspathEntriesSupplier;
private final Supplier<ImmutableSetMultimap<BuildRule, String>>
declaredClasspathEntriesSupplier;
/**
* Function for opening a JAR and returning all symbols that can be referenced from inside of that
* jar.
*/
@VisibleForTesting
static interface JarResolver extends
Function<String, ImmutableSet<String>> {}
private final JarResolver JAR_RESOLVER =
new JarResolver() {
@Override
public ImmutableSet<String> apply(String classPath) {
ImmutableSet.Builder<String> topLevelSymbolsBuilder = ImmutableSet.builder();
try {
ClassLoader loader = URLClassLoader.newInstance(
new URL[]{new File(classPath).toURI().toURL()},
/* parent */ null);
// For every class contained in that jar, check to see if the package name
// (e.g. com.facebook.foo), the simple name (e.g. ImmutableSet) or the name
// (e.g com.google.common.collect.ImmutableSet) is one of the missing symbols.
for (ClassPath.ClassInfo classInfo : ClassPath.from(loader).getTopLevelClasses()) {
topLevelSymbolsBuilder.add(classInfo.getPackageName(),
classInfo.getSimpleName(),
classInfo.getName());
}
} catch (IOException e) {
// Since this simply is a heuristic, return an empty set if we fail to load a jar.
return topLevelSymbolsBuilder.build();
}
return topLevelSymbolsBuilder.build();
}
};
/**
* This is set in {@link #buildInternal(com.facebook.buck.rules.BuildContext)} and is available to subclasses.
*/
protected ImmutableList<HasAndroidResourceDeps> androidResourceDeps;
protected DefaultJavaLibraryRule(CachingBuildRuleParams cachingBuildRuleParams,
Set<String> srcs,
Set<String> resources,
@Nullable String proguardConfig,
AnnotationProcessingParams annotationProcessingParams,
boolean exportDeps) {
this(
cachingBuildRuleParams,
srcs,
resources,
proguardConfig,
annotationProcessingParams,
exportDeps,
JavacOptionsUtil.DEFAULT_SOURCE_LEVEL,
JavacOptionsUtil.DEFAULT_TARGET_LEVEL
);
}
protected DefaultJavaLibraryRule(CachingBuildRuleParams cachingBuildRuleParams,
Set<String> srcs,
Set<String> resources,
@Nullable String proguardConfig,
AnnotationProcessingParams annotationProcessingParams,
boolean exportDeps,
String sourceLevel,
String targetLevel) {
super(cachingBuildRuleParams);
this.srcs = ImmutableSortedSet.copyOf(srcs);
this.resources = ImmutableSortedSet.copyOf(resources);
this.annotationProcessingParams = Preconditions.checkNotNull(annotationProcessingParams);
this.proguardConfig = proguardConfig;
this.sourceLevel = sourceLevel;
this.targetLevel = targetLevel;
this.exportDeps = exportDeps;
if (!srcs.isEmpty() || !resources.isEmpty()) {
File file = new File(getOutputJarPath(getBuildTarget()));
this.outputJar = Optional.of(file);
} else {
this.outputJar = Optional.absent();
}
// Note that both srcs and resources are sorted so that the list order is consistent even if
// the iteration order of the sets passed to the constructor changes. See
// AbstractBuildRule.getInputsToCompareToOutput() for details.
inputsToConsiderForCachingPurposes = ImmutableList.<String>builder()
.addAll(this.srcs)
.addAll(this.resources)
.build();
outputClasspathEntriesSupplier =
Suppliers.memoize(new Supplier<ImmutableSet<String>>() {
@Override
public ImmutableSet<String> get() {
ImmutableSet<String> outputClasspathEntries;
// If this java_library exports its dependencies then just return the transitive
// dependencies.
if (DefaultJavaLibraryRule.this.exportDeps) {
outputClasspathEntries = ImmutableSet.copyOf(
getTransitiveClasspathEntries().values());
} else if (outputJar.isPresent()) {
outputClasspathEntries = ImmutableSet.of(getOutput().getPath());
} else {
outputClasspathEntries = ImmutableSet.of();
}
return outputClasspathEntries;
}
});
transitiveClasspathEntriesSupplier =
Suppliers.memoize(new Supplier<ImmutableSetMultimap<BuildRule, String>>() {
@Override
public ImmutableSetMultimap<BuildRule, String> get() {
final ImmutableSetMultimap.Builder<BuildRule, String> classpathEntries =
ImmutableSetMultimap.builder();
ImmutableSetMultimap<BuildRule, String> classpathEntriesForDeps =
Classpaths.getClasspathEntries(getDeps());
classpathEntries.putAll(classpathEntriesForDeps);
if (DefaultJavaLibraryRule.this.exportDeps) {
classpathEntries.putAll(DefaultJavaLibraryRule.this,
classpathEntriesForDeps.values());
}
// Only add ourselves to the classpath if there's a jar to be built.
if (outputJar.isPresent()) {
classpathEntries.putAll(DefaultJavaLibraryRule.this,
getOutput().getPath());
}
return classpathEntries.build();
}
});
declaredClasspathEntriesSupplier =
Suppliers.memoize(new Supplier<ImmutableSetMultimap<BuildRule, String>>() {
@Override
public ImmutableSetMultimap<BuildRule, String> get() {
final ImmutableSetMultimap.Builder<BuildRule, String> classpathEntries =
ImmutableSetMultimap.builder();
Iterable<JavaLibraryRule> javaLibraryDeps = Iterables.filter(
Sets.union(getDeps(), ImmutableSet.of(DefaultJavaLibraryRule.this)),
JavaLibraryRule.class);
for (JavaLibraryRule rule : javaLibraryDeps) {
classpathEntries.putAll(rule, rule.getOutputClasspathEntries());
}
return classpathEntries.build();
}
});
}
/**
* @param outputDirectory Directory to write class files to
* @param javaSourceFilePaths .java files to compile: may be empty
* @param transitiveClasspathEntries Classpaths of all transitive dependencies.
* @param declaredClasspathEntries Classpaths of all declared dependencies.
* @param annotationProcessingData to process JSR269 java annotations
* @param suggestBuildRules Function to convert from missing symbols to the suggested rules.
* @return commands to compile the specified inputs
*/
private static ImmutableList<Step> createCommandsForJavac(
String outputDirectory,
final SortedSet<String> javaSourceFilePaths,
ImmutableSet<String> transitiveClasspathEntries,
ImmutableSet<String> declaredClasspathEntries,
Supplier<String> bootclasspathSupplier,
AnnotationProcessingData annotationProcessingData,
Optional<String> invokingRule,
BuildDependencies buildDependencies,
Optional<DependencyCheckingJavacStep.SuggestBuildRules> suggestBuildRules,
String sourceLevel,
String targetLevel) {
ImmutableList.Builder<Step> commands = ImmutableList.builder();
// Only run javac if there are .java files to compile.
if (!javaSourceFilePaths.isEmpty()) {
Step javac = new DependencyCheckingJavacStep(
outputDirectory,
javaSourceFilePaths,
transitiveClasspathEntries,
declaredClasspathEntries,
bootclasspathSupplier,
annotationProcessingData,
invokingRule,
buildDependencies,
suggestBuildRules,
sourceLevel,
targetLevel);
commands.add(javac);
}
return commands.build();
}
private static String getOutputJarPath(BuildTarget target) {
return String.format(
"%s/%slib__%s__output/%s.jar",
BuckConstant.GEN_DIR,
target.getBasePathWithSlash(),
target.getShortName(),
target.getShortName());
}
/**
* @return directory path relative to the project root where .class files will be generated.
* The return value does not end with a slash.
*/
private static String getClassesDir(BuildTarget target) {
return String.format(
"%s/%slib__%s__classes",
BuckConstant.BIN_DIR,
target.getBasePathWithSlash(),
target.getShortName());
}
@Override
public boolean isAndroidRule() {
return false;
}
@Override
public boolean isLibrary() {
return true;
}
@Override
protected RuleKey.Builder ruleKeyBuilder() {
return super.ruleKeyBuilder()
.set("srcs", srcs)
.set("resources", resources)
.set("classpathEntries", ImmutableSortedSet.copyOf(getDeclaredClasspathEntries().values()))
.set("isAndroidLibrary", isAndroidRule())
.set("sourceLevel", sourceLevel)
.set("targetLevel", targetLevel)
.set("exportDeps", exportDeps);
}
@Override
public BuildRuleType getType() {
return BuildRuleType.JAVA_LIBRARY;
}
@Override
public ImmutableSortedSet<String> getJavaSrcs() {
return srcs;
}
@Override
public ImmutableSetMultimap<BuildRule, String> getTransitiveClasspathEntries() {
return transitiveClasspathEntriesSupplier.get();
}
@Override
public ImmutableSetMultimap<BuildRule, String> getDeclaredClasspathEntries() {
return declaredClasspathEntriesSupplier.get();
}
@Override
public ImmutableSet<String> getOutputClasspathEntries() {
return outputClasspathEntriesSupplier.get();
}
@Override
public AnnotationProcessingData getAnnotationProcessingData() {
return annotationProcessingParams;
}
@Nullable
public String getProguardConfig() {
return proguardConfig;
}
@Override
@Nullable
protected List<String> getInputsToCompareToOutput(BuildContext context) {
return inputsToConsiderForCachingPurposes;
}
@Override
public boolean getExportDeps() {
return exportDeps;
}
/**
* Checks to see if all of the dependant rules are cached. By default, AbstractCachingBuildRule
* will consider a rule's deps uncached if any of its descendants were uncached.
*/
@VisibleForTesting
@Override
protected boolean depsCached(final BuildContext context, Logger logger) throws IOException {
if (context.getBuildDependencies() != BuildDependencies.FIRST_ORDER_ONLY) {
return super.depsCached(context, logger);
}
for (BuildRule dep : getDeps()) {
if (dep instanceof DefaultJavaLibraryRule) {
DefaultJavaLibraryRule javaDep = (DefaultJavaLibraryRule)dep;
if (javaDep.getExportDeps()) {
if (javaDep.hasUncachedDescendants(context)) {
logger.info(String.format("%s not cached because java library %s exports its deps " +
"and has uncached descendants",
this,
dep.getFullyQualifiedName()));
return false;
}
} else if (!javaDep.ruleInputsCached(context, logger)) {
logger.info(String.format("%s not cached because java library %s's inputs changed",
this,
dep.getFullyQualifiedName()));
return false;
}
} else if (!dep.isCached(context)) {
logger.info(String.format("%s not cached because %s is not cached",
this,
dep.getFullyQualifiedName()));
return false;
}
}
return true;
}
/**
* Building a java_library() rule entails compiling the .java files specified in the srcs
* attribute. They are compiled into a directory under {@link BuckConstant#BIN_DIR}.
*/
@Override
protected final List<Step> buildInternal(BuildContext context) throws IOException {
ImmutableList.Builder<Step> commands = ImmutableList.builder();
BuildTarget buildTarget = getBuildTarget();
// If this rule depends on AndroidResourceRules, then we need to generate the R.java files that
// this rule needs in order to be able to compile itself.
androidResourceDeps = UberRDotJavaUtil.getAndroidResourceDeps(this,
context.getDependencyGraph());
boolean dependsOnAndroidResourceRules = !androidResourceDeps.isEmpty();
if (dependsOnAndroidResourceRules) {
UberRDotJavaUtil.createDummyRDotJavaFiles(androidResourceDeps, buildTarget, commands);
}
ImmutableSetMultimap<BuildRule, String> transitiveClasspathEntries =
getTransitiveClasspathEntries();
ImmutableSetMultimap<BuildRule, String> declaredClasspathEntries =
getDeclaredClasspathEntries();
// If this rule depends on AndroidResourceRules, then we need to include the compiled R.java
// files on the classpath when compiling this rule.
if (dependsOnAndroidResourceRules) {
ImmutableSetMultimap.Builder<BuildRule, String> transitiveClasspathEntriesWithRDotJava =
ImmutableSetMultimap.builder();
transitiveClasspathEntriesWithRDotJava.putAll(transitiveClasspathEntries);
ImmutableSetMultimap.Builder<BuildRule, String> declaredClasspathEntriesWithRDotJava =
ImmutableSetMultimap.builder();
declaredClasspathEntriesWithRDotJava.putAll(declaredClasspathEntries);
ImmutableSet<String> rDotJavaClasspath =
ImmutableSet.of(UberRDotJavaUtil.getRDotJavaBinFolder(buildTarget));
transitiveClasspathEntriesWithRDotJava.putAll(this, rDotJavaClasspath);
declaredClasspathEntriesWithRDotJava.putAll(this, rDotJavaClasspath);
declaredClasspathEntries = declaredClasspathEntriesWithRDotJava.build();
transitiveClasspathEntries = transitiveClasspathEntriesWithRDotJava.build();
}
// Only override the bootclasspath if this rule is supposed to compile Android code.
Supplier<String> bootclasspathSupplier;
if (isAndroidRule()) {
bootclasspathSupplier = context.getAndroidBootclasspathSupplier();
} else {
bootclasspathSupplier = Suppliers.ofInstance(null);
}
// Javac requires that the root directory for generated sources already exist.
String annotationGenFolder = annotationProcessingParams.getGeneratedSourceFolderName();
if (annotationGenFolder != null) {
MakeCleanDirectoryStep mkdirGeneratedSources =
new MakeCleanDirectoryStep(annotationGenFolder);
commands.add(mkdirGeneratedSources);
}
// Always create the output directory, even if there are no .java files to compile because there
// might be resources that need to be copied there.
String outputDirectory = getClassesDir(getBuildTarget());
commands.add(new MakeCleanDirectoryStep(outputDirectory));
Optional<DependencyCheckingJavacStep.SuggestBuildRules> suggestBuildRule =
createSuggestBuildFunction(context,
transitiveClasspathEntries,
declaredClasspathEntries,
JAR_RESOLVER);
// This adds the javac command, along with any supporting commands.
List<Step> javac = createCommandsForJavac(
outputDirectory,
srcs,
ImmutableSet.copyOf(transitiveClasspathEntries.values()),
ImmutableSet.copyOf(declaredClasspathEntries.values()),
bootclasspathSupplier,
annotationProcessingParams,
Optional.of(getFullyQualifiedName()),
context.getBuildDependencies(),
suggestBuildRule,
sourceLevel,
targetLevel);
commands.addAll(javac);
// If there are resources, then link them to the appropriate place in the classes directory.
addResourceCommands(commands, outputDirectory, context.getJavaPackageFinder());
if (outputJar.isPresent()) {
commands.add(new MakeCleanDirectoryStep(outputJar.get().getParent()));
commands.add(new JarDirectoryStep(
outputJar.get().getPath(), Collections.singleton(outputDirectory), null, null));
}
return commands.build();
}
/**
* @param transitiveNotDeclaredDep A {@link BuildRule} that is contained in the transitive
* dependency list but is not declared as a dependency.
* @param failedImports A Set of remaining failed imports. This function will mutate this set
* and remove any imports satisfied by {@code transitiveNotDeclaredDep}.
* @return whether or not adding {@code transitiveNotDeclaredDep} as a dependency to this build
* rule would have satisfied one of the {@code failedImports}.
*/
private boolean isMissingBuildRule(BuildRule transitiveNotDeclaredDep,
Set<String> failedImports,
JarResolver jarResolver) {
ImmutableSet<String> classPaths = getTransitiveClasspathEntries().get(transitiveNotDeclaredDep);
boolean containsMissingBuildRule = false;
// Open the output jar for every jar contained as the output of transitiveNotDeclaredDep. With
// the exception of rules that export their dependencies, this will result in a single
// classpath.
for (String classPath : classPaths) {
ImmutableSet<String> topLevelSymbols;
topLevelSymbols = jarResolver.apply(classPath);
for (String symbolName : topLevelSymbols) {
if (failedImports.contains(symbolName)) {
failedImports.remove(symbolName);
containsMissingBuildRule = true;
// If we've found all of the missing imports, bail out early.
if (failedImports.isEmpty()) {
return true;
}
}
}
}
return containsMissingBuildRule;
}
/**
* @return A function that takes a list of failed imports from a javac invocation and returns a
* set of rules to suggest that the developer import to satisfy those imports.
*/
@VisibleForTesting
Optional<DependencyCheckingJavacStep.SuggestBuildRules> createSuggestBuildFunction(
BuildContext context,
ImmutableSetMultimap<BuildRule, String> transitiveClasspathEntries,
ImmutableSetMultimap<BuildRule, String> declaredClasspathEntries,
final JarResolver jarResolver) {
if (context.getBuildDependencies() != BuildDependencies.WARN_ON_TRANSITIVE) {
return Optional.absent();
}
final Set<BuildRule> transitiveNotDeclaredDeps = Sets.difference(
transitiveClasspathEntries.keySet(),
declaredClasspathEntries.keySet());
final ImmutableList<BuildRule> sortedTransitiveNotDeclaredDeps = ImmutableList.copyOf(
TopologicalSort.sort(context.getDependencyGraph(),
new Predicate<BuildRule>() {
@Override
public boolean apply(BuildRule input) {
return transitiveNotDeclaredDeps.contains(input);
}
})).reverse();
DependencyCheckingJavacStep.SuggestBuildRules suggestBuildRuleFn =
new DependencyCheckingJavacStep.SuggestBuildRules() {
@Override
public ImmutableSet<String> apply(ImmutableSet<String> failedImports) {
ImmutableSet.Builder<String> suggestedDeps = ImmutableSet.builder();
Set<String> remainingImports = Sets.newHashSet(failedImports);
for (BuildRule transitiveNotDeclaredDep : sortedTransitiveNotDeclaredDeps) {
boolean ruleCanSeeDep = transitiveNotDeclaredDep.isVisibleTo(
DefaultJavaLibraryRule.this.getBuildTarget());
if (ruleCanSeeDep &&
isMissingBuildRule(transitiveNotDeclaredDep, remainingImports, jarResolver)) {
suggestedDeps.add(transitiveNotDeclaredDep.getFullyQualifiedName());
}
// If we've wiped out all remaining imports, break the loop looking for them.
if (remainingImports.isEmpty()) {
break;
}
}
return suggestedDeps.build();
}
};
return Optional.of(suggestBuildRuleFn);
}
@VisibleForTesting
void addResourceCommands(ImmutableList.Builder<Step> commands,
String outputDirectory,
JavaPackageFinder javaPackageFinder) {
if (!resources.isEmpty()) {
for (String resource : resources) {
// If the path to the file defining this rule were:
// "first-party/orca/lib-http/tests/com/facebook/orca/BUILD"
//
// And the value of resource were:
// "first-party/orca/lib-http/tests/com/facebook/orca/protocol/base/batch_exception1.txt"
//
// Then javaPackageAsPath would be:
// "com/facebook/orca/protocol/base/"
//
// And the path that we would want to copy to the classes directory would be:
// "com/facebook/orca/protocol/base/batch_exception1.txt"
//
// Therefore, some path-wrangling is required to produce the correct string.
String javaPackageAsPath = javaPackageFinder.findJavaPackageFolderForPath(resource);
String relativeSymlinkPath;
if ("".equals(javaPackageAsPath)) {
// In this case, the project root is acting as the default package, so the resource path
// works fine.
relativeSymlinkPath = resource;
} else {
int lastIndex = resource.lastIndexOf(javaPackageAsPath);
Preconditions.checkState(lastIndex >= 0,
"Resource path %s must contain %s",
resource,
javaPackageAsPath);
relativeSymlinkPath = resource.substring(lastIndex);
}
String target = outputDirectory + '/' + relativeSymlinkPath;
MkdirAndSymlinkFileStep link = new MkdirAndSymlinkFileStep(resource, target);
commands.add(link);
}
}
}
@Override
public File getOutput() {
return outputJar.orNull();
}
public static Builder newJavaLibraryRuleBuilder() {
return new Builder();
}
public static class Builder extends AbstractCachingBuildRuleBuilder implements
SrcsAttributeBuilder, ResourcesAttributeBuilder {
protected Set<String> srcs = Sets.newHashSet();
protected Set<String> resources = Sets.newHashSet();
protected final AnnotationProcessingParams.Builder annotationProcessingBuilder =
new AnnotationProcessingParams.Builder();
protected String sourceLevel = JavacOptionsUtil.DEFAULT_SOURCE_LEVEL;
protected String targetLevel = JavacOptionsUtil.DEFAULT_TARGET_LEVEL;
protected boolean exportDeps = false;
@Nullable
protected String proguardConfig = null;
protected Builder() {}
@Override
public DefaultJavaLibraryRule build(Map<String, BuildRule> buildRuleIndex) {
CachingBuildRuleParams cachingBuildRuleParams = createCachingBuildRuleParams(buildRuleIndex);
AnnotationProcessingParams processingParams =
annotationProcessingBuilder.build(buildRuleIndex);
return new DefaultJavaLibraryRule(
cachingBuildRuleParams,
srcs,
resources,
proguardConfig,
processingParams,
exportDeps,
sourceLevel,
targetLevel);
}
public AnnotationProcessingParams.Builder getAnnotationProcessingBuilder() {
return annotationProcessingBuilder;
}
@Override
public Builder setBuildTarget(BuildTarget buildTarget) {
super.setBuildTarget(buildTarget);
annotationProcessingBuilder.setOwnerTarget(buildTarget);
return this;
}
@Override
public Builder addDep(String dep) {
super.addDep(dep);
return this;
}
@Override
public Builder addSrc(String src) {
srcs.add(src);
return this;
}
@Override
public Builder addResource(String relativePathToResource) {
resources.add(relativePathToResource);
return this;
}
@Override
public Builder addVisibilityPattern(BuildTargetPattern visibilityPattern) {
super.addVisibilityPattern(visibilityPattern);
return this;
}
@Override
public Builder setArtifactCache(ArtifactCache artifactCache) {
super.setArtifactCache(artifactCache);
return this;
}
public Builder setProguardConfig(String proguardConfig) {
this.proguardConfig = proguardConfig;
return this;
}
public Builder setSourceLevel(String sourceLevel) {
this.sourceLevel = sourceLevel;
return this;
}
public Builder setTargetLevel(String targetLevel) {
this.targetLevel = targetLevel;
return this;
}
public Builder setExportDeps(boolean exportDeps) {
this.exportDeps = exportDeps;
return this;
}
}
}