2010-03-10 2 views
8

Как показано в the documentation, Gradle использует ориентированный ациклический граф (DAG) для построения графика зависимости. По моему мнению, наличие отдельных циклов для оценки и выполнения является важной особенностью инструмента построения. например В документе Gradle doc указано, что это позволяет использовать некоторые функции, которые в противном случае были бы невозможны.Каковы реальные примеры графа зависимости Gradle?

Я заинтересован в реальных примерах, иллюстрирующих мощь этой функции. Каковы некоторые прецеденты, для которых важна графа зависимостей? Меня особенно интересуют личные истории с поля, будь то с Gradle или аналогично оборудованным инструментом.

Я делаю это «wiki сообщества» с самого начала, так как будет сложно оценить «правильный» ответ.

ответ

5

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

Первый вопрос: почему граф зависимостей задачи Gradle гарантированно ацикличен. Я не нашел ответа на этот вопрос, но противоположный случай легко сконструирован, поэтому я предполагаю, что обнаружение цикла - это проверка, выполняемая при построении графика, и сборка не выполняется до выполнения первой задачи, если существуют незаконные циклические зависимости. Без первого построения графика это условие сбоя может быть не обнаружено до тех пор, пока сборка не будет завершена. Кроме того, процедура обнаружения должна запускаться после выполнения каждой задачи, что было бы очень неэффективно (пока граф был построен постепенно и доступен по всему миру, поиск глубины сначала потребовался бы, чтобы найти начальную точку, а затем циклические оценки потребуют минимальной работы, но общая работа по-прежнему будет больше, чем полное сокращение всего набора отношений с самого начала). Я бы назвал раннее обнаружение основным преимуществом.

Зависимость задачи может быть ленивой (см. 4.3 Зависимости задач и соответствующий пример в 13.14). Невозможно корректно оценивать ленивые зависимости задач, пока не будет построен весь граф. То же самое можно сказать и о переходном (нестандартном) разрешении зависимостей, которое может вызвать бесчисленные проблемы и требует повторной повторной компиляции, поскольку обнаружены и устранены дополнительные зависимости (также требующие повторных запросов к репозиторию). Функция правил задачи (13.8) также невозможна.Эти проблемы и, возможно, многие другие, могут быть обобщены, учитывая, что Gradle использует динамический язык и может динамически добавлять и изменять задачи, поэтому до оценки первого прохода результаты могут быть недетерминированными, поскольку путь выполнения построен и измененные во время выполнения, таким образом, различные последовательности оценки могут приводить к произвольным разным результатам, если существуют зависимости или директивы поведения, которые неизвестны до тех пор, пока они еще не созданы. (Это может быть полезно исследовать с некоторыми конкретными примерами. Если это так, то даже два прохода не всегда будут достаточными. Если A -> B, B -> C, где C изменяет поведение A, так что оно больше не зависит от B, тогда у вас есть проблема. Надеюсь, что есть некоторые рекомендации по ограничению метапрограммирования с нелокальной областью, чтобы не допустить ее в произвольных задачах. Примером может служить симуляция парадокса во время путешествия, где внук убивает своего дедушку или женится на своей бабушке, наглядно иллюстрируя некоторые практические этические принципы!)

Это может обеспечить улучшенную отчетность о состоянии и ходе выполнения в текущей исполняемой сборке. TaskExecutionListener предоставляет до/после перехвата обработку каждой задачи, но не зная количества оставшихся задач, не так много можно сказать о статусе, отличном от «6 завершенных задач. О выполнении задачи foo». Вместо этого вы можете инициализировать TaskExecutionListener с количеством задач в gradle.taskGraph.whenReady, а затем присоединить его к TaskExecutionGraph. Теперь он может предоставить информацию, чтобы включить детали отчета, такие как «6 из 72 выполненных задач». Теперь выполнение задачи foo. Ожидаемое оставшееся время: 2h 38m. » Это было бы полезно отображать на консоли для сервера непрерывной интеграции, или если бы Gradle использовался для организации больших многопроектных сборки и оценки времени, это было важно.

Как отметил Джерри Буллард, оценочная часть жизненного цикла имеет решающее значение для определения плана выполнения, который предоставляет информацию об окружающей среде, поскольку среда частично определяется контекстом выполнения (пример 4.15 в разделе «Настройка по DAG») раздел). Кроме того, я мог видеть, что это полезно для оптимизации выполнения. Независимые подпапки могут быть безопасно переданы различным потокам. Алгоритмы ходьбы для выполнения могут быть менее интенсивными в памяти, если они не наивны (моя интуиция говорит, что всегда идти по пути с большинством подпапок приведет к большему стеку, чем всегда предпочитает пути с наименьшими субпатами).

Любопытно, что это может быть ситуация, когда многие компоненты системы изначально вычеркнуты для поддержки демонстраций и инкрементного развития. Затем во время разработки вместо обновления конфигурации сборки по мере реализации каждого компонента сама сборка может определить, готов ли подпроект к включению (возможно, он пытается захватить код, скомпилировать его и запустить предварительно определенный набор тестов) , Если это так, этап оценки выявит это, и соответствующие задачи будут включены, в противном случае он выбирает задачи для заглушек. Возможно, существует зависимость от базы данных Oracle, которая пока недоступна, и вы используете встроенную базу данных тем временем. Вы можете позволить сборке проверить наличие, прозрачно переключиться, когда это возможно, и сказать, что он переключил базы данных, а не рассказывал об этом. В этих направлениях может быть много творческих применений.

Gradle выглядит потрясающе. Спасибо, что вызвали некоторые исследования!

3

example from the same documentation иллюстрирует силу этого подхода:

Как мы описываем в полной детализацией позже (смотрите главу 30, Моделировочная Lifecycle) Gradle имеет фазу конфигурации и время фазы выполнения. После фазы конфигурации Gradle знает все задачи , которые должны быть выполнены. Gradle предлагает вам крючок, чтобы использовать эту информацию . Вариант использования для этого: - проверить, является ли задача выпуска частью выполняемых задач. В зависимости от этого вы можете присвоить некоторым значениям различные значения.

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

+0

Спасибо, за ответ! Однако я был знаком с этим примером. Я ищу что-то с немного большим количеством мяса. –

1

Я оцениваю различные системы сборки сейчас и с помощью градиента. Мне удалось добавить уродливый код, который перечисляет все задачи типа «jar» и изменяет их так, чтобы каждый манифест манифеста включал атрибут «Build-Number» (который используется позднее для составления окончательных имен файлов):

gradle.taskGraph.whenReady { 
    taskGraph -> 
    taskGraph.getAllTasks().findAll { 
     it instanceof org.gradle.api.tasks.bundling.Jar 
    }.each { 
     it.getManifest().getAttributes().put('Build-Number', project.buildVersion.buildNumber) 
    } 
}