Выбор категории процессора добавочных аннотаций Gradle, когда процессор зависит от класса в значении аннотации

У меня есть простой процессор аннотаций, который применяется следующим образом:

@DiffElement(diffReceiver = Renderer.class)
class ViewState {
  String getHello();
  int getWorld();
}

class Renderer {
  void renderHello(String hello);
  void renderWorld(int world);
}

Чтобы этот процессор работал, имена get-функций и аргументы функции Renderer-интерфейса должны совпадать. Он проверяет это и использует аргумент аннотации для просмотра предоставленного класса и генерации кода на его основе.

Создает один файл.

Я прочитал документы по добавочной обработке аннотаций и могу Пока не решаю, в какую категорию отнести этот процессор. Вот мои соображения:

  • он не может быть isolating, потому что он не выводит все из AST аннотированного элемента, так как он также проверяет класс из аргумента аннотации
  • это не может быть aggregating, потому что у него нет аннотации к классу Renderer, поэтому, согласно приведенной выше документации, процессор не будет вызываться при изменении класса Renderer, потому что процессор не зарегистрирован для обработки этого файла, поэтому это приведет к ошибкам в сгенерированном результате.

Вопросов:

  • Я правильно понимаю документацию? Или может еще какая-то категория применима к этому процессору
  • Если он не попадает ни в одну из категорий, как я могу сказать Gradle, что он не является инкрементным, чтобы такие инструменты, как «kotlin kapt», не жаловались пользователю, что мой процессор не инкрементальный

person dimsuz    schedule 09.09.2019    source источник


Ответы (2)


Я правильно понимаю документацию? Или может еще какая-то категория применима к этому процессору

Документация по категориям процессоров действительно небольшая и, на мой взгляд, не содержит примеров. Я потратил много времени на изучение этих документов, создав несколько простых проектов для экспериментов. Так что, насколько я знаю, категорию можно применить, если я в итоге правильно их разобрал :)

Вы говорите, что

он не может быть изолированным, потому что он не выводит все из AST аннотированного элемента, так как он также проверяет класс из аргумента аннотации

Это не совсем так, и я объясню почему. Как упоминалось в документации, изоляция процессоров

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

Здесь очень важна даже транзитивная фраза - это означает, что вы можете анализировать не только AST аннотированного типа, но, например, также некоторые из AST возвращаемого типа этого метода, а затем, скажем, суперкласс этого типа ...

Насколько я понимаю, каждый тип, который вы можете обнаружить с помощью обходов AST из аннотированного элемента, является зависимостью от аннотированного типа (или элемента в целом). И если зависимость изменена, то зависимый тип требует перекомпиляции. Поэтому, когда Renderer класс изменяется, ViewState будет перекомпилирован и, таким образом, повторно обработан, потому что он ссылается на Renderer в качестве аргумента аннотации. Супертипы, суперинтерфейсы, возвращаемые типы методов, типы параметров метода, аргументы класса аннотации, ... - все это считается зависимыми типами.

Таким образом, ваш обработчик аннотаций на самом деле может быть изолирующим.


P.S. Если изоляция не работает, убедитесь, что срок хранения аннотаций составляет CLASS или выше по любой причине.

P.P.S. Я нахожу инкрементность обработки аннотаций действительно сомнительной темой, полной сюрпризов и подводных камней. Эмпирическое правило, которое я обнаружил для себя, заключается в том, что практически каждый процессор с несколькими настройками может быть изолированным, если только ему действительно не нужно генерировать одну сущность на основе множества входных данных. И, что важно, только в том случае, если эти входы могут быть совершенно не связаны друг с другом с точки зрения ссылок или даже находиться в разных библиотеках.

person Jeffset    schedule 27.07.2020
comment
Спасибо за такой подробный анализ! Я изменил принятый ответ на ваш, потому что он отвечает на мой вопрос напрямую (хотя предыдущий ответ Колина тоже оказался полезным). - person dimsuz; 27.07.2020

он не может быть агрегирован, потому что у него нет аннотации к классу Renderer, поэтому, согласно приведенной выше документации, процессор не будет вызываться при изменении класса Renderer, потому что процессор не зарегистрирован для обработки этого файла, поэтому это приведет к ошибкам в сгенерированном результате.

Это не будет прямым ответом, но я думаю, что он все же может ответить на ваш вопрос: это утверждение, насколько я понимаю, является фактическим источником вашей проблемы. Здесь вас подведет любая слегка инкрементная система, а не только режим «агрегирования» Gradle (например, попробуйте использовать свой процессор в простом проекте Eclipse и, возможно, даже в IntelliJ, хотя у меня там были смешанные результаты).

Если вы действительно хотите принудительно применить эти изменения к неаннотированному типу, это заставит ваш обработчик аннотаций снова запуститься, вы не должны ограничивать свой обработчик аннотаций этой аннотацией, но должны возвращать магическое значение "*" из getSupportedAnnotationTypes(), указывающее, что любой измененный класс должен запустить процессор для повторного запуска. Из документации Javadoc для этого метода:

Наконец, «*» сам по себе представляет набор всех типов аннотаций, включая пустой набор. Обратите внимание, что процессор не должен указывать «*», если он фактически не обрабатывает все файлы; Заявление о ненужных аннотациях может привести к снижению производительности в некоторых средах.

В идеале вы должны просто сделать вторую (или третью и т. Д.) Аннотацию, чтобы вместо этого предоставить эту подсказку, но это не всегда возможно, поэтому вам разрешен этот вариант с подстановочными знаками

person Colin Alworth    schedule 10.09.2019
comment
Я подозревал, что мне придется реструктурировать мой процессор и добавить дополнительную аннотацию, чтобы правильно отслеживать изменения в аннотированных классах. Я не знал о "*" опции, но боюсь, что это будет излишним. Полагаю, я попытаюсь переосмыслить структуру аннотаций. Спасибо! - person dimsuz; 10.09.2019
comment
Конечно, это излишне, но оно того стоит, если у вас есть способ предоставить проект gradle только для этих типов, так что имеет смысл повторно запускать процессор для любых изменений. - person Colin Alworth; 10.09.2019