001    /*
002            Copyright (c) 2009 Olivier Chafik, All Rights Reserved
003            
004            This file is part of JNAerator (http://jnaerator.googlecode.com/).
005            
006            JNAerator is free software: you can redistribute it and/or modify
007            it under the terms of the GNU General Public License as published by
008            the Free Software Foundation, either version 3 of the License, or
009            (at your option) any later version.
010            
011            JNAerator is distributed in the hope that it will be useful,
012            but WITHOUT ANY WARRANTY; without even the implied warranty of
013            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014            GNU General Public License for more details.
015            
016            You should have received a copy of the GNU General Public License
017            along with JNAerator.  If not, see <http://www.gnu.org/licenses/>.
018    */
019    package com.ochafik.lang.compiler;
020    
021    import java.util.*;
022    import java.util.regex.Pattern;
023    import java.io.*;
024    import java.net.MalformedURLException;
025    import java.net.URL;
026    import java.net.URLConnection;
027    import java.net.URLDecoder;
028    
029    import javax.tools.*;
030    import javax.tools.Diagnostic.Kind;
031    
032    import com.ochafik.io.IOUtils;
033    import com.ochafik.util.listenable.Adapter;
034    import com.ochafik.util.string.RegexUtils;
035    import com.ochafik.util.string.StringUtils;
036    
037    public class CompilerUtils {
038            
039            public static String getClassPath(Class<?> c, File cacheDirectory) throws MalformedURLException, IOException {
040    
041                    URL resource = c.getResource(c.getSimpleName() + ".class");
042                    if (resource != null) {
043                            String resstr = resource.toString();
044    //                      if (resstr.contains("Prog/"))
045    //                              resstr = "jar:http://ochafik.free.fr/Java/jnaerator.jar!/...";
046                            
047                            if (resstr.matches("jar:.*!.*"))
048                                    resstr = resstr.substring("jar:".length(), resstr.indexOf("!"));
049                            else {
050                                    String p = '/' + c.getName().replace('.', '/') + ".class";
051                                    if (resstr.endsWith(p))
052                                            resstr = resstr.substring(0, resstr.length() - p.length());
053                            }
054                            return getLocalFile(new URL(resstr), cacheDirectory).toString();
055                    }
056                    /*
057                    if (resource != null) {
058                            String resstr = resource.toString();
059                            if (resstr.matches("jar:file:.*!.*"))
060                                    return resstr.substring("jar:file:".length(), resstr.indexOf("!"));
061                            else if (resstr.matches("jar:http:.*!.*"))
062                                    return resstr.substring("jar:".length(), resstr.indexOf("!"));
063                            else {
064                                    String p = '/' + c.getName().replace('.', '/') + ".class";
065                                    if (resstr.endsWith(p))
066                                            return resstr.substring(0, resstr.length() - p.length());
067                            }
068                    }*/
069                    return null;
070            }
071            public static Set<String> getClassPaths(File cacheDirectory, Class<?>... cs) throws MalformedURLException, IOException {
072                    Set<String> ret = new TreeSet<String>();
073                    for (Class<?> c : cs) {
074                            String cp ;
075                            if (c == null || (cp = getClassPath(c, cacheDirectory)) == null)
076                                    continue;
077                            ret.add(cp);
078                    }
079                    return ret;
080            }
081            static Map<String, File> localURLCaches = new HashMap<String, File>();
082            static File getLocalFile(URL remoteFile, File cacheDirectory) throws IOException {
083                    if ("file".equals(remoteFile.getProtocol()))
084                            return new File(URLDecoder.decode(remoteFile.getFile(), "utf-8"));
085                    
086                    String remoteStr = remoteFile.toString();
087                    File f = localURLCaches.get(remoteStr);
088                    if (f == null) {
089                            String fileName = new File(remoteStr).getName();
090                            URLConnection con = null;
091                            try {
092                                    con = remoteFile.openConnection();
093                            } catch (IOException ex) {
094                                    ex.printStackTrace();
095                            }
096                            if (cacheDirectory != null) {
097                                    f = new File(cacheDirectory, fileName);
098                                    if (f.exists() && (con == null || f.lastModified() > con.getLastModified())) {
099                                            System.out.println("Reusing cached file " + f);
100                                            if (con != null)
101                                                    con.getInputStream().close();
102                                            return f;
103                                    }
104                            } else {
105                                    f = File.createTempFile(fileName, ".jar");
106                                    f.deleteOnExit();
107                            }
108                            System.out.print("Downloading file " + remoteFile + " to " + f);
109                            
110                            InputStream in = new BufferedInputStream(remoteFile.openStream());
111                            try {
112                                    OutputStream out = new BufferedOutputStream(new FileOutputStream(f));
113                                    try {
114                                            //System.out.print("Downloading file '" + remoteStr + "'...");
115                                            long length = IOUtils.readWrite(in, out);
116                                            System.out.println(" OK (" + length + " bytes)");
117                                            localURLCaches.put(remoteStr, f.getAbsoluteFile());
118                                    } finally {
119                                            out.close();
120                                    }
121                            } finally {
122                                    in.close();
123                            }
124                    }
125                    return f;
126            }
127            public static void compile(JavaCompiler compiler, MemoryFileManager fileManager, DiagnosticCollector<JavaFileObject> diagnostics, String sourceCompatibility, File cacheDirectory, Class<?>...classpathHints) throws MalformedURLException, IOException {
128                    //JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
129                    System.out.println("compiler = " + (compiler == null ? "<none found>" : compiler.getClass().getName()));
130                    Set<String> bootclasspaths = getClassPaths(cacheDirectory, classpathHints);
131                    bootclasspaths.addAll(getClassPaths(cacheDirectory, String.class));
132                    String bootclasspath = StringUtils.implode(bootclasspaths, File.pathSeparator);
133                    System.out.println("bootclasspath = " + bootclasspath);
134                    Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects();  
135                    List<String> options = sourceCompatibility == null ? null : Arrays.asList(
136                            "-target", sourceCompatibility, 
137                            "-source", sourceCompatibility,
138                            "-bootclasspath", bootclasspath, //"/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/classes.jar",//bootclasspath,
139                            "-classpath", bootclasspath //"/Users/ochafik/Prog/Java/bin/jnaerator.jar"//
140                                    //"http://ochafik.free.fr/Java/jnaerator.jar"//bootclasspath
141                    );  
142    //              DebugUtils.println(fileManager.inputs.values());
143                    compiler.getTask(null, fileManager, diagnostics, options, null, fileObjects).call();
144                    
145    //              for (final Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
146    //                      if (diagnostic == null)
147    //                              continue;
148    //                      //diagnostic.getKind()
149    //                      //System.out.format("Error on line %d in %d%n", diagnostic.getLineNumber(), diagnostic.getSource());//.toUri());
150    //                      if (diagnostic.getKind() == Kind.ERROR) {
151    //                              System.err.println("\n" +  diagnostic.getSource().toUri() + ":");
152    //                              System.err.println(RegexUtils.regexReplace(Pattern.compile("\n"), "\n" +  diagnostic.getSource().getCharContent(true), new Adapter<String[], String>() {
153    //                                      int line = 0;
154    //
155    //                                      @Override
156    //                                      public String adapt(String[] value) {
157    //                                              line++;
158    //                                              return "\n" + line + ":" + (diagnostic.getLineNumber() == line ? ">>>" : "") +"\t\t";
159    //                                      }
160    //                              }));
161    //                      }
162    ////                            System.out.println("Error on line " + diagnostic.getLineNumber() + ":" + diagnostic.getColumnNumber() + " in " + (diagnostic.getSource() == null ? "<unknown source>" : diagnostic.getSource().getName()) + ": " + diagnostic.getMessage(Locale.getDefault()));
163    //              }
164            }
165            
166            public static JavaCompiler getJavaCompiler(boolean preferJavac) throws FileNotFoundException {
167                    JavaCompiler compiler;
168                    if (preferJavac) {
169                            compiler = ToolProvider.getSystemJavaCompiler();
170                            if (compiler != null)
171                                    return compiler;
172                    }
173                    try {
174                            compiler = (JavaCompiler)Class.forName("org.eclipse.jdt.internal.compiler.tool.EclipseCompiler").newInstance();
175                    } catch (Exception e) {
176                            compiler = ToolProvider.getSystemJavaCompiler();
177                            if (compiler == null)
178                                    throw new FileNotFoundException("No Java compiler found (not run from JDK, no Eclipse Compiler in classpath)");
179                    }
180                    return compiler;
181            }
182            public static void main2(String[] args) {
183                    try {
184                            String jarOut = args.length == 0 ? "out.jar" : args[0];
185    
186                            JavaCompiler compiler = getJavaCompiler(false);
187                            
188                            DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
189                            MemoryFileManager fileManager = new MemoryFileManager(compiler.getStandardFileManager(diagnostics, null, null));
190                            fileManager.addSourceInput("test/Main.java", "package test; public class Main { }");
191                            fileManager.close();
192    
193                            compile(compiler, fileManager, diagnostics, null, null);
194                            for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
195                                    //diagnostic.getKind()
196                                    //System.out.format("Error on line %d in %d%n", diagnostic.getLineNumber(), diagnostic.getSource());//.toUri());
197                                    System.out.format("Error on line " + diagnostic.getLineNumber() + ":" + diagnostic.getLineNumber() + " in " + diagnostic.getSource());//.toUri());
198                            }
199    
200                            boolean outputSources = true;
201                            System.out.println("Writing " + jarOut + (outputSources ? " with" : " without") + " sources");
202                            fileManager.writeJar(new FileOutputStream(jarOut), outputSources, null);
203    
204                    } catch (IOException ex) {
205                            ex.printStackTrace();
206                    }
207            }
208    }
209