blob: c1008cd3d2300abbdabbea5fca8cb13a87b87ad5 [file] [log] [blame]
/*
* Copyright 2016 The Bazel Authors. All rights reserved.
*
* 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.google.idea.blaze.clwb.run;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.idea.blaze.base.async.executor.BlazeExecutor;
import com.google.idea.blaze.base.async.process.ExternalTask;
import com.google.idea.blaze.base.command.BlazeCommand;
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
import com.google.idea.blaze.base.command.buildresult.BuildResultHelper;
import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
import com.google.idea.blaze.base.run.confighandler.BlazeCommandRunConfigurationRunner;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.ScopedTask;
import com.google.idea.blaze.base.scope.output.StatusOutput;
import com.google.idea.blaze.base.scope.scopes.BlazeConsoleScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.util.SaveUtil;
import com.google.idea.common.experiments.BoolExperiment;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.jetbrains.cidr.execution.CidrCommandLineState;
import java.io.File;
/** CLion-specific handler for {@link BlazeCommandRunConfiguration}s. */
public class BlazeCidrRunConfigurationRunner implements BlazeCommandRunConfigurationRunner {
private static final Logger LOG = Logger.getInstance(ExternalTask.class);
private static final BoolExperiment FORCE_DEBUG_BUILD_FOR_DEBUGGING_TEST =
new BoolExperiment("clwb.force.debug.build.for.debugging.test", true);
private final BlazeCommandRunConfiguration configuration;
/** Calculated during the before-run task, and made available to the debugger. */
File executableToDebug = null;
BlazeCidrRunConfigurationRunner(BlazeCommandRunConfiguration configuration) {
this.configuration = configuration;
}
@Override
public RunProfileState getRunProfileState(Executor executor, ExecutionEnvironment env) {
return new CidrCommandLineState(env, new BlazeCidrLauncher(configuration, this, env));
}
@Override
public boolean executeBeforeRunTask(ExecutionEnvironment environment) {
if (!isDebugging(environment)) {
return true;
}
try {
File executable = getExecutableToDebug();
if (executable != null) {
executableToDebug = executable;
return true;
}
} catch (ExecutionException e) {
LOG.error(e.getMessage());
}
return false;
}
private static boolean isDebugging(ExecutionEnvironment environment) {
Executor executor = environment.getExecutor();
return executor instanceof DefaultDebugExecutor;
}
/**
* Builds blaze C/C++ target in debug mode, and returns the output build artifact.
*
* @throws ExecutionException if no unique output artifact was found.
*/
private File getExecutableToDebug() throws ExecutionException {
final Project project = configuration.getProject();
final BlazeCommandRunConfigurationCommonState handlerState =
(BlazeCommandRunConfigurationCommonState) configuration.getHandler().getState();
final WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
final ProjectViewSet projectViewSet =
ProjectViewManager.getInstance(project).getProjectViewSet();
BuildResultHelper buildResultHelper = BuildResultHelper.forFiles(file -> true);
final ListenableFuture<Void> buildOperation =
BlazeExecutor.submitTask(
project,
new ScopedTask() {
@Override
protected void execute(BlazeContext context) {
context
.push(new IssuesScope(project))
.push(new BlazeConsoleScope.Builder(project).build());
context.output(new StatusOutput("Building debug binary"));
BlazeCommand.Builder command =
BlazeCommand.builder(
Blaze.getBuildSystemProvider(project).getBinaryPath(),
BlazeCommandName.BUILD)
.addTargets(configuration.getTarget())
.addBlazeFlags(BlazeFlags.buildFlags(project, projectViewSet))
.addBlazeFlags(handlerState.getBlazeFlags())
.addBlazeFlags(buildResultHelper.getBuildFlags());
// If we are trying to debug, make sure we are building in debug mode.
// This can cause a rebuild, so it is a heavyweight setting.
if (FORCE_DEBUG_BUILD_FOR_DEBUGGING_TEST.getValue()) {
command.addBlazeFlags("-c", "dbg");
}
ExternalTask.builder(workspaceRoot)
.addBlazeCommand(command.build())
.context(context)
.stderr(
buildResultHelper.stderr(
new IssueOutputLineProcessor(project, context, workspaceRoot)))
.build()
.run();
}
});
try {
SaveUtil.saveAllFiles();
buildOperation.get();
} catch (InterruptedException | java.util.concurrent.ExecutionException e) {
throw new ExecutionException(e);
}
ImmutableList<File> outputArtifacts = buildResultHelper.getBuildArtifacts();
if (outputArtifacts.isEmpty()) {
throw new ExecutionException(
String.format("No output artifacts found when building %s", configuration.getTarget()));
}
if (outputArtifacts.size() > 1) {
throw new ExecutionException(
String.format(
"More than 1 executable was produced when building %s; don't know which one to debug",
configuration.getTarget()));
}
LocalFileSystem.getInstance().refreshIoFiles(outputArtifacts);
return Iterables.getOnlyElement(outputArtifacts);
}
}