blob: bf97eea7a346c53d526eee62d2df3ccd7ffd3554 [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.java.run;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
import com.google.idea.blaze.base.run.BlazeRunConfiguration;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.ui.UiUtil;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.LocatableConfigurationBase;
import com.intellij.execution.configurations.RuntimeConfigurationError;
import com.intellij.execution.configurations.RuntimeConfigurationException;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.util.execution.ParametersListUtil;
import org.jdom.Element;
import javax.annotation.Nullable;
import javax.swing.*;
import java.util.List;
/**
* A run configuration which executes Blaze commands.
*/
public class BlazeCommandRunConfiguration extends LocatableConfigurationBase implements BlazeRunConfiguration {
private static final String TARGET_TAG = "blaze-target";
private static final String COMMAND_ATTR = "blaze-command";
private static final String USER_BLAZE_FLAG_TAG = "blaze-user-flag";
private static final String USER_EXE_FLAG_TAG = "blaze-user-exe-flag";
protected final String buildSystemName;
@Nullable private TargetExpression target;
@Nullable private BlazeCommandName command;
private ImmutableList<String> blazeFlags = ImmutableList.of();
private ImmutableList<String> exeFlags = ImmutableList.of();
public BlazeCommandRunConfiguration(Project project,
ConfigurationFactory factory,
String name) {
super(project, factory, name);
buildSystemName = Blaze.buildSystemName(project);
}
@Override
@Nullable
public TargetExpression getTarget() {
return target;
}
@Nullable
public BlazeCommandName getCommand() {
return command;
}
/**
* @return The list of blaze flags that the user specified manually.
*/
protected List<String> getBlazeFlags() {
return blazeFlags;
}
/**
* @return The list of executable flags the user specified manually.
*/
protected List<String> getExeFlags() {
return exeFlags;
}
/**
* @return The list of all flags to be used on the Blaze command line for blaze. Subclasses should override this method to add flags for
* higher-level settings (e.g. "run locally").
*/
public List<String> getAllBlazeFlags() {
return getBlazeFlags();
}
/**
* @return The list of all flags to be used for the executable on the Blaze command line. Subclasses should override this method to add
* flags if desired.
*/
public List<String> getAllExeFlags() {
return getExeFlags();
}
public void setTarget(@Nullable TargetExpression target) {
this.target = target;
}
public void setCommand(@Nullable BlazeCommandName command) {
this.command = command;
}
public final void setBlazeFlags(List<String> flags) {
this.blazeFlags = ImmutableList.copyOf(flags);
}
public final void setExeFlags(List<String> flags) {
this.exeFlags = ImmutableList.copyOf(flags);
}
@Override
public void checkConfiguration() throws RuntimeConfigurationException {
if (target == null) {
throw new RuntimeConfigurationError(String.format("You must specify a %s target expression.", buildSystemName));
}
if (command == null) {
throw new RuntimeConfigurationError(String.format("You must specify a command.", buildSystemName));
}
}
@Override
public void readExternal(Element element) throws InvalidDataException {
super.readExternal(element);
// Target is persisted as a tag to permit multiple targets in the future.
Element targetElement = element.getChild(TARGET_TAG);
if (targetElement != null && !Strings.isNullOrEmpty(targetElement.getTextTrim())) {
target = TargetExpression.fromString(targetElement.getTextTrim());
}
else {
target = null;
}
String commandString = element.getAttributeValue(COMMAND_ATTR);
command = Strings.isNullOrEmpty(commandString) ?
null : BlazeCommandName.fromString(commandString);
blazeFlags = loadUserFlags(element, USER_BLAZE_FLAG_TAG);
exeFlags = loadUserFlags(element, USER_EXE_FLAG_TAG);
}
private static ImmutableList<String> loadUserFlags(Element root, String tag) {
ImmutableList.Builder<String> flagsBuilder = ImmutableList.builder();
for (Element e : root.getChildren(tag)) {
String flag = e.getTextTrim();
if (flag != null && !flag.isEmpty()) {
flagsBuilder.add(flag);
}
}
return flagsBuilder.build();
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
super.writeExternal(element);
if (target != null) {
// Target is persisted as a tag to permit multiple targets in the future.
Element targetElement = new Element(TARGET_TAG);
targetElement.setText(target.toString());
element.addContent(targetElement);
}
if (command != null) {
element.setAttribute(COMMAND_ATTR, command.toString());
}
saveUserFlags(element, blazeFlags, USER_BLAZE_FLAG_TAG);
saveUserFlags(element, exeFlags, USER_EXE_FLAG_TAG);
}
private static void saveUserFlags(Element root, List<String> flags, String tag) {
for (String flag : flags) {
Element child = new Element(tag);
child.setText(flag);
root.addContent(child);
}
}
@Override
public BlazeCommandRunConfiguration clone() {
final BlazeCommandRunConfiguration configuration = (BlazeCommandRunConfiguration)super.clone();
configuration.target = target;
configuration.command = command;
configuration.blazeFlags = blazeFlags;
configuration.exeFlags = exeFlags;
return configuration;
}
@Override
@Nullable
public String suggestedName() {
TargetExpression target = getTarget();
if (!(target instanceof Label)) {
return null;
}
return String.format("%s %s: %s", buildSystemName, command.toString(), ((Label)target).ruleName().toString());
}
@Nullable
@Override
public BlazeCommandRunProfileState getState(Executor executor,
ExecutionEnvironment environment) throws ExecutionException {
return new BlazeCommandRunProfileState(environment, executor instanceof DefaultDebugExecutor);
}
@Override
public SettingsEditor<? extends BlazeCommandRunConfiguration> getConfigurationEditor() {
return new BlazeCommandRunConfigurationSettingsEditor(buildSystemName);
}
@VisibleForTesting
static class BlazeCommandRunConfigurationSettingsEditor extends SettingsEditor<BlazeCommandRunConfiguration> {
private final String buildSystemName;
private final JTextField targetField = new JTextField();
private final ComboBox commandCombo;
private final JTextArea blazeFlagsField = new JTextArea(5, 0);
private final JTextArea exeFlagsField = new JTextArea(5, 0);
public BlazeCommandRunConfigurationSettingsEditor(String buildSystemName) {
this.buildSystemName = buildSystemName;
commandCombo = new ComboBox(
new DefaultComboBoxModel(BlazeCommandName.knownCommands().toArray()));
// Allow the user to manually specify an unlisted command.
commandCombo.setEditable(true);
}
@VisibleForTesting
@Override
public void resetEditorFrom(BlazeCommandRunConfiguration s) {
targetField.setText(s.target == null ? null : s.target.toString());
commandCombo.setSelectedItem(s.command);
blazeFlagsField.setText(ParametersListUtil.join(s.blazeFlags));
exeFlagsField.setText(ParametersListUtil.join(s.exeFlags));
}
@VisibleForTesting
@Override
public void applyEditorTo(BlazeCommandRunConfiguration s) throws ConfigurationException {
String targetString = targetField.getText();
s.target = Strings.isNullOrEmpty(targetString) ?
null : TargetExpression.fromString(targetString);
Object selectedCommand = commandCombo.getSelectedItem();
if (selectedCommand instanceof BlazeCommandName) {
s.command = (BlazeCommandName)selectedCommand;
}
else {
s.command = Strings.isNullOrEmpty((String)selectedCommand) ?
null : BlazeCommandName.fromString(selectedCommand.toString());
}
s.blazeFlags = ImmutableList.copyOf(ParametersListUtil.parse(Strings.nullToEmpty(blazeFlagsField.getText())));
s.exeFlags = ImmutableList.copyOf(ParametersListUtil.parse(Strings.nullToEmpty(exeFlagsField.getText())));
}
@Override
protected JComponent createEditor() {
return UiUtil.createBox(
new JLabel("Target expression:"),
targetField,
new JLabel(buildSystemName + " command:"),
commandCombo,
new JLabel(buildSystemName +" flags:"),
blazeFlagsField,
new JLabel("Executable flags:"),
exeFlagsField
);
}
}
}