使用JavaFileManager编译项目

时间:2012-06-01 16:30:48

标签: java compiler-construction multiple-projects

我目前正在创建一个允许用户编译单个或多个项目的系统。我一直在做一些研究,并决定使用JavaCompilerAPI来做到这一点。我正在玩它并且设法获得单个java文件编译和单个java文件编译列表。

我无法做的是获得一个java项目编译,更不用说一组了。我在某处读到你可以使用JavaFileManager来做这个,我已经读了一下但是我找不到任何这样的例子,所以我卡住了。

这是我到目前为止所做的:

public List doCompilation(String sourceCode, String locationOfFile) {
   List<String> compile = new ArrayList<>();

    SimpleJavaFileObject fileObject = new DynamicJavaSourceCodeObject(locationOfFile, sourceCode);
    JavaFileObject javaFileObjects[] = new JavaFileObject[]{fileObject};

    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, Locale.getDefault(), null);

    Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(javaFileObjects);

    String[] compileOptions = new String[]{"-d", "C:/projects/Compiler/TempBINfolder/bin"};
    Iterable<String> compilationOptions = Arrays.asList(compileOptions);

    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

    CompilationTask compilerTask = compiler.getTask(null, stdFileManager, diagnostics, compilationOptions, null, compilationUnits);

    boolean status = compilerTask.call();

    if (!status) {//If compilation error occurs 
        // Iterate through each compilation problem and print it
        for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { 
            compile.add(diagnostic.getKind().toString()+" on line  "+ diagnostic.getLineNumber() +"\nIn file:  \n"+ diagnostic.toString()+"\n\n");
        }
    }
    try {
        stdFileManager.close();//Close the file manager
    } catch (IOException e) {
        e.printStackTrace();
    }

    return compile;
}

有人知道怎么做吗?

3 个答案:

答案 0 :(得分:3)

  • 尝试使用shrinkwrap,它非常易于使用...

答案 1 :(得分:1)

您可以在指定的项目目录下枚举文件(以及递归的目录,如果需要),并按照您现在的方式逐个编译它们。

答案 2 :(得分:1)

您需要在编译器类路径中包含输出目录。请注意我如何在下面的类路径中包含targetDirectory

/**
 * Compiles Java source-code.
 * <p/>
 * @author Gili Tzabari
 */
public final class JavaCompiler
{
    private List<Path> sourcePath = new ArrayList<>();
    private List<Path> classPath = new ArrayList<>();
    private final Set<DebugType> debugOptions = new HashSet<>(Arrays.asList(DebugType.LINES,
        DebugType.SOURCE, DebugType.VARIABLES));

    /**
     * Sets the compiler classpath.
     * <p/>
     * @param sourcePath the paths to search for the source format of dependent classes
     * @throws NullPointerException if sourcePath is null
     * @throws IllegalArgumentException if sourcePath refers to a non-existent path or a non-directory
     * @return the JavaCompiler
     */
    public JavaCompiler sourcePath(List<Path> sourcePath)
    {
        Preconditions.checkNotNull(sourcePath, "sourcePath may not be null");
        for (Path path: sourcePath)
        {
            if (!Files.exists(path))
            {
                throw new IllegalArgumentException("sourcePath refers to non-existant path: " + path.
                    toAbsolutePath());
            }
            if (!Files.isDirectory(path))
            {
                throw new IllegalArgumentException("sourcePath refers to a non-directory: " + path.
                    toAbsolutePath());
            }
        }

        this.sourcePath = ImmutableList.copyOf(sourcePath);
        return this;
    }

    /**
     * Sets the compiler classpath.
     * <p/>
     * @param classPath the paths to search for the compiled format of dependent classes
     * @throws NullPointerException if classPath is null
     * @throws IllegalArgumentException if classPath refers to a non-existent path
     * @return the JavaCompiler
     */
    public JavaCompiler classPath(List<Path> classPath)
    {
        Preconditions.checkNotNull(classPath, "classPath may not be null");
        for (Path path: classPath)
        {
            if (!Files.exists(path))
            {
                throw new IllegalArgumentException("classPath refers to non-existant path: " + path.
                    toAbsolutePath());
            }
        }

        this.classPath = ImmutableList.copyOf(classPath);
        return this;
    }

    /**
     * Indicates the kind of debugging information the generated files should contain.
     * <p/>
     * @param debugOptions the kind of debugging information the generated files should contain. By
     * default all debugging information is generated.
     * @return the JavaCompiler object
     */
    public JavaCompiler debug(DebugType... debugOptions)
    {
        this.debugOptions.clear();
        this.debugOptions.addAll(Arrays.asList(debugOptions));
        return this;
    }

