blob: 62645c9d9bdab984b28d500761310a8b04f0d787 [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.compiler.CompilerConfiguration;
import com.intellij.compiler.CompilerConfigurationImpl;
import com.intellij.compiler.ant.*;
import com.intellij.compiler.impl.CompilerUtil;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompilerBundle;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.util.*;
public class GenerateAntBuildAction extends CompileActionBase {
@NonNls private static final String XML_EXTENSION = ".xml";
protected void doAction(DataContext dataContext, final Project project) {
((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).convertPatterns();
final GenerateAntBuildDialog dialog = new GenerateAntBuildDialog(project);
dialog.show();
if (dialog.isOK()) {
final String[] names = dialog.getRepresentativeModuleNames();
final GenerationOptionsImpl[] genOptions = {null};
Runnable runnable = new Runnable() {
public void run() {
genOptions[0] = new GenerationOptionsImpl(project, dialog.isGenerateSingleFileBuild(), dialog.isFormsCompilationEnabled(),
dialog.isBackupFiles(), dialog.isForceTargetJdk(), dialog.isRuntimeClasspathInlined(),
dialog.isIdeaHomeGenerated(), names, dialog.getOutputFileName());
}
};
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, "Analyzing project structure...", true, project)) {
return;
}
if (!validateGenOptions(project, genOptions[0])) {
return;
}
generate(project, genOptions[0]);
}
}
/**
* Validate generation options and notify user about possible problems
*
* @param project a context project
* @param genOptions a generation optiosn
* @return true if the generator should proceed with current options or if there is not conflict.
*/
private static boolean validateGenOptions(Project project, GenerationOptionsImpl genOptions) {
final Collection<String> EMPTY = Collections.emptyList();
Collection<String> conflicts = EMPTY;
for (ModuleChunk chunk : genOptions.getModuleChunks()) {
final ChunkCustomCompilerExtension[] customeCompilers = chunk.getCustomCompilers();
if (customeCompilers.length > 1) {
if (conflicts == EMPTY) {
conflicts = new LinkedList<String>();
}
conflicts.add(chunk.getName());
}
}
if (!conflicts.isEmpty()) {
StringBuilder msg = new StringBuilder();
for (String conflictingChunk : conflicts) {
msg.append(CompilerBundle.message("generate.ant.build.custom.compiler.conflict.message.row", conflictingChunk));
}
int rc = Messages
.showOkCancelDialog(project, CompilerBundle.message("generate.ant.build.custom.compiler.conflict.message", msg.toString()),
CompilerBundle.message("generate.ant.build.custom.compiler.conflict.title"), Messages.getErrorIcon());
if (rc != Messages.OK) {
return false;
}
}
return true;
}
public void update(AnActionEvent event) {
Presentation presentation = event.getPresentation();
Project project = CommonDataKeys.PROJECT.getData(event.getDataContext());
presentation.setEnabled(project != null);
}
private void generate(final Project project, final GenerationOptions genOptions) {
ApplicationManager.getApplication().saveAll();
final List<File> filesToRefresh = new ArrayList<File>();
final IOException[] _ex = new IOException[]{null};
final List<File> _generated = new ArrayList<File>();
try {
if (genOptions.generateSingleFile) {
final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
final File destFile = new File(projectBuildFileDestDir, genOptions.getBuildFileName());
final File propertiesFile = new File(projectBuildFileDestDir, genOptions.getPropertiesFileName());
ensureFilesWritable(project, new File[]{destFile, propertiesFile});
}
else {
final List<File> allFiles = new ArrayList<File>();
final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
allFiles.add(new File(projectBuildFileDestDir, genOptions.getBuildFileName()));
allFiles.add(new File(projectBuildFileDestDir, genOptions.getPropertiesFileName()));
final ModuleChunk[] chunks = genOptions.getModuleChunks();
for (final ModuleChunk chunk : chunks) {
final File chunkBaseDir = BuildProperties.getModuleChunkBaseDir(chunk);
allFiles.add(new File(chunkBaseDir, BuildProperties.getModuleChunkBuildFileName(chunk) + XML_EXTENSION));
}
ensureFilesWritable(project, allFiles.toArray(new File[allFiles.size()]));
}
new Task.Modal(project, CompilerBundle.message("generate.ant.build.title"), false) {
public void run(@NotNull final ProgressIndicator indicator) {
indicator.setIndeterminate(true);
indicator.setText(CompilerBundle.message("generate.ant.build.progress.message"));
try {
final File[] generated;
if (genOptions.generateSingleFile) {
generated = generateSingleFileBuild(project, genOptions, filesToRefresh);
}
else {
generated = generateMultipleFileBuild(project, genOptions, filesToRefresh);
}
if (generated != null) {
ContainerUtil.addAll(_generated, generated);
}
}
catch (IOException e) {
_ex[0] = e;
}
}
}.queue();
}
catch (IOException e) {
_ex[0] = e;
}
if (_ex[0] != null) {
Messages.showErrorDialog(project, CompilerBundle.message("error.ant.files.generate.failed", _ex[0].getMessage()),
CompilerBundle.message("generate.ant.build.title"));
}
else {
StringBuffer filesString = new StringBuffer();
for (int idx = 0; idx < _generated.size(); idx++) {
final File file = _generated.get(idx);
if (idx > 0) {
filesString.append(",\n");
}
filesString.append(file.getPath());
}
Messages.showInfoMessage(project, CompilerBundle.message("message.ant.files.generated.ok", filesString.toString()),
CompilerBundle.message("generate.ant.build.title"));
}
if (filesToRefresh.size() > 0) {
CompilerUtil.refreshIOFiles(filesToRefresh);
}
}
private boolean backup(final File file, final Project project, GenerationOptions genOptions, List<File> filesToRefresh) {
if (!genOptions.backupPreviouslyGeneratedFiles || !file.exists()) {
return true;
}
final String path = file.getPath();
final int extensionIndex = path.lastIndexOf(".");
final String extension = path.substring(extensionIndex, path.length());
//noinspection HardCodedStringLiteral
final String backupPath = path.substring(0, extensionIndex) +
"_" +
new Date(file.lastModified()).toString().replaceAll("\\s+", "_").replaceAll(":", "-") +
extension;
final File backupFile = new File(backupPath);
boolean ok;
try {
FileUtil.rename(file, backupFile);
ok = true;
}
catch (IOException e) {
Messages.showErrorDialog(project, CompilerBundle.message("error.ant.files.backup.failed", path),
CompilerBundle.message("generate.ant.build.title"));
ok = false;
}
filesToRefresh.add(backupFile);
return ok;
}
private File[] generateSingleFileBuild(Project project, GenerationOptions genOptions, List<File> filesToRefresh) throws IOException {
final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
projectBuildFileDestDir.mkdirs();
final File destFile = new File(projectBuildFileDestDir, genOptions.getBuildFileName());
final File propertiesFile = new File(projectBuildFileDestDir, genOptions.getPropertiesFileName());
if (!backup(destFile, project, genOptions, filesToRefresh)) {
return null;
}
if (!backup(propertiesFile, project, genOptions, filesToRefresh)) {
return null;
}
generateSingleFileBuild(project, genOptions, destFile, propertiesFile);
filesToRefresh.add(destFile);
filesToRefresh.add(propertiesFile);
return new File[]{destFile, propertiesFile};
}
public static void generateSingleFileBuild(final Project project,
final GenerationOptions genOptions,
final File buildxmlFile,
final File propertiesFile) throws IOException {
FileUtil.createIfDoesntExist(buildxmlFile);
FileUtil.createIfDoesntExist(propertiesFile);
final PrintWriter dataOutput = makeWriter(buildxmlFile);
try {
new SingleFileProjectBuild(project, genOptions).generate(dataOutput);
}
finally {
dataOutput.close();
}
final PrintWriter propertiesOut = makeWriter(propertiesFile);
try {
new PropertyFileGeneratorImpl(project, genOptions).generate(propertiesOut);
}
finally {
propertiesOut.close();
}
}
/**
* Create print writer over file with UTF-8 encoding
*
* @param buildxmlFile a file to write to
* @return a created print writer
* @throws UnsupportedEncodingException if endcoding not found
* @throws FileNotFoundException if file not found
*/
private static PrintWriter makeWriter(final File buildxmlFile) throws UnsupportedEncodingException, FileNotFoundException {
return new PrintWriter(new OutputStreamWriter(new FileOutputStream(buildxmlFile), "UTF-8"));
}
private void ensureFilesWritable(Project project, File[] files) throws IOException {
final List<VirtualFile> toCheck = new ArrayList<VirtualFile>(files.length);
final LocalFileSystem lfs = LocalFileSystem.getInstance();
for (File file : files) {
final VirtualFile vFile = lfs.findFileByIoFile(file);
if (vFile != null) {
toCheck.add(vFile);
}
}
final ReadonlyStatusHandler.OperationStatus status =
ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(VfsUtil.toVirtualFileArray(toCheck));
if (status.hasReadonlyFiles()) {
throw new IOException(status.getReadonlyFilesMessage());
}
}
public File[] generateMultipleFileBuild(Project project, GenerationOptions genOptions, List<File> filesToRefresh) throws IOException {
final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
projectBuildFileDestDir.mkdirs();
final List<File> generated = new ArrayList<File>();
final File projectBuildFile = new File(projectBuildFileDestDir, genOptions.getBuildFileName());
final File propertiesFile = new File(projectBuildFileDestDir, genOptions.getPropertiesFileName());
final ModuleChunk[] chunks = genOptions.getModuleChunks();
final File[] chunkFiles = new File[chunks.length];
for (int idx = 0; idx < chunks.length; idx++) {
final ModuleChunk chunk = chunks[idx];
final File chunkBaseDir = BuildProperties.getModuleChunkBaseDir(chunk);
chunkFiles[idx] = new File(chunkBaseDir, BuildProperties.getModuleChunkBuildFileName(chunk) + XML_EXTENSION);
}
if (!backup(projectBuildFile, project, genOptions, filesToRefresh)) {
return null;
}
if (!backup(propertiesFile, project, genOptions, filesToRefresh)) {
return null;
}
FileUtil.createIfDoesntExist(projectBuildFile);
final PrintWriter mainDataOutput = makeWriter(projectBuildFile);
try {
final MultipleFileProjectBuild build = new MultipleFileProjectBuild(project, genOptions);
build.generate(mainDataOutput);
generated.add(projectBuildFile);
// the sequence in which modules are imported is important cause output path properties for dependent modules should be defined first
for (int idx = 0; idx < chunks.length; idx++) {
final ModuleChunk chunk = chunks[idx];
final File chunkBuildFile = chunkFiles[idx];
final File chunkBaseDir = chunkBuildFile.getParentFile();
if (chunkBaseDir != null) {
chunkBaseDir.mkdirs();
}
final boolean moduleBackupOk = backup(chunkBuildFile, project, genOptions, filesToRefresh);
if (!moduleBackupOk) {
return null;
}
FileUtil.createIfDoesntExist(chunkBuildFile);
final PrintWriter out = makeWriter(chunkBuildFile);
try {
new ModuleChunkAntProject(project, chunk, genOptions).generate(out);
generated.add(chunkBuildFile);
}
finally {
out.close();
}
}
}
finally {
mainDataOutput.close();
}
// properties
final PrintWriter propertiesOut = makeWriter(propertiesFile);
try {
new PropertyFileGeneratorImpl(project, genOptions).generate(propertiesOut);
generated.add(propertiesFile);
}
finally {
propertiesOut.close();
}
filesToRefresh.addAll(generated);
return generated.toArray(new File[generated.size()]);
}
}