Рубрики
Без рубрики

Разработка плагина Gradle для J2 CLOUD

В предыдущем посте я исследовал, как JCL используется в Bazel. Начиная с этого поста, с этого знания… Помеченный j2cl, gwt, базель, java.

В предыдущем посте я исследовал, как JCL используется в Bazel. Начиная с этого поста, с учетом этих знаний, я попытаюсь разработать плагин Gradle для J2 CL.

Строительные блоки

Давайте начнем с низкоуровневых строительных блоков.

Все J2CL ( GwtIncompatibleStrip и J2clTranspile ) и компилятор закрытия ( CommandLineRunner ) инструменты могут быть легко вызваны из процессов Java (работники Bazel на самом деле вызывают разные, иногда внутренние, API), поэтому их можно вызвать из Gradle workers . Также не должно быть проблемой вызывать инструменты JCL постепенно, обрабатывая только измененные файлы; ну, на самом деле, это не будет проблемой для GwtIncompatibleStrip , который обрабатывает файлы один за другим изолированно; однако J2clTranspile , как и javac , также потребуется повторно обработать другие файлы Java, ссылающиеся на измененные файлы, поэтому сделать его инкрементным означало бы знать об этих зависимостях; давайте оставим эту оптимизационную работу на потом (FWIW, способ, которым Bazel справляется с этим, заключается в том, чтобы не выполнять любую инкрементную/частичную обработку любого рода, но вместо этого использовать небольшие наборы файлов, как правило, на уровне пакета Java).

Это означает, что нам придется объявить конфигурацию 3 s (для зависимостей 3 инструментов), и мы могли бы создавать соответствующие задачи с соответствующими входами и выходами.

Это будет работать для источников проекта, но не для внешних зависимостей. Для них мы, вероятно, могли бы использовать преобразования артефактов , но это исключает компиляцию исходных файлов с помощью javac , по крайней мере , если мы хотим использовать стандарт Gradle JavaCompile задача (я обдумывал идею GWT-совместимой полосы на основе ASM , которая тогда решила бы проблему, как преобразование артефакта двоичной банки). Однако давайте оставим внешние зависимости на потом.

Говоря о задачах Компилятора Java , для JCL мы хотели бы настроить их путь к классу начальной загрузки в JAR для эмуляции среды выполнения Java. К сожалению, -путь к загрузочному классу может использоваться только при компиляции для Java 8, что исключает использование любого синтаксиса Java 9+: частные методы в интерфейсах, var для вывода типов и т. Д. Для более поздних версий Java мы хотели бы использовать --system , но это совершенно другой формат, и он еще даже не поддерживается Bazel, не говоря уже о том, что он создан J2 CL (хотя, возможно, его не так сложно создать из JAR для эмуляции среды выполнения Java). Fwiw, Google также сталкивается с той же проблемой для Android для добавления поддержки синтаксиса Java 9 +, а также J2ObjC, поэтому мы можем быть уверены, что они найдут решение (на самом деле они уже работают над этим ).

Тесты

Для тестов обработчик аннотаций и его @J2clTestInput были разработаны как детали реализации, скрытые за правилом j2cl_test в Bazel. В отличие от Bazel, где одно правило j2cl_test запускает только один тестовый класс (который на самом деле может быть набором тестов), с макросом gen_j2cl_tests для их автоматического создания из исходных файлов с использованием соглашения об именовании, в Gradle у нас будет одна задача для всего src/теста . Однако у нас может быть задача, которая генерирует набор JUnit, аннотированный @J2clTestInput , и ссылается на тестовые классы, используя соглашение об именовании, а затем обрабатывает его с помощью процессора аннотаций.

Это сгенерирует некоторый Java-код для переноса с помощью J2CL. На этом этапе можно повторно использовать задачу J2clTranspile сверху, используя src/тест/java и генерировать Java-код одновременно.

Затем файл test_summary.json необходимо обработать и выполнить одну компиляцию закрытия для каждой точки входа JS (по одной для класса тестов без набора, с расширением файла .testsuite ). Это не может повторно использовать задачу Компилятор закрытия : нам нужна одна задача, управляющая несколькими компиляциями закрытия. Таким образом, результатом будет несколько приложений JS; мы поместим их в отдельные каталоги (названные в честь оригинального теста Java) и создадим дополнительную HTML-страницу для их запуска. Задачу можно было бы сделать инкрементной, только перекомпилируя тесты, которые изменились, но для этого потребуется информация о зависимостях между файлами (которую можно извлечь с помощью закрытия, с некоторой дополнительной работой; или, возможно, даже просто проанализировав goog.обеспечивать /|/goog.требуется ). Кстати, эти знания также могут быть использованы задачей Компилятор закрытия , чтобы пропустить компиляцию, если файл изменился, который ни для чего не нужен.

Наконец, нам нужно будет запустить эти сгенерированные тесты в браузерах. Лучший способ сделать это – использовать Selenium WebDriver. Таким образом, нам понадобится задача, берущая эти каталоги скомпилированных тестов, которая запускает HTTP-сервер для их обслуживания и загружает каждый из них в веб-браузер через WebDriver, создавая отчеты (это помогает пропустить задачу, если входные данные не изменились).

Заставляя все это работать

Для приложения, подобного образцу HelloWorld из репозитория Bazel, объединение всех этих задач приведет к следующему графику:

Код для этих задач и пример проекта доступны на Github .

tbroyer/gradle-j2cl-плагин

Плагин Gradle J2CL

Следующие шаги

Теперь, когда у нас есть строительные блоки и мы можем соединить их вместе для простого примера, следующими шагами будут:

  • обработка внешних зависимостей (удаление и перенос их на лету)
  • обработка зависимостей проекта (библиотечный подпроект будет предоставлять свои транспилированные источники подпроекту приложения)
  • определение соглашений, чтобы сделать вещи такими же простыми, как применение плагина Gradle (приложение JCL было бы самым простым, так как библиотеки могли бы быть нацелены на J2CL, GWT, JVM, J2ObjC и т. Д.)

Оригинал: “https://dev.to/tbroyer/designing-a-gradle-plugin-for-j2cl-c7k”