【问题标题】:Using JavaFileManager to compile projects使用 JavaFileManager 编译项目
【发布时间】:2012-06-01 16:30:48
【问题描述】:

我目前正在创建一个允许用户编译单个或多个项目的系统。我一直在做一些研究,并决定使用 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;
}

有人知道怎么做吗?

【问题讨论】:

  • “java 项目”是什么意思?它与一组 java 文件(每个文件都可以单独编译)有何不同?您在尝试编译“项目”时遇到了哪些问题?
  • 好吧,当我说 java 项目时,我的意思是某个目录中的一组 java 文件。我不,我可以单独编译它们,但我目前拥有它的方式是所有文件都在 JList 中,用户可以选择他们想要编译的文件,我想要做的是添加项目文件夹(文件夹中的所有java文件)到JTree,然后使用项目的根节点编译单个项目,就像您可以通过单击包屏幕中的项目并编译然后编译所有java文件在eclipse中编译一个完整项目文件夹
  • 我遇到的问题是我收到一个错误,上面写着“找不到符号”并指向您正在使用的类,即使文件编译在 Eclipse 和导入中找到您正在使用方法 from 的类的声明也在那里,它说我找不到该类
  • 您需要确保预先编译了相应的 .java 文件和生成的类。或者,收集您传递给compiler.getTask() 的编译单元列表中的所有.java 文件。后一种方法应确保正确解决循环依赖关系(如果有)

标签: java compiler-construction multiple-projects


【解决方案1】:

【讨论】:

  • 只是在这里阅读有关收缩包装的信息,看起来很简单,可以使用。您是否建议将每个项目的所有文件移动到一个新的 'ShrinkWrap.create(JavaArchive.class,"archive.jar")' 中,然后编译 jar 文件是吗?
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    您需要在编译器类路径中包含输出目录。请注意我是如何在下面的类路径中包含 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
        }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-14
    • 1970-01-01
    • 1970-01-01
    • 2021-06-24
    • 2013-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多