view graal/com.oracle.max.base/src/com/sun/max/ide/ToolChain.java @ 4142:bc8527f3071c

Adjust code base to new level of warnings.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Sun, 18 Dec 2011 05:24:06 +0100
parents e233f5660da4
children
line wrap: on
line source

/*
 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.max.ide;

import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;

import javax.tools.*;
import javax.tools.JavaCompiler.*;

import com.sun.max.program.*;

/**
 * Provides an interface for invoking a Java source compiler (e.g. javac) and a C header and stub generator (i.e.
 * javah). The Java source compiler used is determined the <a
 * href="http://java.sun.com/javase/6/docs/api/java/util/ServiceLoader.html">service provider mechanism</a>. For
 * example, if you want to use the Eclipse batch compiler, then simply place the stand alone ecj.jar JAR file on the
 * classpath.
 */
public final class ToolChain {

    private ToolChain() {
    }

    private static JavaCompiler javaCompiler;

    private static JavaCompiler javaCompiler() {
        if (javaCompiler == null) {
            final Iterator<JavaCompiler> iterator = ServiceLoader.load(JavaCompiler.class).iterator();
            if (iterator.hasNext()) {
                javaCompiler = iterator.next();
            } else {
                javaCompiler = ToolProvider.getSystemJavaCompiler();
            }
            ProgramError.check(javaCompiler != null, "Cannot find a Java compiler");
        }
        return javaCompiler;
    }

    /**
     * Compiles the source for a given class. The location of the source file to be compiled and the directory to which
     * the output class files are to be written are determined by the current {@link JavaProject} context.
     * <p>
     * The supported {@code options} are:
     * <p>
     *
     * <pre>
     *     -noinlinejsr    implement {@code finally} clauses using the {@link Bytecode#JSR} and {@link Bytecode#RET} bytecodes
     * </pre>
     *
     *
     * @param projClass a class denoting a project (i.e. any class in the project)
     * @param className the name of the class to be compiled
     * @param options options for modifying the compilation
     * @return true if the compilation succeeded without any errors, false otherwise
     */
    public static boolean compile(Class projClass, String className, String... options) {
        return compile(projClass, new String[] {className}, options);
    }

