reporting string ": invalid flag: " as error, all other stuff as informational
[idea/javafx-plugin.git] / src / org / jetbrains / javafx / build / JavaFxCompilerHandler.java
1 /*
2  * Copyright 2000-2010 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.jetbrains.javafx.build;
17
18 import com.intellij.compiler.CompilerException;
19 import com.intellij.compiler.impl.javaCompiler.ModuleChunk;
20 import com.intellij.openapi.compiler.CompileContext;
21 import com.intellij.openapi.compiler.CompilerMessageCategory;
22 import com.intellij.openapi.compiler.TranslatingCompiler;
23 import com.intellij.openapi.compiler.ex.CompileContextEx;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.module.Module;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.projectRoots.Sdk;
28 import com.intellij.openapi.roots.ProjectFileIndex;
29 import com.intellij.openapi.roots.ProjectRootManager;
30 import com.intellij.openapi.util.io.FileUtil;
31 import com.intellij.openapi.util.text.StringUtil;
32 import com.intellij.openapi.vfs.LocalFileSystem;
33 import com.intellij.openapi.vfs.VfsUtil;
34 import com.intellij.openapi.vfs.VirtualFile;
35 import com.intellij.util.Chunk;
36 import com.intellij.util.PathUtil;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39 import org.jetbrains.javafx.JavaFxUtil;
40
41 import java.io.*;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.regex.Matcher;
47 import java.util.regex.Pattern;
48
49 /**
50  * Created by IntelliJ IDEA.
51  *
52  * @author: Alexey.Ivanov
53  */
54 public class JavaFxCompilerHandler {
55   private static final Logger LOG = Logger.getInstance("#org.jetbrains.javafx.build.JavaFxCompilerHandler");
56
57   private final CompileContext myContext;
58   private final Sdk mySdk;
59   private final Project myProject;
60   private String myCompilerOutputPath;
61   private List<File> myTempFiles = new ArrayList<File>();
62
63   public JavaFxCompilerHandler(CompileContext context, Sdk sdk, Project project) {
64     myContext = context;
65     mySdk = sdk;
66     myProject = project;
67   }
68
69   public void runCompiler(final CompileContext context, final Module module, final List<VirtualFile> files) throws CompilerException {
70     LOG.debug("JavaFX SDK version: " + mySdk.getVersionString());
71     final List<String> command = new ArrayList<String>();
72     command.add(mySdk.getHomePath() + "/bin/javafxc");
73     command.add("-cp");
74     command.add(createClasspath(context, module));
75     myCompilerOutputPath = JavaFxUtil.getCompilerOutputPath(module);
76     if (myCompilerOutputPath != null) {
77       command.add("-d");
78       command.add(myCompilerOutputPath);
79     }
80     command.add(createSourcePathCommand(files));
81     LOG.debug("Compilation command: " + command);
82
83     final ProcessBuilder builder = new ProcessBuilder(command);
84     builder.redirectErrorStream(true);
85     try {
86       final Process process = builder.start();
87       readProcessOutput(process);
88     }
89     catch (IOException e) {
90       myContext.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
91     }
92   }
93
94   private String createClasspath(final CompileContext context, final Module module) throws CompilerException {
95     final ModuleChunk chunk =
96       new ModuleChunk((CompileContextEx)context, new Chunk<Module>(module), Collections.<Module, List<VirtualFile>>emptyMap());
97     final String compilationClasspath = chunk.getCompilationClasspath();
98     LOG.debug("Classpath: " + compilationClasspath);
99     return compilationClasspath;
100   }
101
102   private String createSourcePathCommand(final List<VirtualFile> files) throws CompilerException {
103     try {
104       final File tempFile = createTempFile("idea_javafxc_src");
105       final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(tempFile)));
106       try {
107         for (VirtualFile file : files) {
108           writer.println(PathUtil.getLocalPath(file));
109           LOG.debug("Source file: " + file.getPath());
110         }
111       }
112       finally {
113         writer.close();
114       }
115       return ("@" + tempFile.getAbsolutePath());
116     }
117     catch (IOException e) {
118       throw new CompilerException(e.getMessage(), e);
119     }
120   }
121
122   private File createTempFile(final String prefix) throws IOException {
123     final File tempFile = FileUtil.createTempFile(prefix, ".tmp");
124     tempFile.deleteOnExit();
125     myTempFiles.add(tempFile);
126     return tempFile;
127   }
128
129   @Nullable
130   public String getCompilerOutputPath() {
131     return myCompilerOutputPath;
132   }
133
134   // TODO:
135
136   @NotNull
137   public Collection<TranslatingCompiler.OutputItem> getOutputItems() {
138     return Collections.emptyList();
139   }
140
141   public void compileFinished() {
142     FileUtil.asyncDelete(myTempFiles);
143     myTempFiles.clear();
144   }
145
146   private void readProcessOutput(final Process process) throws IOException {
147     try {
148       final InputStreamReader reader = new InputStreamReader(process.getInputStream());
149       try {
150         final StringBuilder builder = new StringBuilder();
151         final char[] buf = new char[2048];
152         int read = reader.read(buf);
153         while (read >= 0) {
154           final String output = new String(buf, 0, read);
155           builder.append(output);
156           read = reader.read(buf);
157         }
158         handleOutput(builder.toString());
159       }
160       finally {
161         cancel(process);
162         reader.close();
163       }
164     }
165     catch (IOException e) {
166       myContext.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
167     }
168   }
169
170   private static void cancel(Process process) {
171     if (process != null) {
172       process.destroy();
173     }
174   }
175
176   private void handleOutput(final String output) {
177     // TODO: better errors parsing
178     LOG.debug("Compiler output: " + output);
179     final Pattern errorPattern = Pattern.compile("((?:[A-Z]:\\\\)?[\\.\\w\\\\/]*):(\\d*): (.*)");
180     final String[] outputLines = output.split("[\\n\\r]");
181     for (String outputLine : outputLines) {
182       if (StringUtil.isEmptyOrSpaces(outputLine)) {
183         continue;
184       }
185       final Matcher matcher = errorPattern.matcher(outputLine);
186       if (matcher.matches()) {
187         final String file = matcher.group(1);
188         final String line = matcher.group(2);
189         String message = matcher.group(3);
190         final String column = "0";
191
192         CompilerMessageCategory messageCategory = CompilerMessageCategory.ERROR;
193         if (StringUtil.startsWith(message, "warning:")) {
194           messageCategory = CompilerMessageCategory.WARNING;
195           message = message.substring(9);
196         }
197         final ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
198         final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(file);
199         final VirtualFile relativeFile = VfsUtil.findRelativeFile(file, projectFileIndex.getSourceRootForFile(virtualFile));
200         myContext.addMessage(messageCategory, message, relativeFile != null ? relativeFile.getUrl() : null,
201                              line != null ? Integer.parseInt(line) : 0, column != null ? Integer.parseInt(column) : 0);
202         LOG.debug("Message: " + message);
203       }
204       else {
205         if (output.indexOf(": invalid flag:") != -1) {
206             myContext.addMessage(CompilerMessageCategory.ERROR, output, null, -1, -1);
207         } else {
208             myContext.addMessage(CompilerMessageCategory.INFORMATION, output, null, -1, -1);
209         }
210       }
211     }
212   }
213 }