"Build All artifacts" action added
[fedora-idea.git] / java / compiler / impl / src / com / intellij / compiler / actions / GenerateAntBuildAction.java
blob39e9fd3cf8015914dab44eaa30160bf2bc438103
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.compiler.actions;
18 import com.intellij.compiler.CompilerConfiguration;
19 import com.intellij.compiler.CompilerConfigurationImpl;
20 import com.intellij.compiler.ant.*;
21 import com.intellij.compiler.impl.CompilerUtil;
22 import com.intellij.openapi.actionSystem.AnActionEvent;
23 import com.intellij.openapi.actionSystem.DataContext;
24 import com.intellij.openapi.actionSystem.PlatformDataKeys;
25 import com.intellij.openapi.actionSystem.Presentation;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.compiler.CompilerBundle;
28 import com.intellij.openapi.progress.ProgressIndicator;
29 import com.intellij.openapi.progress.ProgressManager;
30 import com.intellij.openapi.progress.Task;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.ui.Messages;
33 import com.intellij.openapi.util.io.FileUtil;
34 import com.intellij.openapi.vfs.LocalFileSystem;
35 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
36 import com.intellij.openapi.vfs.VfsUtil;
37 import com.intellij.openapi.vfs.VirtualFile;
38 import org.jetbrains.annotations.NonNls;
39 import org.jetbrains.annotations.NotNull;
41 import java.io.*;
42 import java.util.*;
44 public class GenerateAntBuildAction extends CompileActionBase {
45 @NonNls private static final String XML_EXTENSION = ".xml";
47 protected void doAction(DataContext dataContext, final Project project) {
48 ((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).convertPatterns();
49 final GenerateAntBuildDialog dialog = new GenerateAntBuildDialog(project);
50 dialog.show();
51 if (dialog.isOK()) {
52 final String[] names = dialog.getRepresentativeModuleNames();
53 final GenerationOptionsImpl[] genOptions = {null};
54 Runnable runnable = new Runnable() {
55 public void run() {
56 genOptions[0] = new GenerationOptionsImpl(project, dialog.isGenerateSingleFileBuild(), dialog.isFormsCompilationEnabled(),
57 dialog.isBackupFiles(), dialog.isForceTargetJdk(), dialog.isRuntimeClasspathInlined(),
58 dialog.isIdeaHomeGenerated(), names);
61 if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, "Analyzing project structure...", true, project)) {
62 return;
64 if (!validateGenOptions(project, genOptions[0])) {
65 return;
67 generate(project, genOptions[0]);
71 /**
72 * Validate generation options and notify user about possible problems
74 * @param project a context project
75 * @param genOptions a generation optiosn
76 * @return true if the generator should proceed with current options or if there is not conflict.
78 private static boolean validateGenOptions(Project project, GenerationOptionsImpl genOptions) {
79 final Collection<String> EMPTY = Collections.emptyList();
80 Collection<String> conflicts = EMPTY;
81 for (ModuleChunk chunk : genOptions.getModuleChunks()) {
82 final ChunkCustomCompilerExtension[] customeCompilers = chunk.getCustomCompilers();
83 if (customeCompilers.length > 1) {
84 if (conflicts == EMPTY) {
85 conflicts = new LinkedList<String>();
87 conflicts.add(chunk.getName());
90 if (!conflicts.isEmpty()) {
91 StringBuilder msg = new StringBuilder();
92 for (String conflictingChunk : conflicts) {
93 msg.append(CompilerBundle.message("generate.ant.build.custom.compiler.conflict.message.row", conflictingChunk));
95 int rc = Messages
96 .showOkCancelDialog(project, CompilerBundle.message("generate.ant.build.custom.compiler.conflict.message", msg.toString()),
97 CompilerBundle.message("generate.ant.build.custom.compiler.conflict.titile"), Messages.getErrorIcon());
98 if (rc != 0) {
99 return false;
102 return true;
105 public void update(AnActionEvent event) {
106 Presentation presentation = event.getPresentation();
107 Project project = PlatformDataKeys.PROJECT.getData(event.getDataContext());
108 presentation.setEnabled(project != null);
111 private void generate(final Project project, final GenerationOptions genOptions) {
112 ApplicationManager.getApplication().saveAll();
113 final List<File> filesToRefresh = new ArrayList<File>();
114 final IOException[] _ex = new IOException[]{null};
115 final List<File> _generated = new ArrayList<File>();
117 try {
118 if (genOptions.generateSingleFile) {
119 final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
120 final File destFile = new File(projectBuildFileDestDir, BuildProperties.getProjectBuildFileName(project) + XML_EXTENSION);
121 final File propertiesFile = new File(projectBuildFileDestDir, BuildProperties.getPropertyFileName(project));
123 ensureFilesWritable(project, new File[]{destFile, propertiesFile});
125 else {
126 final List<File> allFiles = new ArrayList<File>();
128 final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
129 allFiles.add(new File(projectBuildFileDestDir, BuildProperties.getProjectBuildFileName(project) + XML_EXTENSION));
130 allFiles.add(new File(projectBuildFileDestDir, BuildProperties.getPropertyFileName(project)));
132 final ModuleChunk[] chunks = genOptions.getModuleChunks();
133 for (final ModuleChunk chunk : chunks) {
134 final File chunkBaseDir = BuildProperties.getModuleChunkBaseDir(chunk);
135 allFiles.add(new File(chunkBaseDir, BuildProperties.getModuleChunkBuildFileName(chunk) + XML_EXTENSION));
138 ensureFilesWritable(project, allFiles.toArray(new File[allFiles.size()]));
141 new Task.Modal(project, CompilerBundle.message("generate.ant.build.title"), false) {
142 public void run(@NotNull final ProgressIndicator indicator) {
143 indicator.setIndeterminate(true);
144 indicator.setText(CompilerBundle.message("generate.ant.build.progress.message"));
145 try {
146 final File[] generated;
147 if (genOptions.generateSingleFile) {
148 generated = generateSingleFileBuild(project, genOptions, filesToRefresh);
150 else {
151 generated = generateMultipleFileBuild(project, genOptions, filesToRefresh);
153 if (generated != null) {
154 _generated.addAll(Arrays.asList(generated));
157 catch (IOException e) {
158 _ex[0] = e;
161 }.queue();
164 catch (IOException e) {
165 _ex[0] = e;
168 if (_ex[0] != null) {
169 Messages.showErrorDialog(project, CompilerBundle.message("error.ant.files.generate.failed", _ex[0].getMessage()),
170 CompilerBundle.message("generate.ant.build.title"));
172 else {
173 StringBuffer filesString = new StringBuffer();
174 for (int idx = 0; idx < _generated.size(); idx++) {
175 final File file = _generated.get(idx);
176 if (idx > 0) {
177 filesString.append(",\n");
179 filesString.append(file.getPath());
181 Messages.showInfoMessage(project, CompilerBundle.message("message.ant.files.generated.ok", filesString.toString()),
182 CompilerBundle.message("generate.ant.build.title"));
185 if (filesToRefresh.size() > 0) {
186 CompilerUtil.refreshIOFiles(filesToRefresh);
190 private boolean backup(final File file, final Project project, GenerationOptions genOptions, List<File> filesToRefresh) {
191 if (!genOptions.backupPreviouslyGeneratedFiles || !file.exists()) {
192 return true;
194 final String path = file.getPath();
195 final int extensionIndex = path.lastIndexOf(".");
196 final String extension = path.substring(extensionIndex, path.length());
197 //noinspection HardCodedStringLiteral
198 final String backupPath = path.substring(0, extensionIndex) +
199 "_" +
200 new Date(file.lastModified()).toString().replaceAll("\\s+", "_").replaceAll(":", "-") +
201 extension;
202 final File backupFile = new File(backupPath);
203 boolean ok;
204 try {
205 FileUtil.rename(file, backupFile);
206 ok = true;
208 catch (IOException e) {
209 Messages.showErrorDialog(project, CompilerBundle.message("error.ant.files.backup.failed", path),
210 CompilerBundle.message("generate.ant.build.title"));
211 ok = false;
213 filesToRefresh.add(backupFile);
214 return ok;
217 private File[] generateSingleFileBuild(Project project, GenerationOptions genOptions, List<File> filesToRefresh) throws IOException {
218 final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
219 projectBuildFileDestDir.mkdirs();
220 final File destFile = new File(projectBuildFileDestDir, BuildProperties.getProjectBuildFileName(project) + XML_EXTENSION);
221 final File propertiesFile = new File(projectBuildFileDestDir, BuildProperties.getPropertyFileName(project));
223 if (!backup(destFile, project, genOptions, filesToRefresh)) {
224 return null;
226 if (!backup(propertiesFile, project, genOptions, filesToRefresh)) {
227 return null;
230 generateSingleFileBuild(project, genOptions, destFile, propertiesFile);
232 filesToRefresh.add(destFile);
233 filesToRefresh.add(propertiesFile);
234 return new File[]{destFile, propertiesFile};
237 public static void generateSingleFileBuild(final Project project,
238 final GenerationOptions genOptions,
239 final File buildxmlFile,
240 final File propertiesFile) throws IOException {
241 FileUtil.createIfDoesntExist(buildxmlFile);
242 FileUtil.createIfDoesntExist(propertiesFile);
243 final PrintWriter dataOutput = makeWriter(buildxmlFile);
244 try {
245 new SingleFileProjectBuild(project, genOptions).generate(dataOutput);
247 finally {
248 dataOutput.close();
250 final PrintWriter propertiesOut = makeWriter(propertiesFile);
251 try {
252 new PropertyFileGeneratorImpl(project, genOptions).generate(propertiesOut);
254 finally {
255 propertiesOut.close();
260 * Create print writer over file with UTF-8 encoding
262 * @param buildxmlFile a file to write to
263 * @return a created print writer
264 * @throws UnsupportedEncodingException if endcoding not found
265 * @throws FileNotFoundException if file not found
267 private static PrintWriter makeWriter(final File buildxmlFile) throws UnsupportedEncodingException, FileNotFoundException {
268 return new PrintWriter(new OutputStreamWriter(new FileOutputStream(buildxmlFile), "UTF-8"));
271 private void ensureFilesWritable(Project project, File[] files) throws IOException {
272 final List<VirtualFile> toCheck = new ArrayList<VirtualFile>(files.length);
273 final LocalFileSystem lfs = LocalFileSystem.getInstance();
274 for (File file : files) {
275 final VirtualFile vFile = lfs.findFileByIoFile(file);
276 if (vFile != null) {
277 toCheck.add(vFile);
280 final ReadonlyStatusHandler.OperationStatus status =
281 ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(VfsUtil.toVirtualFileArray(toCheck));
282 if (status.hasReadonlyFiles()) {
283 throw new IOException(status.getReadonlyFilesMessage());
287 public File[] generateMultipleFileBuild(Project project, GenerationOptions genOptions, List<File> filesToRefresh) throws IOException {
288 final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
289 projectBuildFileDestDir.mkdirs();
290 final List<File> generated = new ArrayList<File>();
291 final File projectBuildFile = new File(projectBuildFileDestDir, BuildProperties.getProjectBuildFileName(project) + XML_EXTENSION);
292 final File propertiesFile = new File(projectBuildFileDestDir, BuildProperties.getPropertyFileName(project));
293 final ModuleChunk[] chunks = genOptions.getModuleChunks();
295 final File[] chunkFiles = new File[chunks.length];
296 for (int idx = 0; idx < chunks.length; idx++) {
297 final ModuleChunk chunk = chunks[idx];
298 final File chunkBaseDir = BuildProperties.getModuleChunkBaseDir(chunk);
299 chunkFiles[idx] = new File(chunkBaseDir, BuildProperties.getModuleChunkBuildFileName(chunk) + XML_EXTENSION);
302 if (!backup(projectBuildFile, project, genOptions, filesToRefresh)) {
303 return null;
305 if (!backup(propertiesFile, project, genOptions, filesToRefresh)) {
306 return null;
309 FileUtil.createIfDoesntExist(projectBuildFile);
310 final PrintWriter mainDataOutput = makeWriter(projectBuildFile);
311 try {
312 final MultipleFileProjectBuild build = new MultipleFileProjectBuild(project, genOptions);
313 build.generate(mainDataOutput);
314 generated.add(projectBuildFile);
316 // the sequence in which modules are imported is important cause output path properties for dependent modules should be defined first
318 for (int idx = 0; idx < chunks.length; idx++) {
319 final ModuleChunk chunk = chunks[idx];
320 final File chunkBuildFile = chunkFiles[idx];
321 final File chunkBaseDir = chunkBuildFile.getParentFile();
322 if (chunkBaseDir != null) {
323 chunkBaseDir.mkdirs();
325 final boolean moduleBackupOk = backup(chunkBuildFile, project, genOptions, filesToRefresh);
326 if (!moduleBackupOk) {
327 return null;
330 FileUtil.createIfDoesntExist(chunkBuildFile);
331 final PrintWriter out = makeWriter(chunkBuildFile);
332 try {
333 new ModuleChunkAntProject(project, chunk, genOptions).generate(out);
334 generated.add(chunkBuildFile);
336 finally {
337 out.close();
341 finally {
342 mainDataOutput.close();
344 // properties
345 final PrintWriter propertiesOut = makeWriter(propertiesFile);
346 try {
347 new PropertyFileGeneratorImpl(project, genOptions).generate(propertiesOut);
348 generated.add(propertiesFile);
350 finally {
351 propertiesOut.close();
354 filesToRefresh.addAll(generated);
355 return generated.toArray(new File[generated.size()]);