/*
* 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.event.BuckEvent;
import com.facebook.buck.event.BuckEventBus;
import com.facebook.buck.event.ThrowableConsoleEvent;
import com.facebook.buck.jvm.core.JavaPackageFinder;
import com.facebook.buck.model.BuildId;
import com.facebook.buck.rules.CellPathResolver;
import com.facebook.buck.rules.RuleKeyDiagnosticsMode;
import com.facebook.buck.shell.WorkerProcessPool;
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.concurrent.ConcurrencyLimit;
import com.facebook.buck.util.concurrent.ResourceAllocationFairness;
import com.facebook.buck.util.concurrent.ResourceAmountsEstimator;
import com.facebook.buck.util.environment.Platform;
import com.facebook.buck.util.immutables.BuckStyleImmutable;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListeningExecutorService;
import java.io.Closeable;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.immutables.value.Value;
@Value.Immutable
@BuckStyleImmutable
abstract class AbstractExecutionContext implements Closeable {
@Value.Parameter
abstract Console getConsole();
@Value.Parameter
abstract BuckEventBus getBuckEventBus();
@Value.Parameter
abstract Platform getPlatform();
@Value.Parameter
abstract ImmutableMap<String, String> getEnvironment();
@Value.Parameter
abstract JavaPackageFinder getJavaPackageFinder();
@Value.Parameter
abstract Map<ExecutorPool, ListeningExecutorService> getExecutors();
@Value.Parameter
abstract Optional<TargetDevice> getTargetDevice();
@Value.Parameter
abstract Optional<TargetDeviceOptions> getTargetDeviceOptions();
@Value.Parameter
abstract Optional<AdbOptions> getAdbOptions();
/**
* Worker process pools that are persisted across buck invocations inside buck daemon. If buck is
* running without daemon, there will be no persisted pools.
*/
@Value.Parameter
abstract Optional<ConcurrentMap<String, WorkerProcessPool>> getPersistentWorkerPools();
@Value.Parameter
abstract CellPathResolver getCellPathResolver();
@Value.Parameter
abstract ProcessExecutor getProcessExecutor();
/**
* Returns an {@link AndroidPlatformTarget} if the user specified one. If the user failed to
* specify one, an exception will be thrown.
*/
@Value.Default
public Supplier<AndroidPlatformTarget> getAndroidPlatformTargetSupplier() {
return AndroidPlatformTarget.EXPLODING_ANDROID_PLATFORM_TARGET_SUPPLIER;
}
@Value.Default
public long getDefaultTestTimeoutMillis() {
return 0L;
}
@Value.Default
public boolean isCodeCoverageEnabled() {
return false;
}
@Value.Default
public boolean isInclNoLocationClassesEnabled() {
return false;
}
@Value.Default
public boolean shouldReportAbsolutePaths() {
return false;
}
@Value.Default
public boolean isDebugEnabled() {
return false;
}
@Value.Default
public RuleKeyDiagnosticsMode getRuleKeyDiagnosticsMode() {
return RuleKeyDiagnosticsMode.NEVER;
}
/**
* Worker process pools that you can populate as needed. These will be destroyed as soon as buck
* invocation finishes, thus, these pools are not persisted across buck invocations.
*/
@Value.Default
public ConcurrentMap<String, WorkerProcessPool> getWorkerProcessPools() {
return new ConcurrentHashMap<>();
}
@Value.Default
public ConcurrencyLimit getConcurrencyLimit() {
return new ConcurrencyLimit(
/* threadLimit */ Runtime.getRuntime().availableProcessors(),
ResourceAllocationFairness.FAIR,
ResourceAmountsEstimator.DEFAULT_MANAGED_THREAD_COUNT,
ResourceAmountsEstimator.DEFAULT_AMOUNTS,
ResourceAmountsEstimator.DEFAULT_MAXIMUM_AMOUNTS);
}
@Value.Default
public ClassLoaderCache getClassLoaderCache() {
return new ClassLoaderCache();
}
@Value.Derived
public Verbosity getVerbosity() {
return getConsole().getVerbosity();
}
@Value.Derived
public PrintStream getStdErr() {
return getConsole().getStdErr();
}
@Value.Derived
public PrintStream getStdOut() {
return getConsole().getStdErr();
}
@Value.Derived
public BuildId getBuildId() {
return getBuckEventBus().getBuildId();
}
@Value.Derived
public Ansi getAnsi() {
return getConsole().getAnsi();
}
/**
* Returns the {@link AndroidPlatformTarget}, if present. If not, throws a {@link
* RuntimeException}. 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 RuntimeException if no AndroidPlatformTarget is available
*/
@Value.Lazy
public AndroidPlatformTarget getAndroidPlatformTarget() {
return getAndroidPlatformTargetSupplier().get();
}
public ListeningExecutorService getExecutorService(ExecutorPool p) {
ListeningExecutorService executorService = getExecutors().get(p);
Preconditions.checkNotNull(executorService);
return executorService;
}
public String getPathToAdbExecutable() {
return getAndroidPlatformTarget().getAdbExecutable().toString();
}
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 ExecutionContext createSubContext(
PrintStream newStdout, PrintStream newStderr, Optional<Verbosity> verbosityOverride) {
Console console =
new Console(
verbosityOverride.orElse(this.getConsole().getVerbosity()),
newStdout,
newStderr,
this.getConsole().getAnsi());
return ExecutionContext.builder()
.from(this)
.setConsole(console)
.setProcessExecutor(getProcessExecutor().cloneWithOutputStreams(newStdout, newStderr))
.setClassLoaderCache(getClassLoaderCache().addRef())
.setWorkerProcessPools(new ConcurrentHashMap<String, WorkerProcessPool>())
.build();
}
@Override
public void close() throws IOException {
getClassLoaderCache().close();
try {
for (WorkerProcessPool pool : getWorkerProcessPools().values()) {
pool.close();
}
} finally {
getWorkerProcessPools().clear();
}
}
}