    /**
     * Compiles the source code.
     * <p/>
     * @param sourceFiles the source files to compile
     * @param targetDirectory the directory to compile into. This path included in the compiler
     * classpath.
     * @throws IllegalArgumentException if sourceFiles, targetDirectory are null; or if sourceFiles
     * refers to a non-existent file or a non-file; or if targetDirectory is not a directory
     * @throws CompilationException if the operation fails
     */
    public void run(final Collection<Path> sourceFiles, final Path targetDirectory)
        throws IllegalArgumentException, CompilationException
    {
        if (sourceFiles == null)
            throw new IllegalArgumentException("sourceFiles may not be null");
        if (sourceFiles.isEmpty())
            return;
        for (Path file: sourceFiles)
        {
            if (!Files.exists(file))
            {
                throw new IllegalArgumentException("sourceFiles refers to a non-existant file: "
                    + file.toAbsolutePath());
            }
            if (!Files.isRegularFile(file))
            {
                throw new IllegalArgumentException("sourceFiles refers to a non-file: "
                    + file.toAbsolutePath());
            }
        }
        if (targetDirectory == null)
            throw new IllegalArgumentException("targetDirectory may not be null");
        if (!Files.exists(targetDirectory))
        {
            throw new IllegalArgumentException("targetDirectory must exist: " + targetDirectory.
                toAbsolutePath());
        }
        if (!Files.isDirectory(targetDirectory))
        {
            throw new IllegalArgumentException("targetDirectory must be a directory: " + targetDirectory.
                toAbsolutePath());
        }
        Set<Path> uniqueSourceFiles = ImmutableSet.copyOf(sourceFiles);
        Set<Path> uniqueSourcePath = ImmutableSet.copyOf(sourcePath);
        final javax.tools.JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null)
        {
            throw new AssertionError("javax.tools.JavaCompiler is not available. Is tools.jar missing "
                + "from the classpath?");
        }
        final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null,
            null);

        Iterable<? extends JavaFileObject> compilationUnits;
        try
        {
            Set<File> modifiedFiles = getModifiedFiles(uniqueSourceFiles, uniqueSourcePath,
                targetDirectory, new HashSet<Path>());
            if (modifiedFiles.isEmpty())
                return;
            compilationUnits = fileManager.getJavaFileObjectsFromFiles(modifiedFiles);
        }
        catch (IOException e)
        {
            throw new CompilationException(e);
        }
        final List<Path> effectiveClasspath = new ArrayList<>();
        effectiveClasspath.add(targetDirectory);
        effectiveClasspath.addAll(classPath);
        final List<String> options = new ArrayList<>();
        options.add("-cp");
        options.add(Joiner.on(File.pathSeparatorChar).join(effectiveClasspath));

        final StringBuilder debugLine = new StringBuilder("-g:");
        for (DebugType type: debugOptions)
        {
            switch (type)
            {
                case LINES:
                {
                    debugLine.append("lines,");
                    break;
                }
                case SOURCE:
                {
                    debugLine.append("source,");
                    break;
                }
                case VARIABLES:
                {
                    debugLine.append("vars,");
                    break;
                }
                default:
                    throw new AssertionError(type);
            }
        }
        if (!debugOptions.isEmpty())
        {
            debugLine.deleteCharAt(debugLine.length() - ",".length());
            options.add(debugLine.toString());
        }

        if (!uniqueSourcePath.isEmpty())
        {
            options.add("-sourcepath");
            options.add(Joiner.on(File.pathSeparatorChar).join(uniqueSourcePath));
        }
        options.add("-s");
        options.add(targetDirectory.toString());
        options.add("-d");
        options.add(targetDirectory.toString());
        final Writer output = null;
        final CompilationTask task = compiler.getTask(output, fileManager, diagnostics, options, null,
            compilationUnits);
        final boolean result = task.call();
        try
        {
            printDiagnostics(diagnostics, options, sourceFiles);
        }
        catch (IOException e)
        {
            throw new BuildException(e);
        }
        if (!result)
            throw new CompilationException();
        try
        {
            fileManager.close();
        }
        catch (IOException e)
        {
            throw new BuildException(e);
        }
    }

    /**
     * Returns the java source-code file corresponding to a class name.
     * <p/>
     * @param className the fully-qualified class name to look up
     * @param sourcePath the source-code search path
     * @return null if no match was found
     */
    private static File getJavaSource(String className, Set<File> sourcePath)
    {
        // TODO: check for class files instead of source
        for (File path: sourcePath)
        {
            File result = classNameToFile(path, className);
            if (!result.exists())
                continue;
            return result;
        }
        return null;
    }

    /**
     * Converts a class name to its source-code file.
     * <p/>
     * @param sourcePath the source-code search path
     * @param className the fully-qualified class name
     * @return the source-code file
     */
    private static File classNameToFile(File sourcePath, String className)
    {
        return new File(sourcePath, className.replace(".", "/") + ".java");
    }

    /**
     * Displays any compilation errors.
     * <p/>
     * @param diagnostics the compiler diagnostics
     * @param options the command-line options passed to the compiler
     * @param sourceFiles the source files to compile
     * @throws IOException if an I/O error occurs
     */
    private void printDiagnostics(final DiagnosticCollector<JavaFileObject> diagnostics,
        final List<String> options, final Collection<Path> sourceFiles) throws IOException
    {
        Logger log = LoggerFactory.getLogger(JavaCompiler.class.getName() + ".stderr");
        int errors = 0;
        int warnings = 0;
        boolean firstTime = true;
        for (Diagnostic<? extends JavaFileObject> diagnostic: diagnostics.getDiagnostics())
        {
            if (firstTime)
            {
                firstTime = false;
                StringBuilder message = new StringBuilder();
                message.append("Invoking: javac ");
                for (String token: options)
                    message.append(token).append(" ");
                message.append(Joiner.on(" ").join(sourceFiles));
                log.debug(message.toString());
            }
            JavaFileObject source = diagnostic.getSource();
            if (source == null)
                log.error(diagnostic.getMessage(null));
            else
            {
                StringBuilder message = new StringBuilder();
                message.append(source.getName());
                if (diagnostic.getLineNumber() != Diagnostic.NOPOS)
                    message.append(":").append(diagnostic.getLineNumber());
                message.append(": ").append(diagnostic.getMessage(null));
                log.error(message.toString());
                try (BufferedReader reader =
                        new BufferedReader(new InputStreamReader(source.openInputStream())))
                {
                    String line = null;
                    for (long lineNumber = 0, size = diagnostic.getLineNumber(); lineNumber < size;
                        ++lineNumber)
                    {
                        line = reader.readLine();
                        if (line == null)
                            break;
                    }
                    if (line != null)
                    {
                        log.error(line);
                        message = new StringBuilder();
                        for (long i = 1, size = diagnostic.getColumnNumber(); i < size; ++i)
                            message.append(" ");
                        message.append("^");
                        log.error(message.toString());
                    }
                }
            }
            switch (diagnostic.getKind())
            {
                case ERROR:
                {
                    ++errors;
                    break;
                }
                case NOTE:
                case OTHER:
                case WARNING:
                case MANDATORY_WARNING:
                {
                    ++warnings;
                    break;
                }
                default:
                    throw new AssertionError(diagnostic.getKind());
            }
        }
        if (errors > 0)
        {
            StringBuilder message = new StringBuilder();
            message.append(errors).append(" error");
            if (errors > 1)
                message.append("s");
            log.error(message.toString());
        }
        if (warnings > 0)
        {
            StringBuilder message = new StringBuilder();
            message.append(warnings).append(" warning");
            if (warnings > 1)
                message.append("s");
            log.warn(message.toString());
        }
    }

    /**
     * Returns the source-code files that have been modified.
     * <p/>
     * @param sourceFiles the source files to process
     * @param sourcePath the source file search path
     * @param targetDirectory the directory to compile into
     * @param unmodifiedFiles files that are known not to have been modified
     * @return all changed source-code files that are accepted by the filter
     * @throws IOException if an I/O error occurs
     */
    private Set<File> getModifiedFiles(final Set<Path> sourceFiles,
        final Set<Path> sourcePath, final Path targetDirectory, final Set<Path> unmodifiedFiles)
        throws IOException
    {
        // TODO: Right now all files are assumed to have been modified
        Set<File> result = new HashSet<>();
        for (Path path: sourceFiles)
            result.add(path.toFile());
        return result;
    }

    /**
     * Returns the command-line representation of the object.
     * <p/>
     * @param sourceFiles the source files to compile
     * @param targetDirectory the directory to compile into
     * @param sourcePath the source file search path
     * @return the command-line representation of the object
     * @throws IllegalArgumentException if the classpath contains non-file components
     * @throws IOException if an I/O error occurs
     */
    private List<String> toCommandLine(final Collection<Path> sourceFiles, final Path targetDirectory,
        final Collection<Path> sourcePath)
        throws IOException
    {
        final List<String> result = Lists.newArrayList("javac");
        if (!classPath.isEmpty())
        {
            result.add("-cp");
            try
            {
                final StringBuilder line = new StringBuilder();
                for (final Iterator<Path> i = classPath.iterator(); i.hasNext();)
                {
                    line.append(i.next().getParent().toString());
                    if (i.hasNext())
                        line.append(File.pathSeparatorChar);
                }
                result.add(line.toString());
            }
            catch (IllegalArgumentException e)
            {
                // Occurs if URL does not refer to a file
                throw new IllegalStateException(e);
            }
        }
        for (File javaFile: getModifiedFiles(ImmutableSet.copyOf(sourceFiles),
            ImmutableSet.copyOf(sourcePath), targetDirectory, new HashSet<Path>()))
        {
            result.add(javaFile.getPath());
        }
        result.add("-d");
        result.add(targetDirectory.getParent().toString());
        return result;
    }

    @Override
    public String toString()
    {
        return getClass().getName() + "[classPath=" + classPath + "]";
    }

    /**
     * The type of debugging information that generated files may contain.
     * <p/>
     * @author Gili Tzabari
     */
    @SuppressWarnings("PublicInnerClass")
    public enum DebugType
    {
        /**
         * No debugging information.
         */
        NONE,
        /**
         * Line number information.
         */
        LINES,
        /**
         * Local variable information.
         */
        VARIABLES,
        /**
         * Source file information.
         */
        SOURCE
    }
}