Блок buildscript

Каждый разработчик, использующий Gradle в качестве основного инструмента сборки, знает о блоке buildscript. Или хотя бы знает, как им пользоваться. Прежде чем я углублюсь в то, почему мы должны избавиться от этого, я хочу объяснить, что это такое на самом деле и для чего он нужен.

buildscript {
  repositories {
    google()
    jcenter()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:3.1.1'
  }
}

Приведенный выше файл build.gradle автоматически создается Android Studio каждый раз, когда вы создаете новый проект (без Kotlin), и находится в каталоге верхнего уровня вашего проекта.

Но что делает этот блок? По сути, он определяет зависимости, которые требуются для самой сборки. Или, другими словами: он определяет зависимости, которые требуются для создания вашего приложения (каким бы ни было ваше «приложение» (приложение для Android, подключаемый модуль Gradle, настольное приложение Swift…)).

В нашем примере Android мы должны определить зависимость Android Gradle Plugin, которая помогает нам создавать APK:

// project/build.gradle
buildscript {
  repositories {
    google()
    jcenter()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:3.1.1'
  }
}
// project/app/build.gradle.kts
apply {
  plugin("com.android.application")
}

Поскольку мы знаем, что он делает, почему мы должны отказываться от него? 🤔

Ну, я не говорил, что нам больше не нужна логика, лежащая в основе этого (применить зависимости, необходимые для сборки). Я скорее говорю, что мы должны заменить старый блок buildscript чем-то «новым».

Возвышение блока плагинов

Новый блок plugins на самом деле не нов. Это уже было введено в Gradle 2.1. В то время это явно не привлекало особого внимания.

Тем не мение. Блок плагинов удобнее для чтения и использования:

// project/app/build.gradle.kts
plugins {
  id("org.gradle.java")
  kotlin("jvm") version "1.2.40"
}

Вы, наверное, заметили, что это только один файл вместо двух ?!

Но есть некоторые «ограничения». По умолчанию блок plugin может разрешать только плагины, которые:

К сожалению, не все плагины Gradle доступны на портале плагинов (посмотрите на вас, Android Gradle Plugin! 😡).

Как мы можем применять такие плагины, которые не являются «основными» и не публикуются на портале плагинов?

Для этого есть довольно приятный трюк. На мой взгляд, для этого требуется некоторый «шаблонный код» в settings.gradle.kts, но это стоит того:

pluginManagement {
  repositories {
    gradlePluginPortal()
    jcenter()
    google()
  }
  resolutionStrategy {
    eachPlugin {
      if (requested.id.id == "com.android.application") {
        useModule("com.android.tools.build:gradle:${requested.version}")
      }
    }
  }
}

Сначала мы должны определить все репозитории, в которых блок plugin должен искать плагины (порядок важен *). В блоке eachPlugin мы можем взглянуть на каждый примененный плагин (из id("name")) и вести себя на его основе. В нашем примере мы говорим что-то вроде:
«если id равно com.android.application, то используйте this в качестве модуля».

* Порядок на самом деле не «важен». Но Gradle будет искать примененные плагины в этих репозиториях в заданной последовательности. Возможно, вы захотите сначала добавить репозиторий с большинством доступных плагинов.

Вы можете спросить, что это за строка в useModule? Это строка classpath, которую мы использовали в устаревшем блоке buildscript.

Обладая этой информацией, Gradle знает, где искать и как искать там плагины в блоке plugins. Довольно круто, не так ли? 😎

Наконец, мы можем применить подключаемый модуль Android Gradle следующим образом:

plugins {
    id("com.android.application") version "3.1.1"
}

Пришло время меняться!

Проблема с версией

Gradle разработан для модульности. Это означает, что проект может содержать разные модули, которые могут применять разные (или одинаковые) плагины. Если вы это сделаете, вы можете закончить так:

// project/app/build.gradle.kts
plugins {
  id("com.android.application") version "3.1.1"
  kotlin("android") version "1.2.40"
}
// project/library1/build.gradle.kts
plugins {
  id("com.android.library") version "3.1.1"
  kotlin("android") version "1.2.40"
}
// project/library2/build.gradle.kts
plugins {
  id("com.android.library") version "3.1.1"
  kotlin("android") version "1.2.40"
  kotlin("kapt") version "1.2.40"
}

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

Интересный факт: как разработчик Android вы, возможно, никогда не касались settings.gradle или даже не знали, что он существует, верно? 😉

Помимо добавления только useModule (для отсутствующих плагинов на портале плагинов) мы можем дополнительно использовать useVersion. Для всех плагинов! Даже если они опубликованы на портале плагинов (что верно для всех плагинов Kotlin):

pluginManagement { 
  repositories { ... }
  eachPlugin { 
    if (requested.id.id.startsWith("com.android")) {
      useModule("com.android.tools.build:gradle:3.1.1")
    }
    if (requested.id.id.startsWith("org.jetbrains.kotlin")) {
      useVersion("1.2.31")
    }
  } 
}

С этим небольшим изменением мы можем применять наши плагины без определенной версии. Теперь Gradle знает конкретную версию для всех плагинов, которые начинаются с com.android и org.jetbrains.kotlin:

plugins {
  id("com.android.application") 
  kotlin("android") 
  kotlin("kapt")
}

Резюме

  • Удалите блок buildscript
  • Скопируйте и вставьте приведенный выше код в свой settings.gradle
  • Используйте блок plugins и танцуйте 🕺