/*
* Copyright 2009 the original author or authors.
*
* 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 org.gradle.initialization;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableSortedSet;
import org.gradle.BuildListener;
import org.gradle.BuildResult;
import org.gradle.api.Task;
import org.gradle.api.internal.ExceptionAnalyser;
import org.gradle.api.internal.GradleInternal;
import org.gradle.api.internal.SettingsInternal;
import org.gradle.configuration.BuildConfigurer;
import org.gradle.execution.BuildConfigurationActionExecuter;
import org.gradle.execution.BuildExecuter;
import org.gradle.execution.TaskGraphExecuter;
import org.gradle.execution.taskgraph.CalculateTaskGraphBuildOperationType;
import org.gradle.internal.concurrent.CompositeStoppable;
import org.gradle.internal.operations.BuildOperationContext;
import org.gradle.internal.operations.BuildOperationExecutor;
import org.gradle.internal.operations.RunnableBuildOperation;
import org.gradle.internal.progress.BuildOperationDescriptor;
import org.gradle.internal.service.scopes.BuildScopeServices;
import org.gradle.internal.work.WorkerLeaseService;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
public class DefaultGradleLauncher implements GradleLauncher {
private enum Stage {
Load, Configure, Build
}
private final InitScriptHandler initScriptHandler;
private final SettingsLoader settingsLoader;
private final BuildConfigurer buildConfigurer;
private final ExceptionAnalyser exceptionAnalyser;
private final BuildListener buildListener;
private final ModelConfigurationListener modelConfigurationListener;
private final BuildCompletionListener buildCompletionListener;
private final BuildOperationExecutor buildOperationExecutor;
private final BuildConfigurationActionExecuter buildConfigurationActionExecuter;
private final BuildExecuter buildExecuter;
private final BuildScopeServices buildServices;
private final List<?> servicesToStop;
private GradleInternal gradle;
private SettingsInternal settings;
private Stage stage;
public DefaultGradleLauncher(GradleInternal gradle, InitScriptHandler initScriptHandler, SettingsLoader settingsLoader,
BuildConfigurer buildConfigurer, ExceptionAnalyser exceptionAnalyser,
BuildListener buildListener, ModelConfigurationListener modelConfigurationListener,
BuildCompletionListener buildCompletionListener, BuildOperationExecutor operationExecutor,
BuildConfigurationActionExecuter buildConfigurationActionExecuter, BuildExecuter buildExecuter,
BuildScopeServices buildServices, List<?> servicesToStop) {
this.gradle = gradle;
this.initScriptHandler = initScriptHandler;
this.settingsLoader = settingsLoader;
this.buildConfigurer = buildConfigurer;
this.exceptionAnalyser = exceptionAnalyser;
this.buildListener = buildListener;
this.modelConfigurationListener = modelConfigurationListener;
this.buildOperationExecutor = operationExecutor;
this.buildConfigurationActionExecuter = buildConfigurationActionExecuter;
this.buildExecuter = buildExecuter;
this.buildCompletionListener = buildCompletionListener;
this.buildServices = buildServices;
this.servicesToStop = servicesToStop;
}
@Override
public GradleInternal getGradle() {
return gradle;
}
@Override
public SettingsInternal getSettings() {
return settings;
}
@Override
public BuildResult run() {
return doBuild(Stage.Build);
}
@Override
public BuildResult getBuildAnalysis() {
return doBuild(Stage.Configure);
}
@Override
public BuildResult load() throws ReportedException {
return doBuild(Stage.Load);
}
private BuildResult doBuild(final Stage upTo) {
// TODO:pm Move this to RunAsBuildOperationBuildActionRunner when BuildOperationWorkerRegistry scope is changed
final AtomicReference<BuildResult> buildResult = new AtomicReference<BuildResult>();
WorkerLeaseService workerLeaseService = buildServices.get(WorkerLeaseService.class);
workerLeaseService.withLocks(workerLeaseService.getWorkerLease()).execute(new Runnable() {
@Override
public void run() {
Throwable failure = null;
try {
buildListener.buildStarted(gradle);
doBuildStages(upTo);
} catch (Throwable t) {
failure = exceptionAnalyser.transform(t);
}
buildResult.set(new BuildResult(upTo.name(), gradle, failure));
buildListener.buildFinished(buildResult.get());
if (failure != null) {
throw new ReportedException(failure);
}
}
});
return buildResult.get();
}
private void doBuildStages(Stage upTo) {
if (stage == Stage.Build) {
throw new IllegalStateException("Cannot build with GradleLauncher multiple times");
}
if (stage == null) {
// Evaluate init scripts
initScriptHandler.executeScripts(gradle);
// Build `buildSrc`, load settings.gradle, and construct composite (if appropriate)
settings = settingsLoader.findAndLoadSettings(gradle);
stage = Stage.Load;
}
if (upTo == Stage.Load) {
return;
}
if (stage == Stage.Load) {
buildOperationExecutor.run(new ConfigureBuild());
stage = Stage.Configure;
}
if (upTo == Stage.Configure) {
return;
}
// After this point, the GradleLauncher cannot be reused
stage = Stage.Build;
buildOperationExecutor.run(new CalculateTaskGraph());
buildOperationExecutor.run(new ExecuteTasks());
}
/**
* <p>Adds a listener to this build instance. The listener is notified of events which occur during the execution of the build. See {@link org.gradle.api.invocation.Gradle#addListener(Object)} for
* supported listener types.</p>
*
* @param listener The listener to add. Has no effect if the listener has already been added.
*/
@Override
public void addListener(Object listener) {
gradle.addListener(listener);
}
public void stop() {
try {
CompositeStoppable.stoppable(buildServices).add(servicesToStop).stop();
} finally {
buildCompletionListener.completed();
}
}
private class ConfigureBuild implements RunnableBuildOperation {
@Override
public void run(BuildOperationContext context) {
buildConfigurer.configure(gradle);
if (!isConfigureOnDemand()) {
projectsEvaluated();
}
modelConfigurationListener.onConfigure(gradle);
}
@Override
public BuildOperationDescriptor.Builder description() {
return BuildOperationDescriptor.displayName(contextualize("Configure build"));
}
}
private class CalculateTaskGraph implements RunnableBuildOperation {
@Override
public void run(BuildOperationContext buildOperationContext) {
buildConfigurationActionExecuter.select(gradle);
if (isConfigureOnDemand()) {
projectsEvaluated();
}
final TaskGraphExecuter taskGraph = gradle.getTaskGraph();
buildOperationContext.setResult(new CalculateTaskGraphBuildOperationType.Result() {
@Override
public List<String> getRequestedTaskPaths() {
return toTaskPaths(taskGraph.getRequestedTasks());
}
@Override
public List<String> getExcludedTaskPaths() {
return toTaskPaths(taskGraph.getFilteredTasks());
}
private List<String> toTaskPaths(Set<Task> tasks) {
return ImmutableSortedSet.copyOf(Collections2.transform(tasks, new Function<Task, String>() {
@Override
public String apply(Task task) {
return task.getPath();
}
})).asList();
}
});
}
@Override
public BuildOperationDescriptor.Builder description() {
return BuildOperationDescriptor.displayName(contextualize("Calculate task graph"))
.details(new CalculateTaskGraphBuildOperationType.Details() {
});
}
}
private class ExecuteTasks implements RunnableBuildOperation {
@Override
public void run(BuildOperationContext context) {
buildExecuter.execute(gradle);
}
@Override
public BuildOperationDescriptor.Builder description() {
return BuildOperationDescriptor.displayName(contextualize("Run tasks"));
}
}
private boolean isConfigureOnDemand() {
return gradle.getStartParameter().isConfigureOnDemand();
}
private void projectsEvaluated() {
buildListener.projectsEvaluated(gradle);
}
private String contextualize(String descriptor) {
if (isNestedBuild()) {
return descriptor + " (" + gradle.getIdentityPath() + ")";
}
return descriptor;
}
private boolean isNestedBuild() {
return gradle.getParent() != null;
}
}