Gradle настраиваемый плагин: добавьте зависимость от объекта расширения

Я пытаюсь написать плагин, который добавляет зависимости project.dependencies в соответствии с информацией, собранной в объекте расширения плагина. Но это кажется невозможным.

Действительно, данные из объекта расширения доступны только в новой задаче или в закрытии project.afterEvaluate, но зависимости, добавленные в этих местах, игнорируются.


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

apply plugin: MyPlugin

myplugin {
  version '1.0'
}

class MyPlugin implements Plugin<Project> {
  void apply(Project project) {
    project.extensions.create('myplugin', MyPluginExtension)

    project.afterEvaluate {
       def version = project.myplugin.version
       project.dependencies.add("compile", "org.foo:bar:$version") // --> ignored
    }
  }
}

class MyPluginExtension {
  def version
}

В следующем коде инжекция зависимостей работает, но у меня нет доступа к объекту расширения:

apply plugin: MyPlugin

myplugin {
  version '1.0'
}

class MyPlugin implements Plugin<Project> {
  void apply(Project project) {
    project.extensions.create('myplugin', MyPluginExtension)    

    def version = project.myplugin.version // == null
    project.dependencies.add("compile", "org.foo:bar:$version") // --> fail because $version is null

  }
}

class MyPluginExtension {
  def version
}

Есть ли решение?

Ответ 1

Обновление. Мне удалось понять это с момента моего первоначального ответа. Как это сделать, добавьте DependencyResolutionListener, в который вы добавляете зависимости, а затем удаляете прослушиватель, чтобы он не пытался добавлять их на последующих шагах разрешения.

compileDeps = project.getConfigurations().getByName("compile").getDependencies()
project.getGradle().addListener(new DependencyResolutionListener() {
    @Override
    void beforeResolve(ResolvableDependencies resolvableDependencies) {
        compileDeps.add(project.getDependencies().create("org.foo:bar:$version"))
        project.getGradle().removeListener(this)
    }

    @Override
    void afterResolve(ResolvableDependencies resolvableDependencies) {}
})

У меня есть рабочий пример плагина, который использует этот здесь

Исходный ответ:

Это также поздно, но для тех, кто заходит. С последним gradle (2.6 на момент написания) вы можете добавить DependencyResolutionListener и добавить любые зависимости до того, как будут решены зависимости.

project.getGradle().addListener(new DependencyResolutionListener() {
    @Override
    void beforeResolve(ResolvableDependencies resolvableDependencies) {
        depsToAdd.each { dep ->
            compileConfig.getDependencies()
                .add(project.getDependencies().create(dep))
        }
    }

    @Override
    void afterResolve(ResolvableDependencies resolvableDependencies) {

    }
})

Однако, на момент написания этой статьи у меня возникли некоторые проблемы с тем, чтобы работать с Android Studio IDE. Проблема отслеживается в моем вопросе здесь

Ответ 2

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

project.configurations.compile.each {
   ...
}

Но это происходит, конечно, потому что compile - известная конфигурация для любого проекта, который использует плагин java. Однако, если вы используете настраиваемую конфигурацию (как и я), тогда подход слушателя не будет работать, если вы явно не перейдете по своей пользовательской конфигурации.

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

project.afterEvaluate {
    def version = project.myPlugin.version
    project.configurations.myConfig.dependencies.add(
        project.dependencies.add("myConfig", "org.foo:bar:$version")
    )
}

Конечно, в какой-то момент что-то по-прежнему должно перебирать зависимости, чтобы они действительно могли быть решены.

Ответ 3

Не знаю, насколько это актуально, но вы можете обойти это, явно добавив конфигурацию компиляции в путь Java classpath в doFirst:

variant.javaCompile.doFirst {
    variant.javaCompile.classpath += project.configurations.myconfiguration
}

Ответ 4

Самый простой способ сделать это:

project.dependencies {
  delegate.compile("com.android.support:appcompat-v7:25.0.1")
}