Каковы реальные примеры графа зависимостей Gradle? - PullRequest
9 голосов
/ 10 марта 2010

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

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

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

Ответы [ 3 ]

6 голосов
/ 18 марта 2010

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

Мой первый вопрос заключался в том, почему граф зависимостей задач 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. Предполагаемое оставшееся время: 2 ч. 38 м.» Это было бы полезно для отображения на консоли для сервера непрерывной интеграции, или если Gradle использовался для организации большой многопроектной сборки, и оценки времени были критически важны.

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

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

Градл выглядит потрясающе. Спасибо за провокацию!

3 голосов
/ 18 марта 2010

Пример из той же документации иллюстрирует силу этого подхода:

Как мы опишем более подробно позже (См. Главу 30, Жизненный цикл сборки) Gradle имеет фазу конфигурации и фаза исполнения. После Фаза конфигурации Gradle знает все задачи, которые должны быть выполнены. Gradle предлагает вам воспользоваться этим Информация. Вариант использования для этого будет чтобы проверить, если задача выпуска часть задач, подлежащих выполнению. В зависимости от этого вы можете назначить различные значения для некоторых переменных.

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

1 голос
/ 17 апреля 2011

Сейчас я оцениваю разные системы сборки, и с помощью gradle мне удалось добавить некрасивый код, который перечисляет все задачи типа 'jar' и изменяет их так, чтобы каждый манифест 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)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...