blob: 468e5c19bf7a0ca47d30f9b72726f3cad53ce7e3 [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.step;
import com.facebook.buck.android.AndroidPlatformTarget;
import com.facebook.buck.android.NoAndroidSdkException;
import com.facebook.buck.event.BuckEvent;
import com.facebook.buck.event.BuckEventBus;
import com.facebook.buck.event.ThrowableConsoleEvent;
import com.facebook.buck.io.MorePaths;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.java.JavaPackageFinder;
import com.facebook.buck.model.BuildId;
import com.facebook.buck.util.Ansi;
import com.facebook.buck.util.ClassLoaderCache;
import com.facebook.buck.util.Console;
import com.facebook.buck.util.ProcessExecutor;
import com.facebook.buck.util.Verbosity;
import com.facebook.buck.util.environment.Platform;
import com.facebook.buck.util.immutables.BuckStyleImmutable;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.immutables.value.Value;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.annotation.Nullable;
@Value.Immutable(builder = false)
@BuckStyleImmutable
public abstract class ExecutionContext implements Closeable {
@Value.Parameter
public abstract ProjectFilesystem getProjectFilesystem();
@Value.Parameter
public abstract Console getConsole();
/**
* Returns an {@link AndroidPlatformTarget} if the user specified one via {@code local.properties}
* or some other mechanism. If the user failed to specify one, an exception will be thrown.
*/
@Value.Parameter
public abstract Supplier<AndroidPlatformTarget> getAndroidPlatformTargetSupplier();
@Value.Parameter
public abstract Optional<TargetDevice> getTargetDeviceOptional();
@Value.Parameter
public abstract long getDefaultTestTimeoutMillis();
@Value.Parameter
public abstract boolean isCodeCoverageEnabled();
@Value.Parameter
public abstract boolean isDebugEnabled();
@Value.Parameter
public abstract ProcessExecutor getProcessExecutor();
@Value.Parameter
public abstract BuckEventBus getBuckEventBus();
@Value.Parameter
public abstract Platform getPlatform();
@Value.Parameter
public abstract ImmutableMap<String, String> getEnvironment();
@Value.Parameter
public abstract JavaPackageFinder getJavaPackageFinder();
@Value.Parameter
public abstract ObjectMapper getObjectMapper();
@Value.Parameter
public abstract ClassLoaderCache getClassLoaderCache();
@Value.Derived
public Verbosity getVerbosity() {
return getConsole().getVerbosity();
}
/**
* @return A clone of this {@link ExecutionContext} with {@code stdout} and {@code stderr}
* redirected to the provided {@link PrintStream}s.
*/
public ExecutionContext createSubContext(PrintStream newStdout, PrintStream newStderr) {
Console console = new Console(
this.getConsole().getVerbosity(),
newStdout,
newStderr,
this.getConsole().getAnsi());
return ImmutableExecutionContext.copyOf(this)
.withConsole(console)
.withProcessExecutor(new ProcessExecutor(console))
.withClassLoaderCache(getClassLoaderCache().addRef());
}
public void logError(Throwable error, String msg, Object... formatArgs) {
getBuckEventBus().post(ThrowableConsoleEvent.create(error, msg, formatArgs));
}
public void postEvent(BuckEvent event) {
getBuckEventBus().post(event);
}
public Path getProjectDirectoryRoot() {
return getProjectFilesystem().getRootPath();
}
public PrintStream getStdErr() {
return getConsole().getStdErr();
}
public PrintStream getStdOut() {
return getConsole().getStdOut();
}
public Ansi getAnsi() {
return getConsole().getAnsi();
}
public String getPathToAdbExecutable() throws NoAndroidSdkException {
return getAndroidPlatformTarget().getAdbExecutable().toString();
}
public static Builder builder() {
return new Builder();
}
/**
* Returns the {@link AndroidPlatformTarget}, if present. If not, throws a
* {@link NoAndroidSdkException}. Use this when your logic requires the user to specify the
* location of an Android SDK. A user who is building a "pure Java" (i.e., not Android) project
* using Buck should never have to exercise this code path.
* <p>
* If the location of an Android SDK is optional, then use
* {@link #getAndroidPlatformTargetSupplier()}.
* @throws NoAndroidSdkException if no AndroidPlatformTarget is available
*/
public AndroidPlatformTarget getAndroidPlatformTarget() throws NoAndroidSdkException {
return getAndroidPlatformTargetSupplier().get();
}
/**
* Attempts to resolve an executable in a cross-platform way.
* @param base The folder you expect to find the executable in.
* @param executable The name of the executable you wish to find.
* @return The {@link Path} to the executable is resolved, or {@link Optional#absent()}.
*/
public Optional<Path> resolveExecutable(Path base, String executable) {
String possibleExtensions = getEnvironment().get("PATHEXT");
ImmutableList.Builder<String> extensions = ImmutableList.builder();
if (possibleExtensions != null) {
for (String extension : possibleExtensions.split(File.pathSeparator)) {
extensions.add(extension);
}
}
return MorePaths.searchPathsForExecutable(
Paths.get(executable),
ImmutableList.of(base),
extensions.build());
}
@Override
public void close() throws IOException {
getClassLoaderCache().close();
}
public BuildId getBuildId() {
return getBuckEventBus().getBuildId();
}
public static class Builder {
@Nullable private ProjectFilesystem projectFilesystem = null;
@Nullable private Console console = null;
private Supplier<AndroidPlatformTarget> androidPlatformTarget =
AndroidPlatformTarget.explodingAndroidPlatformTargetSupplier;
private Optional<TargetDevice> targetDevice = Optional.absent();
private long defaultTestTimeoutMillis = 0L;
private boolean isCodeCoverageEnabled = false;
private boolean isDebugEnabled = false;
@Nullable private ProcessExecutor processExecutor;
@Nullable private BuckEventBus eventBus = null;
@Nullable private Platform platform = null;
@Nullable private ImmutableMap<String, String> environment = null;
@Nullable private JavaPackageFinder javaPackageFinder = null;
@Nullable private ObjectMapper objectMapper = null;
private ClassLoaderCache classLoaderCache = new ClassLoaderCache();
private Builder() {}
public ExecutionContext build() {
return ImmutableExecutionContext.of(
Preconditions.checkNotNull(projectFilesystem),
Preconditions.checkNotNull(console),
androidPlatformTarget,
targetDevice,
defaultTestTimeoutMillis,
isCodeCoverageEnabled,
isDebugEnabled,
Preconditions.checkNotNull(processExecutor),
Preconditions.checkNotNull(eventBus),
Preconditions.checkNotNull(platform),
Preconditions.checkNotNull(environment),
Preconditions.checkNotNull(javaPackageFinder),
Preconditions.checkNotNull(objectMapper),
Preconditions.checkNotNull(classLoaderCache));
}
public Builder setExecutionContext(ExecutionContext executionContext) {
setProjectFilesystem(executionContext.getProjectFilesystem());
setConsole(executionContext.getConsole());
setAndroidPlatformTargetSupplier(executionContext.getAndroidPlatformTargetSupplier());
setTargetDevice(executionContext.getTargetDeviceOptional());
setDefaultTestTimeoutMillis(executionContext.getDefaultTestTimeoutMillis());
setCodeCoverageEnabled(executionContext.isCodeCoverageEnabled());
setDebugEnabled(executionContext.isDebugEnabled());
setEventBus(executionContext.getBuckEventBus());
setPlatform(executionContext.getPlatform());
setEnvironment(executionContext.getEnvironment());
setJavaPackageFinder(executionContext.getJavaPackageFinder());
setObjectMapper(executionContext.getObjectMapper());
return this;
}
public Builder setProjectFilesystem(ProjectFilesystem projectFilesystem) {
this.projectFilesystem = projectFilesystem;
return this;
}
public Builder setConsole(Console console) {
this.console = console;
if (this.processExecutor == null) {
this.processExecutor = new ProcessExecutor(console);
}
return this;
}
public Builder setAndroidPlatformTargetSupplier(
Supplier<AndroidPlatformTarget> androidPlatformTarget) {
this.androidPlatformTarget = androidPlatformTarget;
return this;
}
public Builder setTargetDevice(Optional<TargetDevice> targetDevice) {
this.targetDevice = targetDevice;
return this;
}
/** Specify 0 for no timeout. */
public Builder setDefaultTestTimeoutMillis(long defaultTestTimeoutMillis) {
Preconditions.checkArgument(
defaultTestTimeoutMillis >= 0,
"Default timeout cannot be negative.");
this.defaultTestTimeoutMillis = defaultTestTimeoutMillis;
return this;
}
public Builder setCodeCoverageEnabled(boolean isCodeCoverageEnabled) {
this.isCodeCoverageEnabled = isCodeCoverageEnabled;
return this;
}
public Builder setDebugEnabled(boolean isDebugEnabled) {
this.isDebugEnabled = isDebugEnabled;
return this;
}
public Builder setProcessExecutor(ProcessExecutor processExecutor) {
this.processExecutor = processExecutor;
return this;
}
public Builder setEventBus(BuckEventBus eventBus) {
this.eventBus = eventBus;
return this;
}
public Builder setPlatform(Platform platform) {
this.platform = platform;
return this;
}
public Builder setEnvironment(ImmutableMap<String, String> environment) {
this.environment = environment;
return this;
}
public Builder setJavaPackageFinder(JavaPackageFinder javaPackageFinder) {
this.javaPackageFinder = javaPackageFinder;
return this;
}
public Builder setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
return this;
}
public Builder setClassLoaderCache(ClassLoaderCache classLoaderCache) {
this.classLoaderCache = classLoaderCache;
return this;
}
}
}