1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.ochafik.lang.compiler;
20
21 import java.util.*;
22 import java.util.regex.Pattern;
23 import java.io.*;
24 import java.net.MalformedURLException;
25 import java.net.URL;
26 import java.net.URLConnection;
27 import java.net.URLDecoder;
28
29 import javax.tools.*;
30 import javax.tools.Diagnostic.Kind;
31
32 import com.ochafik.io.IOUtils;
33 import com.ochafik.util.listenable.Adapter;
34 import com.ochafik.util.string.RegexUtils;
35 import com.ochafik.util.string.StringUtils;
36
37 public class CompilerUtils {
38 public static class CompilationError extends IOException {
39 public final String compilerClass;
40
41 public final List<Diagnostic<? extends JavaFileObject>> diagnostics;
42 public final Map<String, MemoryJavaFile> inputs;
43 private CompilationError(String text, List<Diagnostic<? extends JavaFileObject>> diagnostics, Map<String, MemoryJavaFile> inputs, String compilerClass
44 super(text);
45 this.diagnostics = diagnostics;
46 this.inputs = inputs;
47 this.compilerClass = compilerClass;
48
49 }
50 public static void throwErrors(List<Diagnostic<? extends JavaFileObject>> diagnostics, Map<String, MemoryJavaFile> inputs, String compilerClass
51 List<Diagnostic<? extends JavaFileObject>> errors = new ArrayList<Diagnostic<? extends JavaFileObject>>(diagnostics.size());
52 StringBuilder sb = new StringBuilder();
53
54 for (final Diagnostic<? extends JavaFileObject> diagnostic : diagnostics) {
55 if (diagnostic == null)
56 continue;
57 if (diagnostic.getKind() == Kind.ERROR) {
58 errors.add(diagnostic);
59 sb.append("Error in " + diagnostic.getSource().toUri() + " at line " + diagnostic.getLineNumber() + ", col " + diagnostic.getColumnNumber() + " :\n\t" + diagnostic.getMessage(Locale.getDefault()) + "\n");
60 sb.append(RegexUtils.regexReplace(Pattern.compile("\n"), "\n" + diagnostic.getSource().getCharContent(true), new Adapter<String[], String>() {
61 int line = 0;
62
63 @Override
64 public String adapt(String[] value) {
65 line++;
66 return "\n" + line + ":" + (diagnostic.getLineNumber() == line ? ">>>" : "") +"\t\t";
67 }
68 }) + "\n");
69 }
70
71 }
72 if (errors.isEmpty())
73 return;
74
75 throw new CompilationError(sb.toString(), errors, inputs, compilerClass
76 }
77 }
78 public static String getClassPath(Class<?> c, File cacheDirectory) throws MalformedURLException, IOException {
79
80 URL resource = c.getResource(c.getSimpleName() + ".class");
81 if (resource != null) {
82 String resstr = resource.toString();
83
84
85
86 if (resstr.matches("jar:.*!.*"))
87 resstr = resstr.substring("jar:".length(), resstr.indexOf("!"));
88 else {
89 String p = '/' + c.getName().replace('.', '/') + ".class";
90 if (resstr.endsWith(p))
91 resstr = resstr.substring(0, resstr.length() - p.length());
92 }
93 return getLocalFile(new URL(resstr), cacheDirectory).toString();
94 }
95
96
97
98
99
100
101
102
103
104
105
106
107
108 return null;
109 }
110 public static Set<String> getClassPaths(File cacheDirectory, Class<?>... cs) throws MalformedURLException, IOException {
111 Set<String> ret = new TreeSet<String>();
112 for (Class<?> c : cs) {
113 String cp ;
114 if (c == null || (cp = getClassPath(c, cacheDirectory)) == null)
115 continue;
116 ret.add(cp);
117 }
118 return ret;
119 }
120 static Map<String, File> localURLCaches = new HashMap<String, File>();
121 static File getLocalFile(URL remoteFile, File cacheDirectory) throws IOException {
122 if ("file".equals(remoteFile.getProtocol()))
123 return new File(URLDecoder.decode(remoteFile.getFile(), "utf-8"));
124
125 String remoteStr = remoteFile.toString();
126 File f = localURLCaches.get(remoteStr);
127 if (f == null) {
128 String fileName = new File(remoteStr).getName();
129 URLConnection con = null;
130 try {
131 con = remoteFile.openConnection();
132 } catch (IOException ex) {
133 ex.printStackTrace();
134 }
135 if (cacheDirectory != null) {
136 f = new File(cacheDirectory, fileName);
137 if (f.exists() && (con == null || f.lastModified() > con.getLastModified())) {
138 System.out.println("Reusing cached file " + f);
139 if (con != null)
140 con.getInputStream().close();
141 return f;
142 }
143 } else {
144 f = File.createTempFile(fileName, ".jar");
145 f.deleteOnExit();
146 }
147 System.out.print("Downloading file " + remoteFile + " to " + f);
148
149 InputStream in = new BufferedInputStream(remoteFile.openStream());
150 try {
151 OutputStream out = new BufferedOutputStream(new FileOutputStream(f));
152 try {
153
154 long length = IOUtils.readWrite(in, out);
155 System.out.println(" OK (" + length + " bytes)");
156 localURLCaches.put(remoteStr, f.getAbsoluteFile());
157 } finally {
158 out.close();
159 }
160 } finally {
161 in.close();
162 }
163 }
164 return f;
165 }
166 public static void compile(JavaCompiler compiler, MemoryFileManager fileManager, DiagnosticCollector<JavaFileObject> diagnostics, String sourceCompatibility, File cacheDirectory, Class<?>...classpathHints) throws MalformedURLException, IOException {
167
168
169 Set<String> bootclasspaths = getClassPaths(cacheDirectory, classpathHints);
170 bootclasspaths.addAll(getClassPaths(cacheDirectory, String.class));
171 String bootclasspath = StringUtils.implode(bootclasspaths, File.pathSeparator);
172
173 Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects();
174 List<String> options = sourceCompatibility == null ? null : Arrays.asList(
175 "-target", sourceCompatibility,
176 "-source", sourceCompatibility,
177 "-bootclasspath", bootclasspath,
178 "-classpath", bootclasspath
179
180 );
181
182 compiler.getTask(null, fileManager, diagnostics, options, null, fileObjects).call();
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 }
204
205 public static JavaCompiler getJavaCompiler(boolean preferJavac) throws FileNotFoundException {
206 JavaCompiler compiler;
207 if (preferJavac) {
208 compiler = ToolProvider.getSystemJavaCompiler();
209 if (compiler != null)
210 return compiler;
211 }
212 try {
213 compiler = (JavaCompiler)Class.forName("org.eclipse.jdt.internal.compiler.tool.EclipseCompiler").newInstance();
214 } catch (Exception e) {
215 compiler = ToolProvider.getSystemJavaCompiler();
216 if (compiler == null)
217 throw new FileNotFoundException("No Java compiler found (not run from JDK, no Eclipse Compiler in classpath)");
218 }
219 return compiler;
220 }
221 public static void main2(String[] args) {
222 try {
223 String jarOut = args.length == 0 ? "out.jar" : args[0];
224
225 JavaCompiler compiler = getJavaCompiler(false);
226
227 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
228 MemoryFileManager fileManager = new MemoryFileManager(compiler.getStandardFileManager(diagnostics, null, null));
229 fileManager.addSourceInput("test/Main.java", "package test; public class Main { }");
230 fileManager.close();
231
232 compile(compiler, fileManager, diagnostics, null, null);
233 for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
234
235
236 System.out.format("Error on line " + diagnostic.getLineNumber() + ":" + diagnostic.getLineNumber() + " in " + diagnostic.getSource());
237 }
238
239 boolean outputSources = true;
240 System.out.println("Writing " + jarOut + (outputSources ? " with" : " without") + " sources");
241 fileManager.writeJar(new FileOutputStream(jarOut), outputSources, null);
242
243 } catch (IOException ex) {
244 ex.printStackTrace();
245 }
246 }
247 }
248