All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kilim.tools.Javac Maven / Gradle / Ivy

Go to download

Coroutines, continuations, fibers, actors and message passing for the JVM

There is a newer version: 2.0.2-jdk7
Show newest version
// Copyright 2011 by sriram - offered under the terms of the MIT License

package kilim.tools;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

import kilim.analysis.ClassInfo;

/**
 * Simple utility class to invoke the java compiler.
 */

public class Javac {

    /**
     * Given a list of file-scope java code (equivalent to a .java file, including package and
     * import declarations), compile() invokes javac to compile them, produce classfiles and return
     * a list of  pairs.
     * 
     * compile() dumps the source strings into their respective files, has javac compile them, then
     * reads back the equivalent class files. The name of the source file is gleaned from the string
     * itself; a string containing "public class Foo" is stored in tmpDir/Foo.java (where tmpDir is
     * a temporary directory that's deleted after the compilation), and if no public class or
     * interface is found, the name of the first class in the string is used.
     * 
     * Note that the list of returned classes may be larger than list of sources
     * 
     * Note: the java compiler api is ill-defined and this class should not be considered production.
     * specifically, the classpath appears to depend on the execution environment,
     * eg command line maven vs IDE vs the java command line
     * 
     * @param srcCodes
     *            . List of strings.
     * @return List. className is fully qualified, and byte[] contains the
     *         bytecode of the class.
     * @throws IOException
     */
    public static List compile(List srcCodes) throws IOException {

        List srcInfos = getSourceInfos(srcCodes);

        File rootDir = getTmpDir(); // something like "/tmp/kilim$2348983948"

        File classDir = new File(rootDir.getAbsolutePath() + File.separatorChar + "classes");
        classDir.mkdir(); // "/classes"
        String cp = getClassPath();

        ArrayList
                args = new ArrayList();
        add(args, "-d", classDir.getAbsolutePath());
        if (cp != null)
            add(args, "-cp", cp);
        for (SourceInfo srci : srcInfos) {
            String name = rootDir.getAbsolutePath() + File.separatorChar + srci.className + ".java";
            writeFile(new File(name), srci.srcCode.getBytes());
            args.add(name);
        }
        String [] arguments = args.toArray(new String[0]);

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        compiler.run(null, null, null, arguments);

        List ret = new ArrayList();
        addClasses(ret, "", classDir);
        deleteDir(rootDir);
        return ret;
    }
    static void add(ArrayList list,String ... vals) {
        for (String val : vals)
            list.add(val);
    }

    /** 
     * the compiler classpath appears to depend on the class.path system variable which changes 
     * depending on the execution environment, eg command line maven vs command line java vs IDE vs ant
     * to limit this dependence, generate a classpath from the class loader urls instead
     */
    static String getClassPath() {
        String cp = "";
        ClassLoader sys = ClassLoader.getSystemClassLoader();
        ClassLoader cl = Javac.class.getClassLoader();
        // FIXME::rhetorical - what order should the classpath be ?
        //   the reality is that calling the compiler cannot be 100% robust
        //   so this detail is likely insignificant
        for (; cl != null & cl != sys; cl = cl.getParent())
            if (cl instanceof java.net.URLClassLoader) {
                java.net.URLClassLoader ucl = (java.net.URLClassLoader) cl;
                for (java.net.URL url : ucl.getURLs())
                    cp += File.pathSeparator + url.getPath();
            }
        return cp.length()==0 ? null : cp.substring(1);
    }
    
    private static List getSourceInfos(List srcCodes) {
        List srcInfos = new ArrayList(srcCodes.size());
        for (String srcCode : srcCodes) {
            srcInfos.add(getSourceInfo(srcCode));
        }
        return srcInfos;
    }

    static Pattern publicClassNameRegexp = Pattern.compile("public +(?:class|interface) +(\\w+)");
    static Pattern classNameRegexp = Pattern.compile("(?:class|interface) +(\\w+)");

    private static SourceInfo getSourceInfo(String srcCode) {
        Matcher m = publicClassNameRegexp.matcher(srcCode);
        if (m.find())
            return new SourceInfo(m.group(1), srcCode);
        else {
            m = classNameRegexp.matcher(srcCode);
            if (m.find())
                return new SourceInfo(m.group(1), srcCode);
            else
                throw new IllegalArgumentException(
                        "No class or interface definition found in src: \n'" + srcCode + "'");
        }
    }

    private static File getTmpDir() throws IOException {
        String tmpDirName = System.getProperty("java.io.tmpdir");
        if (tmpDirName == null) {
            tmpDirName = "";
        } else {
            tmpDirName += File.separator;
        }
        Random r = new Random();
        String name = tmpDirName + "kilim$" + r.nextLong();
        File rootDir = new File(name);
        if (!rootDir.mkdir()) {
            throw new IOException("Unable to make tmp directory " + rootDir.getAbsolutePath());
        }
        return rootDir;
    }

    private static void deleteDir(File rootDir) {
        for (File f : rootDir.listFiles()) {
            if (f.isDirectory()) {
                deleteDir(f);
            } else {
                if (!f.delete()) {
                    System.err.println("Unable to delete " + f.getAbsolutePath());
                }
            }
        }
        if (!rootDir.delete()) {
            System.err.println("Unable to delete " + rootDir.getAbsolutePath());
        }
    }

    private static void addClasses(List ret, String pkgName, File dir)
            throws IOException {
        for (File f : dir.listFiles()) {
            String fname = f.getName();
            if (f.isDirectory()) {
                String qname = pkgName + fname + ".";
                addClasses(ret, qname, f);
            } else if (fname.endsWith(".class")) {
                String qname = pkgName + fname.substring(0, fname.length() - 6);
                ret.add(new ClassInfo(qname, readFile(f)));
            } else {
                System.err.println("Unexpected file : " + f.getAbsolutePath());
            }
        }
    }

    private static byte[] readFile(File f) throws IOException {
        int len = (int) f.length();
        byte[] buf = new byte[len];
        FileInputStream fis = new FileInputStream(f);
        int off = 0;
        while (len > 0) {
            int n = fis.read(buf, off, len);
            if (n == -1)
                throw new IOException("Unexpected EOF reading " + f.getAbsolutePath());
            off += n;
            len -= n;
        }
        return buf;
    }

    private static void writeFile(File f, byte[] srcCode) throws IOException {
        FileOutputStream fos = new FileOutputStream(f);
        fos.write(srcCode);
        fos.close();
    }

    private static class SourceInfo {
        public SourceInfo(String nm, String code) {
            className = nm;
            srcCode = code;
        }

        public String className;
        public String srcCode;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy