我开发了一个应用程序,它在运行时从外部源生成 Java 源代码。生成的源代码实现了一个位于 say 中的接口,org.foo
并且还调用了位于org.foo
. 应用程序将生成的 Java 源代码编译为特定的包,例如org.foo.generated
. 提供的编译器ToolProvider.getSystemJavaCompiler()
使用 找到源文件ForwardingJavaFileManager
,编译源文件并且应用程序能够运行它们。
现在我已经在 JPMS 模块中拆分了我的应用程序,编译器抱怨它找不到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;
}
}