    /**
     * Compiles the source for one or more given classes. The location of the source files to be compiled and the
     * directory to which the output class files are to be written are determined by a given project
     * context.
     * <p>
     * The supported {@code options} are:
     * <p>
     *
     * <pre>
     *     -noinlinejsr    implement {@code finally} clauses using the {@link Bytecode#JSR} and {@link Bytecode#RET} bytecodes
     * </pre>
     *
     *
     * @param projClass a class denoting a project (i.e. any class in the project)
     * @param className the name of the class to be compiled
     * @param options options for modifying the compilation
     * @return true if the compilation succeeded without any errors, false otherwise
     */
    public static boolean compile(Class projClass, String[] classNames, String... options) {

        final Classpath classPath = JavaProject.getClassPath(projClass, true);
        final Classpath sourcePath = JavaProject.getSourcePath(projClass, true);
        final String outputDirectory = classPath.entries().get(0).toString();

        final ArrayList<File> sourceFiles = new ArrayList<>(classNames.length);
        for (String className : classNames) {
            final String sourceFilePathSuffix = className.replace('.', File.separatorChar) + ".java";
            final File sourceFile = sourcePath.findFile(sourceFilePathSuffix);
            if (sourceFile == null) {
                ProgramWarning.message("Could not find source file for " + className);
                return false;
            }
            sourceFiles.add(sourceFile);
        }

        final JavaCompiler compiler = javaCompiler();
        final String compilerName = compiler.getClass().getName();
        final List<String> opts = new ArrayList<>(Arrays.asList(new String[] {"-cp", classPath.toString(), "-d", outputDirectory}));
        if (compilerName.equals("com.sun.tools.javac.api.JavacTool")) {
            opts.add("-cp");
            opts.add(classPath.toString());
            opts.add("-d");
            opts.add(outputDirectory);
            for (String option : options) {
                if (option.equals("-noinlinejsr")) {
                    opts.add("-source");
                    opts.add("1.4");
                    opts.add("-target");
                    opts.add("1.4");
                    opts.add("-XDjsrlimit=0");
                } else {
                    throw new IllegalArgumentException("Unsupported compiler option " + option);
                }
            }
        } else if (compiler.getClass().getName().equals("org.eclipse.jdt.internal.compiler.tool.EclipseCompiler")) {
            opts.add("-cp");
            opts.add(classPath.toString());
            opts.add("-d");
            opts.add(outputDirectory);
            opts.add("-noExit");
            boolean inlineJSR = true;
            for (String option : options) {
                if (option.equals("-noinlinejsr")) {
                    inlineJSR = false;
                } else {
                    throw new IllegalArgumentException("Unsupported compiler option " + option);
                }
            }
            if (inlineJSR) {
                opts.add("-inlineJSR");
            } else {
                opts.add("-source");
                opts.add("1.4");
                opts.add("-target");
                opts.add("1.4");
            }
        } else {
            ProgramWarning.message("Unknown Java compiler may not accept same command line options as javac: " + compilerName);
            opts.add("-cp");
            opts.add(classPath.toString());
            opts.add("-d");
            opts.add(outputDirectory);
        }

        final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
        final Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFiles);
        final CompilationTask task = compiler.getTask(null, fileManager, diagnostics, opts, null, compilationUnits);
        final boolean result = task.call();
        final Set<String> reportedDiagnostics = new HashSet<>();
        for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
            final String message = diagnostic.getMessage(Locale.getDefault());
            if (!reportedDiagnostics.contains(message)) {
                reportedDiagnostics.add(message);
                if (!message.contains("is Sun proprietary API and may be removed in a future release")) {
                    System.err.println(message);
                }
            }
        }
        try {
            fileManager.close();
        } catch (IOException e) {
            ProgramWarning.message("Error closing file manager: " + e);
        }
        return result;
    }

    private static Method javahMainMethod;

    private static Method javah() {
        if (javahMainMethod == null) {
            Class<?> javahMainClass = null;
            try {
                // On the Apple JDKs, there is no tools.jar: all the tools are in classes.jar
                javahMainClass = Class.forName("com.sun.tools.javah.Main");
            } catch (ClassNotFoundException classNotFoundException) {
                // This is expected on non-Apple JDKs
                final ClassLoader systemToolClassLoader = ToolProvider.getSystemToolClassLoader();
                ProgramError.check(systemToolClassLoader != null, "Cannot find the standard system tools class loader");
                try {
                    javahMainClass = Class.forName("com.sun.tools.javah.Main", true, systemToolClassLoader);
                    final URLClassLoader urlClassLoader = (URLClassLoader) javahMainClass.getClassLoader();
                    updateJavaClassPath(urlClassLoader);
                } catch (Exception exception) {
                    ProgramWarning.message("Cannot find or initialize javah: " + exception);
                }
            }
            try {
                if (javahMainClass != null) {
                    javahMainMethod = javahMainClass.getDeclaredMethod("main", String[].class);
                }
            } catch (Exception exception) {
                ProgramWarning.message("Cannot find or initialize javah: " + exception);
            }
        }
        return javahMainMethod;
    }

    /**
     * This hack is necessary as javah uses a doclet to do its actual work. The doclet class is found by creating a URL class loader
     * from the system property "java.class.path" which does not include the path to tools.jar for
     * standard JDK installations.
     */
    private static void updateJavaClassPath(final URLClassLoader urlClassLoader) {
        String javaClassPath = System.getProperty("java.class.path", ".");
        for (URL url : urlClassLoader.getURLs()) {
            final String path = url.getPath();
            if (path != null && !path.isEmpty()) {
                final File file = new File(path);
                if (file.exists()) {
                    javaClassPath += File.pathSeparator + file.getAbsolutePath();
                }
            }
        }
        System.setProperty("java.class.path", javaClassPath);
    }

    public static boolean javah(String[] args) {
        try {
            javah().invoke(null, (Object) args);
            return true;
        } catch (InvocationTargetException e) {
            ProgramWarning.message("Error invoking javah: " + e.getTargetException());
        } catch (Exception e) {
            ProgramWarning.message("Error invoking javah: " + e);
        }
        return false;
    }

    private static Method javapMainMethod;

    private static Method javap() {
        if (javapMainMethod == null) {
            Class<?> javapMainClass = null;
            try {
                // On the Apple JDKs, there is no tools.jar: all the tools are in classes.jar
                javapMainClass = Class.forName("sun.tools.javap.Main");
            } catch (ClassNotFoundException classNotFoundException) {
                // This is expected on non-Apple JDKs
                final ClassLoader systemToolClassLoader = ToolProvider.getSystemToolClassLoader();
                ProgramError.check(systemToolClassLoader != null, "Cannot find the standard system tools class loader");
                try {
                    javapMainClass = Class.forName("sun.tools.javap.Main", true, systemToolClassLoader);
                } catch (Exception exception) {
                    ProgramWarning.message("Cannot find or initialize javap: " + exception);
                }
            }
            try {
                if (javapMainClass != null) {
                    javapMainMethod = javapMainClass.getDeclaredMethod("main", String[].class);
                }
            } catch (Exception exception) {
                ProgramWarning.message("Cannot find or initialize javap: " + exception);
            }
        }
        return javapMainMethod;
    }

    public static boolean javap(String[] args) {
        try {
            javap().invoke(null, (Object) args);
            return true;
        } catch (InvocationTargetException e) {
            ProgramWarning.message("Error invoking javap: " + e.getTargetException());
        } catch (Exception e) {
            ProgramWarning.message("Error invoking javap: " + e);
        }
        return false;
    }
}