View Javadoc

1   /*
2   	Copyright (c) 2009 Olivier Chafik, All Rights Reserved
3   	
4   	This file is part of JNAerator (http://jnaerator.googlecode.com/).
5   	
6   	JNAerator is free software: you can redistribute it and/or modify
7   	it under the terms of the GNU General Public License as published by
8   	the Free Software Foundation, either version 3 of the License, or
9   	(at your option) any later version.
10  	
11  	JNAerator is distributed in the hope that it will be useful,
12  	but WITHOUT ANY WARRANTY; without even the implied warranty of
13  	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  	GNU General Public License for more details.
15  	
16  	You should have received a copy of the GNU General Public License
17  	along with JNAerator.  If not, see <http://www.gnu.org/licenses/>.
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  		//public final String bootclasspath;
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/*, String bootclasspath*/) {
44  			super(text);
45  			this.diagnostics = diagnostics;
46  			this.inputs = inputs;
47  			this.compilerClass = compilerClass;
48  			//this.bootclasspath = bootclasspath;
49  		}
50  		public static void throwErrors(List<Diagnostic<? extends JavaFileObject>> diagnostics, Map<String, MemoryJavaFile> inputs, String compilerClass/*, String bootclasspath*/) throws CompilationError, IOException {
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");//.toUri());
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  				//System.out.println("Error on line " + diagnostic.getLineNumber() + ":" + diagnostic.getColumnNumber() + " in " + (diagnostic.getSource() == null ? "<unknown source>" : diagnostic.getSource().getName()) + ": " + diagnostic.getMessage(Locale.getDefault()));
71  			}
72  			if (errors.isEmpty())
73  				return;
74  
75  			throw new CompilationError(sb.toString(), errors, inputs, compilerClass/*, bootclasspath*/);
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  //			if (resstr.contains("Prog/"))
84  //				resstr = "jar:http://ochafik.free.fr/Java/jnaerator.jar!/...";
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  		if (resource != null) {
97  			String resstr = resource.toString();
98  			if (resstr.matches("jar:file:.*!.*"))
99  				return resstr.substring("jar:file:".length(), resstr.indexOf("!"));
100 			else if (resstr.matches("jar:http:.*!.*"))
101 				return resstr.substring("jar:".length(), resstr.indexOf("!"));
102 			else {
103 				String p = '/' + c.getName().replace('.', '/') + ".class";
104 				if (resstr.endsWith(p))
105 					return resstr.substring(0, resstr.length() - p.length());
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 					//System.out.print("Downloading file '" + remoteStr + "'...");
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 		//JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
168 		//System.out.println("compiler = " + (compiler == null ? "<none found>" : compiler.getClass().getName()));
169 		Set<String> bootclasspaths = getClassPaths(cacheDirectory, classpathHints);
170 		bootclasspaths.addAll(getClassPaths(cacheDirectory, String.class));
171 		String bootclasspath = StringUtils.implode(bootclasspaths, File.pathSeparator);
172 		//System.out.println("bootclasspath = " + bootclasspath);
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, //"/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/classes.jar",//bootclasspath,
178 			"-classpath", bootclasspath //"/Users/ochafik/Prog/Java/bin/jnaerator.jar"//
179 				//"http://ochafik.free.fr/Java/jnaerator.jar"//bootclasspath
180 		);  
181 //		DebugUtils.println(fileManager.inputs.values());
182 		compiler.getTask(null, fileManager, diagnostics, options, null, fileObjects).call();
183 		
184 //		for (final Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
185 //			if (diagnostic == null)
186 //				continue;
187 //			//diagnostic.getKind()
188 //			//System.out.format("Error on line %d in %d%n", diagnostic.getLineNumber(), diagnostic.getSource());//.toUri());
189 //			if (diagnostic.getKind() == Kind.ERROR) {
190 //				System.err.println("\n" +  diagnostic.getSource().toUri() + ":");
191 //				System.err.println(RegexUtils.regexReplace(Pattern.compile("\n"), "\n" +  diagnostic.getSource().getCharContent(true), new Adapter<String[], String>() {
192 //					int line = 0;
193 //
194 //					@Override
195 //					public String adapt(String[] value) {
196 //						line++;
197 //						return "\n" + line + ":" + (diagnostic.getLineNumber() == line ? ">>>" : "") +"\t\t";
198 //					}
199 //				}));
200 //			}
201 ////				System.out.println("Error on line " + diagnostic.getLineNumber() + ":" + diagnostic.getColumnNumber() + " in " + (diagnostic.getSource() == null ? "<unknown source>" : diagnostic.getSource().getName()) + ": " + diagnostic.getMessage(Locale.getDefault()));
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 				//diagnostic.getKind()
235 				//System.out.format("Error on line %d in %d%n", diagnostic.getLineNumber(), diagnostic.getSource());//.toUri());
236 				System.out.format("Error on line " + diagnostic.getLineNumber() + ":" + diagnostic.getLineNumber() + " in " + diagnostic.getSource());//.toUri());
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