1

我开发了一个应用程序,它在运行时从外部源生成 Java 源代码。生成的源代码实现了一个位于 say 中的接口,org.foo并且还调用了位于org.foo. 应用程序将生成的 Java 源代码编译为特定的包,例如org.foo.generated. 提供的编译器ToolProvider.getSystemJavaCompiler()使用 找到源文件ForwardingJavaFileManager,编译源文件并且应用程序能够运行它们。

现在我已经在 J​​PMS 模块中拆分了我的应用程序,编译器抱怨它找不到org.foo由生成的源代码实现的接口所在的包。org.foo我从与生成代码所在的同一模块 () 中调用编译器。错误消息如下所示:

包 org.foo 不存在

ForwardingFileManager通过向查找模块添加一些方法来更改我的方法,如此所述。似乎驻留在java.compiler模块中的编译器无法访问该org.foo模块。所以,我补充说

--add-reads java.compiler=org.foo

到命令行,但这并没有改变任何东西。有什么线索吗?有人将运行时生成的源文件和 JPMS 与 Janino 而不是 ToolProvider 编译器结合起来有什么积极的经验吗?

以下是评论中要求的最小可重现示例。也可以从 github 检索源文件:

https://github.com/evg/demo-jpms-compiler.git

我得到以下没有module-info.java 的输出:

All files compiled without errors: true
0 diagnostics reported by compiler task

我使用module-info.java得到以下输出:

All files compiled without errors: false
3 diagnostics reported by compiler task
DIAGNOSTIC /org/foo/generated/MyGreeter.java:5: error: cannot find symbol
public class MyGreeter implements IGreeter {
                                  ^
  symbol: class IGreeter
DIAGNOSTIC /org/foo/generated/MyGreeter.java:3: error: package org.foo does not exist
import org.foo.*;
^
DIAGNOSTIC /org/foo/generated/MyGreeter.java:6: error: method does not override or implement a method from a supertype
  @Override
  ^

Eclipse 使用的命令行。 没有module-info.java:

C:\Program Files\Java\jdk-11.0.2\bin\javaw.exe -Dfile.encoding=Cp1252 -classpath "H:\git\demo-jpms-compiler\demo-jpms-compiler\target\classes" org.foo.Main

使用模块信息.java:

C:\Program Files\Java\jdk-11.0.2\bin\javaw.exe -Dfile.encoding=Cp1252 -p "H:\git\demo-jpms-compiler\demo-jpms-compiler\target\classes" -m org.foo/org.foo.Main

使用的文件:

模块信息.java

module org.foo {
  requires java.compiler;
}

主.java

package org.foo;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;

public class Main {

    public static void main(String[] args) {
        new Main().run();
    }

    private void run() {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        Iterable<? extends JavaFileObject> compilationUnits = List.of(new JavaSourceFromString("org.foo.generated.MyGreeter", getJavaSource()));
        JavaCompiler.CompilationTask task = compiler.getTask(null, null, diagnosticCollector , compilerOptions, null, compilationUnits);
        boolean allFilesCompiledWithoutErrors = task.call();
        log("All files compiled without errors: " + allFilesCompiledWithoutErrors);
        log(diagnosticCollector.getDiagnostics().size() + " diagnostics reported by compiler task");
        diagnosticCollector.getDiagnostics().stream().forEach(diagnostic->{
            log("DIAGNOSTIC " + diagnostic);
        });
    }

    private void log(String text) {
        System.out.println(text);
    }

    private String getJavaSource() {
        String[] lines = new String[] {
                "package org.foo.generated;",
                "",
                "import org.foo.*;",
                "",
                "public class MyGreeter implements IGreeter {",
                "  @Override",
                "  public String getGreeting() {",
                "    return \"Hello, generated world\";",
                "  }",
                "",
                "}"
        };
        return Stream.of(lines).collect(Collectors.joining(System.lineSeparator()));
    }

    private DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
    private List<String> compilerOptions = Arrays.asList("-Xlint:deprecation");
}

IGreeter.java

package org.foo;

public interface IGreeter {
  public String getGreeting();
}

JavaSourceFromString.java

package org.foo;

import java.net.URI;

import javax.tools.SimpleJavaFileObject;

/**
 * A file object used to represent source coming from a string.
 */
public class JavaSourceFromString extends SimpleJavaFileObject {
    /**
     * The source code of this "file".
     */
    final String code;

    /**
     * Constructs a new JavaSourceFromString.
     * 
     * @param name
     *            the name of the compilation unit represented by this file object
     * @param code
     *            the source code for the compilation unit represented by this file
     *            object
     */
    public JavaSourceFromString(String name, String code) {
        super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}
4

0 回答 0