blob: a4939f65f686155c8b5ebe801598061827e540d9 [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.command;
import static com.facebook.buck.rules.BuildableProperties.Kind.ANDROID;
import com.facebook.buck.android.HasAndroidPlatformTarget;
import com.facebook.buck.event.BuckEventBus;
import com.facebook.buck.event.LogEvent;
import com.facebook.buck.graph.AbstractBottomUpTraversal;
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.BuildRuleSuccess;
import com.facebook.buck.rules.Builder;
import com.facebook.buck.rules.DependencyGraph;
import com.facebook.buck.rules.JavaPackageFinder;
import com.facebook.buck.step.DefaultStepRunner;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.StepFailedException;
import com.facebook.buck.step.StepRunner;
import com.facebook.buck.step.TargetDevice;
import com.facebook.buck.util.AndroidPlatformTarget;
import com.facebook.buck.util.Console;
import com.facebook.buck.util.ProjectFilesystem;
import com.facebook.buck.util.environment.Platform;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
public class Build {
private final DependencyGraph dependencyGraph;
private final ExecutionContext executionContext;
private final ArtifactCache artifactCache;
private final StepRunner stepRunner;
private final JavaPackageFinder javaPackageFinder;
private final BuildDependencies buildDependencies;
/** Not set until {@link #executeBuild(Set)} is invoked. */
@Nullable
private BuildContext buildContext;
/**
* @param androidSdkDir where the user's Android SDK is installed.
* @param buildDependencies How to include dependencies when building rules.
*/
public Build(
DependencyGraph dependencyGraph,
Optional<File> androidSdkDir,
Optional<File> ndkRoot,
Optional<TargetDevice> targetDevice,
ProjectFilesystem projectFilesystem,
ArtifactCache artifactCache,
ListeningExecutorService listeningExecutorService,
JavaPackageFinder javaPackageFinder,
Console console,
long defaultTestTimeoutMillis,
boolean isCodeCoverageEnabled,
boolean isDebugEnabled,
BuildDependencies buildDependencies,
BuckEventBus eventBus,
Platform platform) {
this.dependencyGraph = Preconditions.checkNotNull(dependencyGraph);
Optional<AndroidPlatformTarget> androidPlatformTarget = findAndroidPlatformTarget(
dependencyGraph, androidSdkDir, eventBus);
this.executionContext = ExecutionContext.builder()
.setProjectFilesystem(projectFilesystem)
.setConsole(console)
.setAndroidPlatformTarget(androidPlatformTarget)
.setNdkRoot(ndkRoot)
.setTargetDevice(targetDevice)
.setDefaultTestTimeoutMillis(defaultTestTimeoutMillis)
.setCodeCoverageEnabled(isCodeCoverageEnabled)
.setDebugEnabled(isDebugEnabled)
.setEventBus(eventBus)
.setPlatform(platform)
.build();
this.artifactCache = Preconditions.checkNotNull(artifactCache);
this.stepRunner = new DefaultStepRunner(executionContext, listeningExecutorService);
this.javaPackageFinder = Preconditions.checkNotNull(javaPackageFinder);
this.buildDependencies = Preconditions.checkNotNull(buildDependencies);
}
public DependencyGraph getDependencyGraph() {
return dependencyGraph;
}
public ExecutionContext getExecutionContext() {
return executionContext;
}
public StepRunner getStepRunner() {
return stepRunner;
}
/** Returns null until {@link #executeBuild(Set)} is invoked. */
@Nullable
public BuildContext getBuildContext() {
return buildContext;
}
public static Optional<AndroidPlatformTarget> findAndroidPlatformTarget(
DependencyGraph dependencyGraph, Optional<File> androidSdkDirOption, BuckEventBus eventBus) {
if (androidSdkDirOption.isPresent()) {
File androidSdkDir = androidSdkDirOption.get();
return findAndroidPlatformTarget(dependencyGraph, androidSdkDir, eventBus);
} else {
// If the Android SDK has not been specified, then no AndroidPlatformTarget can be found.
return Optional.<AndroidPlatformTarget>absent();
}
}
private static Optional<AndroidPlatformTarget> findAndroidPlatformTarget(
final DependencyGraph dependencyGraph, final File androidSdkDir, final BuckEventBus eventBus) {
// Traverse the dependency graph to determine androidPlatformTarget.
AbstractBottomUpTraversal<BuildRule, Optional<AndroidPlatformTarget>> traversal =
new AbstractBottomUpTraversal<BuildRule, Optional<AndroidPlatformTarget>>(dependencyGraph) {
private String androidPlatformTargetId = null;
private boolean isEncounteredAndroidRuleInTraversal = false;
@Override
public void visit(BuildRule rule) {
if (rule.getProperties().is(ANDROID)) {
isEncounteredAndroidRuleInTraversal = true;
}
if (rule instanceof HasAndroidPlatformTarget) {
String target = ((HasAndroidPlatformTarget)rule).getAndroidPlatformTarget();
if (androidPlatformTargetId == null) {
androidPlatformTargetId = target;
} else if (!target.equals(androidPlatformTargetId)) {
throw new RuntimeException(
String.format("More than one android platform targeted: %s and %s",
target,
androidPlatformTargetId));
}
}
}
@Override
public Optional<AndroidPlatformTarget> getResult() {
// Find an appropriate AndroidPlatformTarget for the target attribute specified in one of the
// transitively included android_binary() build rules. If no such target has been specified,
// then use a default AndroidPlatformTarget so that it is possible to build non-Android Java
// code, as well.
Optional<AndroidPlatformTarget> result;
if (androidPlatformTargetId != null) {
Optional<AndroidPlatformTarget> target = AndroidPlatformTarget.getTargetForId(
androidPlatformTargetId, androidSdkDir);
if (target.isPresent()) {
result = target;
} else {
throw new RuntimeException("No target found with id: " + androidPlatformTargetId);
}
} else if (isEncounteredAndroidRuleInTraversal) {
AndroidPlatformTarget androidPlatformTarget = AndroidPlatformTarget
.getDefaultPlatformTarget(androidSdkDir);
eventBus.post(LogEvent.warning("No Android platform target specified. Using default: %s",
androidPlatformTarget.getName()));
result = Optional.of(androidPlatformTarget);
} else {
result = Optional.absent();
}
return result;
}
};
traversal.traverse();
return traversal.getResult();
}
public ListenableFuture<List<BuildRuleSuccess>> executeBuild(Set<BuildRule> rulesToBuild)
throws IOException, StepFailedException {
buildContext = BuildContext.builder()
.setDependencyGraph(dependencyGraph)
.setStepRunner(stepRunner)
.setProjectFilesystem(executionContext.getProjectFilesystem())
.setArtifactCache(artifactCache)
.setJavaPackageFinder(javaPackageFinder)
.setEventBus(executionContext.getBuckEventBus())
.setAndroidBootclasspathForAndroidPlatformTarget(
executionContext.getAndroidPlatformTargetOptional())
.setBuildDependencies(buildDependencies)
.setConsole(executionContext.getConsole())
.build();
return Builder.getInstance().buildRules(rulesToBuild, buildContext);
}
}