Я разработал приложение, которое генерирует источник Java во время выполнения из внешнего источника. Сгенерированный исходный код реализует интерфейс, скажем, org.foo
, а также вызывает другие классы, расположенные в org.foo
. Приложение скомпилировало сгенерированный источник Java в указанный пакет c, скажем 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
Я получаю следующий вывод без Информация о модуле. java:
All files compiled without errors: true
0 diagnostics reported by compiler task
I получить следующий вывод с информация о модуле. 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
С module-info. 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
Файлы используется:
module-info. java
module org.foo {
requires java.compiler;
}
Main. 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;
}
}