JavaCompiler с пользовательскими ClassLoader и FileManager
Я хочу скомпилировать исходный код, не имея зависимостей, присутствующих на машине.
Пример: Файл A.java:
import some.pkg.B;
public class A extends B {...}
У меня нет источника B, я хочу подключить JavaFileManager или собственный ClassLoader, чтобы получить соответствующие символы (пакет "some.package" и класс B), а затем использовать сервис, который у меня есть извлекает исходную строку.
Компиляционный код: (inputFiles имеет A.java)
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
CustomClassLoader classLoader = new CustomClassLoader();
StandardJavaFileManager standardfileManager = compiler.getStandardFileManager(this, null, null);
JavaFileManager fileManager = new CustomFileManager(standardfileManager, output, classLoader);
CompilationTask task = compiler.getTask(null, fileManager, this, null, null, inputFiles);
boolean result = task.call();
Захваты на JavaFileManager (getFileForInput..) и на моем загрузчике классов (findClass, loadClass..) не запускались при компиляции, и я получил сообщения об ошибках:
A.java:#: package some.pkg does not exist
A.java:#: cannot find symbol
symbol: class B
ИЗМЕНИТЬ
После игры с API, перейдя через источник JavaCompiler (более старая версия) и прочитав Обзор компиляции Я до сих пор не могу найти API-интерфейс Я могу использовать, чтобы предоставить мне символы из синтаксических деревьев. Кажется, что API должен получить все ресурсы, основанные на именах пакетов, как предложено kschneid.
Один обходной путь, о котором я думал, - это запуск JavaCompiler и анализ сообщений об ошибках для отсутствующих символов. Таким образом, я буду знать, какие символы необходимы, получить их и перекомпилировать.
Любые другие обходные пути/решения?
Ответы
Ответ 1
(Я предполагаю, что вы действительно не используете имя пакета "package", поскольку это было бы просто незаконным...)
Ваш пользовательский JavaFileManager
должен получать свой метод list
. Надеемся, что это обозначение имеет смысл, но сочетание аргументов с этим методом должно выглядеть так:
[PLATFORM_CLASS_PATH, some, [CLASS], false]
[CLASS_PATH, some, [SOURCE, CLASS], false]
[PLATFORM_CLASS_PATH, some.pkg, [CLASS], false]
[CLASS_PATH, some.pkg, [SOURCE, CLASS], false]
Я не уверен, насколько сложно создать для вашей конкретной среды соответствующие экземпляры Iterable<JavaFileObject>
, но я думаю, что потребуется...
Ответ 2
Я обнаружил, что хороший способ подключиться к классам во время компиляции - Groovy AST Transformation. Вы можете посмотреть, что можно сделать здесь
Это не простая старая java, но она может быть удобным инструментом, чтобы знать