blob: 473400b7e7986439d767aea617ff038261e447ce [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.idea.blaze.base.async.process.LineProcessingOutputStream;
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.issueparser.IssueOutputLineProcessor;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
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.DistributedExecutorSupport;
import com.google.idea.blaze.base.run.processhandler.LineProcessingProcessAdapter;
import com.google.idea.blaze.base.run.processhandler.ScopedBlazeProcessHandler;
import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
import com.google.idea.sdkcompat.cidr.CidrConsoleBuilderAdapter;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.CommandLineState;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessListener;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.xdebugger.XDebugSession;
import com.jetbrains.cidr.cpp.execution.CLionRunParameters;
import com.jetbrains.cidr.execution.CidrConsoleBuilder;
import com.jetbrains.cidr.execution.TrivialInstaller;
import com.jetbrains.cidr.execution.debugger.CidrDebugProcess;
import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess;
import com.jetbrains.cidr.execution.testing.CidrLauncher;
import java.io.File;
import javax.annotation.Nullable;
/**
* Handles running/debugging cc_test and cc_binary targets in CLion. Sets up gdb when debugging, and
* uses the Google Test infrastructure for presenting test results.
*/
public final class BlazeCidrLauncher extends CidrLauncher {
private static final Logger LOG = Logger.getInstance(BlazeCidrLauncher.class);
private final Project project;
private final BlazeCommandRunConfiguration configuration;
private final BlazeCommandRunConfigurationCommonState handlerState;
private final BlazeCidrRunConfigurationRunner runner;
private final ExecutionEnvironment executionEnvironment;
BlazeCidrLauncher(
BlazeCommandRunConfiguration configuration,
BlazeCidrRunConfigurationRunner runner,
ExecutionEnvironment environment) {
this.configuration = configuration;
this.handlerState =
(BlazeCommandRunConfigurationCommonState) configuration.getHandler().getState();
this.runner = runner;
this.executionEnvironment = environment;
this.project = configuration.getProject();
}
@Override
public ProcessHandler createProcess(CommandLineState state) throws ExecutionException {
BlazeImportSettings importSettings =
BlazeImportSettingsManager.getInstance(project).getImportSettings();
LOG.assertTrue(importSettings != null);
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
LOG.assertTrue(projectViewSet != null);
ImmutableList<String> testHandlerFlags = ImmutableList.of();
BlazeTestEventsHandler testEventsHandler =
useTestUi()
? BlazeTestEventsHandler.getHandlerForTarget(project, configuration.getTarget())
: null;
if (testEventsHandler != null) {
testHandlerFlags = BlazeTestEventsHandler.getBlazeFlags(project);
}
BlazeCommand.Builder command =
BlazeCommand.builder(
Blaze.getBuildSystemProvider(project).getBinaryPath(),
handlerState.getCommandState().getCommand())
.addTargets(configuration.getTarget())
.addBlazeFlags(BlazeFlags.buildFlags(project, ProjectViewSet.builder().build()))
.addBlazeFlags(testHandlerFlags)
.addBlazeFlags(handlerState.getBlazeFlagsState().getExpandedFlags())
.addExeFlags(handlerState.getExeFlagsState().getExpandedFlags());
command.addBlazeFlags(
DistributedExecutorSupport.getBlazeFlags(
project, handlerState.getRunOnDistributedExecutorState().runOnDistributedExecutor));
state.setConsoleBuilder(createConsoleBuilder(testEventsHandler));
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromImportSettings(importSettings);
return new ScopedBlazeProcessHandler(
project,
command.build(),
workspaceRoot,
new ScopedBlazeProcessHandler.ScopedProcessHandlerDelegate() {
@Override
public void onBlazeContextStart(BlazeContext context) {
context.push(new IssuesScope(project));
}
@Override
public ImmutableList<ProcessListener> createProcessListeners(BlazeContext context) {
LineProcessingOutputStream outputStream =
LineProcessingOutputStream.of(
new IssueOutputLineProcessor(project, context, workspaceRoot));
return ImmutableList.of(new LineProcessingProcessAdapter(outputStream));
}
});
}
@Override
public CidrDebugProcess createDebugProcess(CommandLineState state, XDebugSession session)
throws ExecutionException {
TargetExpression target = configuration.getTarget();
if (target == null) {
return null;
}
if (runner.executableToDebug == null) {
return null;
}
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
LOG.assertTrue(projectViewSet != null);
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
GeneralCommandLine commandLine = new GeneralCommandLine(runner.executableToDebug.getPath());
File workingDir = workspaceRoot.directory();
commandLine.setWorkDirectory(workingDir);
commandLine.addParameters(handlerState.getExeFlagsState().getExpandedFlags());
TrivialInstaller installer = new TrivialInstaller(commandLine);
ImmutableList<String> startupCommands = getGdbStartupCommands(workingDir);
CLionRunParameters parameters =
new CLionRunParameters(
new BlazeGDBDriverConfiguration(project, startupCommands, workspaceRoot), installer);
return new CidrLocalDebugProcess(parameters, session, state.getConsoleBuilder());
}
@Override
protected Project getProject() {
return project;
}
private CidrConsoleBuilder createConsoleBuilder(
@Nullable BlazeTestEventsHandler testEventsHandler) {
if (testEventsHandler != null) {
return new GoogleTestConsoleBuilder(configuration.getProject(), testEventsHandler);
}
return new CidrConsoleBuilderAdapter(configuration.getProject());
}
private ImmutableList<String> getGdbStartupCommands(File workingDir) {
// Forge creates debug symbol paths rooted at /proc/self/cwd .
// We need to tell gdb to translate this path prefix to the user's workspace
// root so the IDE can find the files.
String from = "/proc/self/cwd";
String to = workingDir.getPath();
String subPathCommand = String.format("set substitute-path %s %s", from, to);
return ImmutableList.of(subPathCommand);
}
private boolean useTestUi() {
return BlazeCommandName.TEST.equals(handlerState.getCommandState().getCommand())
&& !handlerState.getRunOnDistributedExecutorState().runOnDistributedExecutor;
}
private final class GoogleTestConsoleBuilder extends CidrConsoleBuilderAdapter {
private final BlazeTestEventsHandler testEventsHandler;
private GoogleTestConsoleBuilder(Project project, BlazeTestEventsHandler testEventsHandler) {
super(project);
this.testEventsHandler = testEventsHandler;
addFilter(new BlazeCidrTestOutputFilter(project));
}
@Override
protected ConsoleView createConsole() {
return SmRunnerUtils.getConsoleView(
configuration.getProject(),
configuration,
executionEnvironment.getExecutor(),
testEventsHandler);
}
}
}