Недавно я начал разрушать монолит, который унаследовал на своем нынешнем месте работы. Чтобы упростить задачу для себя и для остальной команды, которой придется ее поддерживать, я создал три плагина соглашения в buildSrc
. Gradle вызывает эти предварительно скомпилированные плагины скриптов , и они могут быть написаны либо на Groovy, либо на Kotlin. Приведенные ниже примеры приведены в Kotlin. Давайте посмотрим!
buildSrc/build.gradle.kts
plugins { `kotlin-dsl` } repositories { jcenter() google() } dependencies { implementation("com.android.tools.build:gradle:4.0.1") implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72") }
Это дополнение к минимальному минимуму, который вам понадобится, если у вас есть плагины для конвенций, которые применяют любой из плагинов Android или Kotlin.
buildSrc/src/main/kotlin/android-library-convention.gradle.kts
plugins { id("com.android.library") id("kotlin-android") } android { compileSdkVersion(30) defaultConfig { minSdkVersion(21) targetSdkVersion(30) versionCode = 1 versionName = "1" } compileOptions { targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" } testOptions { unitTests.isReturnDefaultValues = true unitTests.isIncludeAndroidResources = true } } dependencies { implementation(platform(project(":platform"))) implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") }
Любой разработчик Android распознал бы этот скрипт, поскольку это именно то, что они использовали бы, если бы использовали Kotlin DSL и просто создали скрипт на месте, примерно в android-lib-module/build.gradle.kts
. Теперь вы можете применить это следующим образом:
android-lib-модуль/build.gradle.kts
plugins { `android-library-convention` }
И это все! Вы, конечно, можете настроить его так, как вам нравится, например, добавив зависимости, необходимые этому модулю (но не необходимые для всех модулей и, следовательно, не являющиеся частью плагина соглашения).
Внимательные читатели наверняка заметили в плагине что-то слегка необычное:
dependencies { implementation(platform(project(":platform"))) }
Это относится к проекту Java Platform и является текущей передовой практикой для управления и централизации версий зависимостей. Обсуждение плагина платформы Java выходит за рамки этой статьи, но я включаю его здесь в качестве примера того, как вы можете использовать этот подход к плагину соглашения для уменьшения шаблонности — без этого вам нужно было бы добавить implementation(platform(project(":platform")))
к каждая библиотека Android создает скрипт сборки, который легко забыть.
Это очень просто:
buildSrc/src/main/kotlin/java-library-convention.gradle.kts
plugins { `java-library` } java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } dependencies { implementation(platform(project(":platform"))) }
И применяется, как и раньше: java-lib-module/build.gradle.kts
plugins { `java-library-convention` }
Что мне особенно нравится в этом, так это то, что мне не нужно забывать добавлять Совместимость с источниками
и targetCompatibility
для каждого модуля библиотеки Java, который у меня есть.
Это тоже очень просто:
buildSrc/src/main/kotlin/kotlin-библиотека-convention.gradle.kts
plugins { id("org.jetbrains.kotlin.jvm") } tasks.withType().configureEach { kotlinOptions { jvmTarget = "1.8" } } dependencies { implementation(platform(project(":platform"))) implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") }
И применяется, как и раньше: kotlin-lib-module/build.gradle.kts
plugins { `kotlin-library-convention` }
Честно говоря, я даже не знаю, сколько раз я создавал новый модуль Kotlin и забывал добавить stdlib. В IDE все выглядит нормально, но удачи в его компиляции!
Причина, по которой я решил показать три отдельных примера, заключается в том, что я придаю большое значение ценностям конкретности и минимализма. 1 Мы могли бы еще больше сократить шаблонность, если бы остановились после написания нашего android-library-convention
плагин. В конце концов, он уже знает, как компилировать проекты Android, библиотеки Java и библиотеки Kotlin.
Вероятно, самая большая проблема, связанная с упрощением до такой степени, заключается в негативном влиянии, которое это окажет на производительность сборки. Создание библиотек Android намного сложнее и медленнее, чем создание библиотек Java. Создание библиотек Kotlin также происходит медленнее, чем создание библиотек Java. И обработка аннотаций происходит намного медленнее, чем без обработки аннотаций! (Пожалуйста, не применяйте kotlin-kapt
если только вы на самом деле не нуждаетесь в обработке аннотаций Kotlin.) Применяйте только то, что вам нужно, и не более: плагины не бесплатны.
С точки зрения архитектуры программного обеспечения также полезно быть конкретным. Применяя только плагин JVM, вы указываете, что он предназначен как библиотека JVM, и вы усложняете его (хорошая вещь!) для следующего разработчика, переходящего к импорту android.content. Контекст
.
Зачем останавливаться на достигнутом? Я регулярно вижу, как люди спрашивают о следующем сценарии:
У меня гетерогенная сборка с модулями приложений Android, модулями библиотеки Android и модулями JVM (Java/Kotlin). Я хочу иметь возможность запускать что-то вроде ./gradlew test
и запускать все важные тестовые задачи во всех модулях. Как я могу это сделать?
Давайте обновим наши плагины соглашения, чтобы показать, как упростить обработку этого сценария.
Давайте обновим наши плагины соглашения, чтобы показать, как упростить обработку этого сценария.
... tasks.register("mainTest") { dependsOn("testDebugUnitTest") // for example }
Давайте обновим наши плагины соглашения, чтобы показать, как упростить обработку этого сценария.
... tasks.register("mainTest") { dependsOn("test") }
(И то же самое для kotlin-library-convention
.)
Теперь вы можете легко воспользоваться одним из удобств Gradle 2 и выполнить
./gradlew mainTest
Когда вы это сделаете, Gradle выполнит основной тест
задача в каждом модуле, в котором она есть. Если в модуле его нет, все в порядке; ошибка будет только в том случае, если задача отсутствует в any module.
Стоит отметить, что следующее было бы не желательно:
./gradlew test testDebugUnitTest
Вы можете подумать, что это позволяет выполнить то же самое за счет всего лишь еще одного слова в командной строке, без необходимости добавлять задачу в каждый модуль. К сожалению, вы столкнетесь с тем фактом, что Android projects do имеют задачу с именем “test”! Вот часть результатов выполнения ./gradlew приложение: test -- dry-run
для моего небольшого проекта (с двумя типами сборки и двумя вариантами продукта):
... :app:preProductionReleaseBuild SKIPPED :app:compileProductionReleaseAidl SKIPPED :app:compileProductionReleaseRenderscript SKIPPED :app:generateProductionReleaseBuildConfig SKIPPED :app:generateProductionReleaseResValues SKIPPED :app:generateProductionReleaseResources SKIPPED :app:injectCrashlyticsMappingFileIdProductionRelease SKIPPED :app:processProductionReleaseGoogleServices SKIPPED :app:mergeProductionReleaseResources SKIPPED :app:createProductionReleaseCompatibleScreenManifests SKIPPED :app:extractDeepLinksProductionRelease SKIPPED :app:processProductionReleaseManifest SKIPPED :app:processProductionReleaseResources SKIPPED :app:kaptGenerateStubsProductionReleaseKotlin SKIPPED :app:kaptProductionReleaseKotlin SKIPPED :app:compileProductionReleaseKotlin SKIPPED :app:javaPreCompileProductionRelease SKIPPED :app:compileProductionReleaseJavaWithJavac SKIPPED :app:kaptGenerateStubsProductionReleaseUnitTestKotlin SKIPPED :app:kaptProductionReleaseUnitTestKotlin SKIPPED :app:compileProductionReleaseUnitTestKotlin SKIPPED :app:preProductionReleaseUnitTestBuild SKIPPED :app:javaPreCompileProductionReleaseUnitTest SKIPPED :app:compileProductionReleaseUnitTestJavaWithJavac SKIPPED :app:mergeProductionReleaseShaders SKIPPED :app:compileProductionReleaseShaders SKIPPED :app:generateProductionReleaseAssets SKIPPED :app:mergeProductionReleaseAssets SKIPPED :app:packageProductionReleaseUnitTestForUnitTest SKIPPED :app:generateProductionReleaseUnitTestConfig SKIPPED :app:processProductionReleaseJavaRes SKIPPED :app:processProductionReleaseUnitTestJavaRes SKIPPED :app:testProductionReleaseUnitTest SKIPPED :app:test SKIPPED
Это даже не одна десятая его части. Если вы еще этого не знали, вы бы быстро узнали, что test
– это, по сути, псевдоним для каждого варианта модульного теста.
В качестве последнего замечания, если вы также хотите каким-то образом объединить все отчеты о тестировании, это немного более сложная проблема, которую я мог бы решить в следующем посте.
Я надеюсь, что этого небольшого примера будет достаточно, чтобы показать мощь и полезность стандартных плагинов и задач.
1 Однако не в концевых сносках. Чем больше, тем веселее! вверх 2 Gradle отлично удобен. вверх
Оригинал: “https://dev.to/autonomousapps/easy-modularity-keeping-your-gradle-build-scripts-clean-and-eliminating-duplication-in-your-multi-module-projects-3pa”