Создайте репозиторий Gradle для автономной разработки

Я работаю над внедрением системы сборки Gradle для части программного обеспечения, которая имеет части, которые разработаны в области без подключения к Интернету, или возможность установки сервера Maven/Ivy (например, Nexus). Чтобы поддерживать разработку в этих средах, я собираю плагин Gradle, который позволяет генерировать "автономную рабочую область".

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

Эта реализация выполнила свою работу, используя блок afterEvaluate. Хотя это работало нормально в Gradle 2.0, оно вызывает предупреждение об устаревании в Gradle 2.2.1, потому что запуск разрешения каким-то образом рассматривается как изменение конфигурации после того, как оно уже разрешено (Attempting to change configuration ':core:runtime' after it has been included in dependency resolution. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0). В целом, этот подход кажется довольно хакерским, так как он также требует от меня изменить файлы build.gradle, чтобы явно перечислять все транзитивные зависимости, поскольку нет доступных POM файлов для правильного указания зависимостей.

Более элегантный подход кажется, что он построит локальный репозиторий Maven всех зависимостей (включая файлы POM, исходные JAR, javadoc JAR и т.д.), а затем просто используйте тип хранилища mavenLocal(). К сожалению, я не уверен, как это сделать правильно, где мне не нужно вызывать разрешение артефакта, чтобы выполнить эту операцию.

Есть ли какой-то лучший способ, которым я могу добиться полной загрузки артефактов в простой путь к пакету, чем просто перевернуть весь мой каталог USER_HOME/.gradle?

Ответы

Ответ 1

Чтобы иметь автономную сборку, вам нужно как-то обеспечить все необходимые зависимости. Один из вариантов здесь - просто передать эти банки в управление версиями. Трудная часть - собрать все эти зависимости. Для этого возможно иметь файл build.gradle, который может работать в двух режимах (онлайн и офлайн):

buildscript {
    repositories {
        if ('allow' == System.properties['build.network_access']) {
            mavenCentral()
        } else {
            maven {
                url 'dependencies'
            }
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.0-beta2'
    }
}

Для запуска в автономном режиме введите:

gradle --offline

И для запуска в онлайн-режиме:

gradle -Dbuild.network_access=allow

И для сбора всех зависимостей используйте этот script, который будет запускать gradle в онлайн-режиме, выберите зависимости для кэширования внутри ${project_dir}/.gradle_home и скопируйте артефакты в локальный репозиторий maven в папке dependencies.

#!/usr/bin/python

import sys
import os
import subprocess
import glob
import shutil

# Place this in build.gradle:
# repositories {
#     if ('allow' == System.properties['build.network_access']) {
#         mavenCentral()
#     } else {
#         maven { url 'dependencies' }
#     }
# }
def main(argv):
    project_dir = os.path.dirname(os.path.realpath(__file__))
    repo_dir = os.path.join(project_dir, "dependencies")
    temp_home = os.path.join(project_dir, ".gradle_home")
    if not os.path.isdir(temp_home):
        os.makedirs(temp_home)
    subprocess.call(["gradle", "-g", temp_home, "-Dbuild.network_access=allow"])
    cache_files = os.path.join(temp_home, "caches/modules-*/files-*")
    for cache_dir in glob.glob(cache_files):
        for cache_group_id in os.listdir(cache_dir):
            cache_group_dir = os.path.join(cache_dir, cache_group_id)
            repo_group_dir = os.path.join(repo_dir, cache_group_id.replace('.', '/'))
            for cache_artifact_id in os.listdir(cache_group_dir):
                cache_artifact_dir = os.path.join(cache_group_dir, cache_artifact_id)
                repo_artifact_dir = os.path.join(repo_group_dir, cache_artifact_id)
                for cache_version_id in os.listdir(cache_artifact_dir):
                    cache_version_dir = os.path.join(cache_artifact_dir, cache_version_id)
                    repo_version_dir = os.path.join(repo_artifact_dir, cache_version_id)
                    if not os.path.isdir(repo_version_dir):
                        os.makedirs(repo_version_dir)
                    cache_items = os.path.join(cache_version_dir, "*/*")
                    for cache_item in glob.glob(cache_items):
                        cache_item_name = os.path.basename(cache_item)
                        repo_item_path = os.path.join(repo_version_dir, cache_item_name)
                        print "%s:%s:%s (%s)" % (cache_group_id, cache_artifact_id, cache_version_id, cache_item_name)
                        shutil.copyfile(cache_item, repo_item_path)
    shutil.rmtree(temp_home)
    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv))

Итак, после каждого изменения зависимости просто запустите этот script и зафиксируйте изменения в папке dependencies. Затем вы можете создавать автономные с помощью gradle --offline или просто gradle.

Ответ 2

В прошлом я использовал подобное решение, хотя "мой" сценарий копирования выполнялся в groovy вместо python.

Пару недель назад я обнаружил другой подход: есть плагин ivypot. Теперь вам больше не нужно использовать "внешний" скрипт, плагин может копировать все зависимости в локальный каталог, который является репозиторием ivy.

Тестовый проект можно найти на github. Я могу предоставить README на английском языке, если на это есть спрос.

Ответ 3

Самое простое решение - сделать снимок всего каталога кэша зависимостей: ~/.gradle

Однако есть некоторые проблемы:

  1. Это займет значительное количество дискового пространства
  2. Он будет содержать тысячи файлов (сложно манипулировать вручную)
  3. Поскольку это кеш, а не репозиторий, Gradle не обязан хранить вещи постоянно

Чтобы обратиться к пунктам 2 и 3 выше, я предлагаю использовать Git (а также резервные копии), чтобы помочь вам победить демон очистки кэша Gradle и избежать ошибки: "нет кэшированной версии для автономного режима". Если у вас есть полностью заполненный кэш зависимостей (сборки --offline работают), передайте кэш для контроля версий, чтобы вы могли восстановить его позже, если необходимо (например, с помощью git stash, чтобы отменить все изменения).


Папка ~/.gradle должна по-прежнему работать как символическая ссылка на другое место в вашей файловой системе, если это упрощает управление проектом или резервное копирование. Может также быть некоторое улучшение этой схемы, доступное с помощью --project-cache-dir, но я еще не пытался это сделать.


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