blob: a77064f59ee79aa35bdb5c29937b4ae7d3f24fd6 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* 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.intellij.compiler.actions;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonShortcuts;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.compiler.CompileScope;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListSeparator;
import com.intellij.openapi.ui.popup.MultiSelectionListPopupStep;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.impl.artifacts.ArtifactUtil;
import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
import com.intellij.packaging.impl.compiler.ArtifactsWorkspaceSettings;
import com.intellij.ui.popup.list.ListPopupImpl;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.EmptyIcon;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.*;
/**
* @author nik
*/
public class BuildArtifactAction extends DumbAwareAction {
private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.balloonGroup("Clean artifact");
public BuildArtifactAction() {
super("Build Artifacts...", "Select and build artifacts configured in the project", null);
}
@Override
public void update(AnActionEvent e) {
final Project project = getEventProject(e);
final Presentation presentation = e.getPresentation();
presentation.setEnabled(project != null && !ArtifactUtil.getArtifactWithOutputPaths(project).isEmpty());
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = getEventProject(e);
if (project == null) return;
final List<Artifact> artifacts = ArtifactUtil.getArtifactWithOutputPaths(project);
if (artifacts.isEmpty()) return;
List<ArtifactPopupItem> items = new ArrayList<ArtifactPopupItem>();
if (artifacts.size() > 1) {
items.add(0, new ArtifactPopupItem(null, "All Artifacts", EmptyIcon.ICON_16));
}
Set<Artifact> selectedArtifacts = new HashSet<Artifact>(ArtifactsWorkspaceSettings.getInstance(project).getArtifactsToBuild());
TIntArrayList selectedIndices = new TIntArrayList();
if (Comparing.haveEqualElements(artifacts, selectedArtifacts) && selectedArtifacts.size() > 1) {
selectedIndices.add(0);
selectedArtifacts.clear();
}
for (Artifact artifact : artifacts) {
final ArtifactPopupItem item = new ArtifactPopupItem(artifact, artifact.getName(), artifact.getArtifactType().getIcon());
if (selectedArtifacts.contains(artifact)) {
selectedIndices.add(items.size());
}
items.add(item);
}
final ProjectSettingsService projectSettingsService = ProjectSettingsService.getInstance(project);
final ArtifactAwareProjectSettingsService settingsService = projectSettingsService instanceof ArtifactAwareProjectSettingsService ? (ArtifactAwareProjectSettingsService)projectSettingsService : null;
final ChooseArtifactStep step = new ChooseArtifactStep(items, artifacts.get(0), project, settingsService);
step.setDefaultOptionIndices(selectedIndices.toNativeArray());
final ListPopupImpl popup = (ListPopupImpl)JBPopupFactory.getInstance().createListPopup(step);
final KeyStroke editKeyStroke = KeymapUtil.getKeyStroke(CommonShortcuts.getEditSource());
if (settingsService != null && editKeyStroke != null) {
popup.registerAction("editArtifact", editKeyStroke, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
Object[] values = popup.getSelectedValues();
popup.cancel();
settingsService.openArtifactSettings(values.length > 0 ? ((ArtifactPopupItem)values[0]).getArtifact() : null);
}
});
}
popup.showCenteredInCurrentWindow(project);
}
private static void doBuild(@NotNull Project project, final @NotNull List<ArtifactPopupItem> items, boolean rebuild) {
final Set<Artifact> artifacts = getArtifacts(items, project);
final CompileScope scope = ArtifactCompileScope.createArtifactsScope(project, artifacts, rebuild);
ArtifactsWorkspaceSettings.getInstance(project).setArtifactsToBuild(artifacts);
//in external build we can set 'rebuild' flag per target type
CompilerManager.getInstance(project).make(scope, null);
}
private static Set<Artifact> getArtifacts(final List<ArtifactPopupItem> items, final Project project) {
Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
for (ArtifactPopupItem item : items) {
artifacts.addAll(item.getArtifacts(project));
}
return artifacts;
}
private static class BuildArtifactItem extends ArtifactActionItem {
private BuildArtifactItem(List<ArtifactPopupItem> item, Project project) {
super(item, project, "Build");
}
@Override
public void run() {
doBuild(myProject, myArtifactPopupItems, false);
}
}
private static class CleanArtifactItem extends ArtifactActionItem {
private CleanArtifactItem(@NotNull List<ArtifactPopupItem> item, @NotNull Project project) {
super(item, project, "Clean");
}
@Override
public void run() {
Set<VirtualFile> parents = new HashSet<VirtualFile>();
final VirtualFile[] roots = ProjectRootManager.getInstance(myProject).getContentSourceRoots();
for (VirtualFile root : roots) {
VirtualFile parent = root;
while (parent != null && !parents.contains(parent)) {
parents.add(parent);
parent = parent.getParent();
}
}
Map<String, String> outputPathContainingSourceRoots = new HashMap<String, String>();
final List<Pair<File, Artifact>> toClean = new ArrayList<Pair<File, Artifact>>();
Set<Artifact> artifacts = getArtifacts(myArtifactPopupItems, myProject);
for (Artifact artifact : artifacts) {
String outputPath = artifact.getOutputFilePath();
if (outputPath != null) {
toClean.add(Pair.create(new File(FileUtil.toSystemDependentName(outputPath)), artifact));
final VirtualFile outputFile = LocalFileSystem.getInstance().findFileByPath(outputPath);
if (parents.contains(outputFile)) {
outputPathContainingSourceRoots.put(artifact.getName(), outputPath);
}
}
}
if (!outputPathContainingSourceRoots.isEmpty()) {
final String message;
if (outputPathContainingSourceRoots.size() == 1 && outputPathContainingSourceRoots.values().size() == 1) {
final String name = ContainerUtil.getFirstItem(outputPathContainingSourceRoots.keySet());
final String output = outputPathContainingSourceRoots.get(name);
message = "The output directory '" + output + "' of '" + name + "' artifact contains source roots of the project. Do you want to continue and clear it?";
}
else {
StringBuilder info = new StringBuilder();
for (String name : outputPathContainingSourceRoots.keySet()) {
info.append(" '").append(name).append("' artifact ('").append(outputPathContainingSourceRoots.get(name)).append("')\n");
}
message = "The output directories of the following artifacts contains source roots:\n" +
info + "Do you want to continue and clear these directories?";
}
final int answer = Messages.showYesNoDialog(myProject, message, "Clean Artifacts", null);
if (answer != Messages.YES) {
return;
}
}
new Task.Backgroundable(myProject, "Cleaning artifacts...", true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
List<File> deleted = new ArrayList<File>();
for (Pair<File, Artifact> pair : toClean) {
indicator.checkCanceled();
File file = pair.getFirst();
if (!FileUtil.delete(file)) {
NOTIFICATION_GROUP.createNotification("Cannot clean '" + pair.getSecond().getName() + "' artifact", "cannot delete '" + file.getAbsolutePath() + "'", NotificationType.ERROR, null).notify(myProject);
}
else {
deleted.add(file);
}
}
LocalFileSystem.getInstance().refreshIoFiles(deleted, true, true, null);
}
}.queue();
}
}
private static class RebuildArtifactItem extends ArtifactActionItem {
private RebuildArtifactItem(List<ArtifactPopupItem> item, Project project) {
super(item, project, "Rebuild");
}
@Override
public void run() {
doBuild(myProject, myArtifactPopupItems, true);
}
}
private static class EditArtifactItem extends ArtifactActionItem {
private final ArtifactAwareProjectSettingsService mySettingsService;
private EditArtifactItem(List<ArtifactPopupItem> item, Project project, final ArtifactAwareProjectSettingsService projectSettingsService) {
super(item, project, "Edit...");
mySettingsService = projectSettingsService;
}
@Override
public void run() {
mySettingsService.openArtifactSettings(myArtifactPopupItems.get(0).getArtifact());
}
}
private static abstract class ArtifactActionItem implements Runnable {
protected final List<ArtifactPopupItem> myArtifactPopupItems;
protected final Project myProject;
private String myActionName;
protected ArtifactActionItem(@NotNull List<ArtifactPopupItem> item, @NotNull Project project, @NotNull String name) {
myArtifactPopupItems = item;
myProject = project;
myActionName = name;
}
public String getActionName() {
return myActionName;
}
}
private static class ArtifactPopupItem {
@Nullable private final Artifact myArtifact;
private final String myText;
private final Icon myIcon;
private ArtifactPopupItem(@Nullable Artifact artifact, String text, Icon icon) {
myArtifact = artifact;
myText = text;
myIcon = icon;
}
@Nullable
public Artifact getArtifact() {
return myArtifact;
}
public String getText() {
return myText;
}
public Icon getIcon() {
return myIcon;
}
public List<Artifact> getArtifacts(Project project) {
final Artifact artifact = getArtifact();
return artifact != null ? Collections.singletonList(artifact) : ArtifactUtil.getArtifactWithOutputPaths(project);
}
}
private static class ChooseArtifactStep extends MultiSelectionListPopupStep<ArtifactPopupItem> {
private final Artifact myFirst;
private final Project myProject;
private ArtifactAwareProjectSettingsService mySettingsService;
public ChooseArtifactStep(List<ArtifactPopupItem> artifacts,
Artifact first,
Project project, final ArtifactAwareProjectSettingsService settingsService) {
super("Build Artifact", artifacts);
myFirst = first;
myProject = project;
mySettingsService = settingsService;
}
@Override
public boolean isSpeedSearchEnabled() {
return true;
}
@Override
public Icon getIconFor(ArtifactPopupItem aValue) {
return aValue.getIcon();
}
@NotNull
@Override
public String getTextFor(ArtifactPopupItem value) {
return value.getText();
}
@Override
public boolean hasSubstep(List<ArtifactPopupItem> selectedValues) {
return true;
}
@Override
public ListSeparator getSeparatorAbove(ArtifactPopupItem value) {
return myFirst.equals(value.getArtifact()) ? new ListSeparator() : null;
}
@Override
public PopupStep<?> onChosen(final List<ArtifactPopupItem> selectedValues, boolean finalChoice) {
if (finalChoice) {
return doFinalStep(new Runnable() {
@Override
public void run() {
doBuild(myProject, selectedValues, false);
}
});
}
final List<ArtifactActionItem> actions = new ArrayList<ArtifactActionItem>();
actions.add(new BuildArtifactItem(selectedValues, myProject));
actions.add(new RebuildArtifactItem(selectedValues, myProject));
actions.add(new CleanArtifactItem(selectedValues, myProject));
if (mySettingsService != null) {
actions.add(new EditArtifactItem(selectedValues, myProject, mySettingsService));
}
return new BaseListPopupStep<ArtifactActionItem>(selectedValues.size() == 1 ? "Action" : "Action for " + selectedValues.size() + " artifacts", actions) {
@NotNull
@Override
public String getTextFor(ArtifactActionItem value) {
return value.getActionName();
}
@Override
public PopupStep onChosen(ArtifactActionItem selectedValue, boolean finalChoice) {
return doFinalStep(selectedValue);
}
};
}
}
}