diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/AssignStatement.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/AssignStatement.kt index 16f465ba..da465759 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/AssignStatement.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/AssignStatement.kt @@ -53,7 +53,7 @@ open class AssignStatement( private val key: Assignable, private val value: Assignee, private val assignmentOp: AssignmentOp = EQUAL -) : Statement { +) : Assignee { override fun write(level: Int, writer: PrintWriter) { indent(level, writer) if (!key.isEmpty()) { @@ -131,7 +131,7 @@ interface AssignmentBuilder { * A common pattern is to not add statements if a list is empty. This function only executes `block` if the given list * is not empty. */ - fun List<*>.notEmpty(block: () -> Unit) { + fun Collection<*>.notEmpty(block: () -> Unit) { if (isNotEmpty()) block() } diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/di/GrazelComponent.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/di/GrazelComponent.kt index 2e26129b..3c868c66 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/di/GrazelComponent.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/di/GrazelComponent.kt @@ -18,11 +18,7 @@ package com.grab.grazel.di import com.grab.grazel.GrazelExtension import com.grab.grazel.di.qualifiers.RootProject -import com.grab.grazel.gradle.AndroidVariantDataSource -import com.grab.grazel.gradle.AndroidVariantsExtractor import com.grab.grazel.gradle.ConfigurationDataSource -import com.grab.grazel.gradle.DefaultAndroidVariantDataSource -import com.grab.grazel.gradle.DefaultAndroidVariantsExtractor import com.grab.grazel.gradle.DefaultConfigurationDataSource import com.grab.grazel.gradle.DefaultGradleProjectInfo import com.grab.grazel.gradle.DefaultRepositoryDataSource @@ -35,6 +31,12 @@ import com.grab.grazel.gradle.dependencies.DependenciesGraphsBuilder import com.grab.grazel.gradle.dependencies.DependenciesModule import com.grab.grazel.gradle.dependencies.DependencyGraphs import com.grab.grazel.gradle.dependencies.MavenInstallArtifactsCalculator +import com.grab.grazel.gradle.variant.AndroidVariantDataSource +import com.grab.grazel.gradle.variant.AndroidVariantsExtractor +import com.grab.grazel.gradle.variant.DefaultAndroidVariantDataSource +import com.grab.grazel.gradle.variant.DefaultAndroidVariantsExtractor +import com.grab.grazel.gradle.variant.VariantBuilder +import com.grab.grazel.gradle.variant.VariantModule import com.grab.grazel.migrate.android.AndroidInstrumentationBinaryDataExtractor import com.grab.grazel.migrate.builder.AndroidBinaryTargetBuilderModule import com.grab.grazel.migrate.builder.AndroidInstrumentationBinaryTargetBuilderModule @@ -86,12 +88,14 @@ internal interface GrazelComponent { fun mavenInstallArtifactsCalculator(): Lazy fun androidInstrumentationBinaryDataExtractor(): Lazy + fun variantBuilder(): Lazy } @Module( includes = [ MigrationCriteriaModule::class, - DependenciesModule::class + DependenciesModule::class, + VariantModule::class ] ) internal interface GrazelModule { diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/BuildVariant.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/BuildVariant.kt index 00bacd72..a9052a4b 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/BuildVariant.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/BuildVariant.kt @@ -16,20 +16,7 @@ package com.grab.grazel.gradle -import com.android.build.gradle.AppExtension -import com.android.build.gradle.LibraryExtension import com.android.build.gradle.api.BaseVariant -import com.android.build.gradle.api.TestVariant -import com.android.build.gradle.api.UnitTestVariant -import com.android.builder.model.BuildType -import com.android.builder.model.ProductFlavor -import com.grab.grazel.extension.DefaultVariantFilter -import com.grab.grazel.extension.VariantFilter -import org.gradle.api.Action -import org.gradle.api.Project -import org.gradle.kotlin.dsl.the -import javax.inject.Inject -import javax.inject.Singleton sealed class VariantInfo { @@ -44,145 +31,4 @@ sealed class VariantInfo { data class AndroidVariant(val baseVariant: BaseVariant) : VariantInfo() { override fun toString(): String = baseVariant.name } -} - -internal interface AndroidVariantDataSource { - /** - * Variant filter instance to filter out unsupported variants - */ - val variantFilter: Action? - - /** - * This method will return the flavors which are ignored after evaluate the ignore variants - * determined by [variantFilter] - */ - fun getIgnoredFlavors(project: Project): List - - /** - * This method will return the variants which are ignored by the configuration determined by [variantFilter] - */ - fun getIgnoredVariants(project: Project): List - - /** - * @return The list of variants that can be migrated. - */ - fun getMigratableVariants(project: Project): List - - /** - * return all variants minus the ones that declared in filtered variants - */ - fun getMigratableVariants( - project: Project, - configurationScope: ConfigurationScope? - ): Set -} - -internal class DefaultAndroidVariantDataSource( - private val androidVariantsExtractor: AndroidVariantsExtractor, - override val variantFilter: Action? = null, -) : AndroidVariantDataSource { - - override fun getMigratableVariants( - project: Project, - configurationScope: ConfigurationScope? - ): Set { - return when (configurationScope) { - ConfigurationScope.TEST -> { - androidVariantsExtractor.getUnitTestVariants(project) - } - ConfigurationScope.ANDROID_TEST -> { - androidVariantsExtractor.getTestVariants(project) - } - else -> { - androidVariantsExtractor.getVariants(project) - } - }.filterNot(::ignoredVariantFilter).toSet() - } - - override fun getIgnoredFlavors(project: Project): List { - val supportFlavors = getMigratableVariants(project).flatMap(BaseVariant::getProductFlavors) - return androidVariantsExtractor.getFlavors(project) - .filter { flavor -> !supportFlavors.any { it.name == flavor.name } } - } - - private fun Project.androidVariants() = - androidVariantsExtractor.getVariants(this) + - androidVariantsExtractor.getUnitTestVariants(this) - - override fun getIgnoredVariants(project: Project): List { - return project.androidVariants().filter(::ignoredVariantFilter) - } - - override fun getMigratableVariants(project: Project): List { - return project.androidVariants().filterNot(::ignoredVariantFilter) - } - - private fun ignoredVariantFilter( - variant: BaseVariant - ): Boolean = DefaultVariantFilter(variant) - .apply { variantFilter?.execute(this) } - .ignored -} - -internal interface AndroidVariantsExtractor { - fun getUnitTestVariants(project: Project): Set - fun getTestVariants(project: Project): Set - fun getVariants(project: Project): Set - fun getFlavors(project: Project): Set - fun getBuildTypes(project: Project): Set -} - - -@Singleton -internal class DefaultAndroidVariantsExtractor @Inject constructor() : AndroidVariantsExtractor { - - private val Project.isAndroidAppOrDynFeature get() = project.isAndroidApplication || project.isAndroidDynamicFeature - - override fun getVariants(project: Project): Set { - return when { - project.isAndroidAppOrDynFeature -> project.the().applicationVariants - project.isAndroidLibrary -> project.the().libraryVariants - else -> emptySet() - } - } - - override fun getTestVariants(project: Project): Set { - return when { - project.isAndroidAppOrDynFeature -> project.the().testVariants - project.isAndroidLibrary -> project.the().testVariants - else -> emptySet() - } - } - - override fun getUnitTestVariants(project: Project): Set { - return when { - project.isAndroidAppOrDynFeature -> project.the().unitTestVariants - project.isAndroidLibrary -> project.the().unitTestVariants - else -> emptySet() - } - } - - override fun getFlavors(project: Project): Set { - return when { - project.isAndroidAppOrDynFeature -> project.the().productFlavors - project.isAndroidLibrary -> project.the().productFlavors - else -> emptySet() - } - } - - override fun getBuildTypes(project: Project): Set { - return when { - project.isAndroidAppOrDynFeature -> project.the().buildTypes - project.isAndroidLibrary -> project.the().buildTypes - else -> emptySet() - } - } -} - -internal fun AndroidVariantDataSource.getMigratableBuildVariants(project: Project): List = - getMigratableVariants(project) - .filter { it !is UnitTestVariant && it !is TestVariant } - -internal fun AndroidVariantDataSource.getMigratableUnitTestVariants(project: Project): List = - getMigratableVariants(project) - .filterIsInstance() \ No newline at end of file +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/Configuration.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/Configuration.kt index a23124ce..164515f2 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/Configuration.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/Configuration.kt @@ -21,6 +21,7 @@ import com.grab.grazel.GrazelExtension import com.grab.grazel.gradle.VariantInfo.AndroidFlavor import com.grab.grazel.gradle.VariantInfo.AndroidVariant import com.grab.grazel.gradle.dependencies.BuildGraphType +import com.grab.grazel.gradle.variant.AndroidVariantDataSource import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.findKaptConfiguration diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/Project.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/Project.kt index 0dba1c8f..4799ff30 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/Project.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/Project.kt @@ -29,6 +29,7 @@ const val KOTLIN_PLUGIN = "kotlin" const val KOTLIN_ANDROID_PLUGIN = "kotlin-android" const val KOTLIN_ANDROID_EXTENSION = "kotlin-android-extensions" const val KOTLIN_PARCELIZE = "kotlin-parcelize" +const val KOTLIN_KAPT = "kotlin-kapt" const val ANDROID_APPLICATION_PLUGIN = "com.android.application" const val ANDROID_LIBRARY_PLUGIN = "com.android.library" const val ANDROID_DYNAMIC_FEATURE = "com.android.dynamic-feature" @@ -59,6 +60,7 @@ val Project.hasKotlinAndroidExtensions get() = plugins.hasPlugin(KOTLIN_ANDROID_EXTENSION) || plugins.hasPlugin(KOTLIN_PARCELIZE) val Project.isKotlin get() = isKotlinJvm || isKotlinAndroid +val Project.hasKapt get() = plugins.hasPlugin(KOTLIN_KAPT) val Project.hasTestInstrumentationRunner get() = !extensions @@ -71,6 +73,7 @@ const val JAVA_LIBRARY_PLUGIN = "java-library" const val APPLICATION = "application" val Project.isJava get() = plugins.hasPlugin(JAVA_PLUGIN) || plugins.hasPlugin(JAVA_LIBRARY_PLUGIN) +val Project.isJvm get() = isKotlinJvm || isJava /** * @return True if the given project is migrated to Bazel. Calculated by checking for presence of `BUILD.bazel` file for diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/Dependencies.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/Dependencies.kt index 155621e3..14fbc465 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/Dependencies.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/Dependencies.kt @@ -20,11 +20,11 @@ import com.grab.grazel.GrazelExtension import com.grab.grazel.bazel.rules.MavenInstallArtifact import com.grab.grazel.bazel.rules.MavenInstallArtifact.Exclusion.SimpleExclusion import com.grab.grazel.di.qualifiers.RootProject -import com.grab.grazel.gradle.AndroidVariantsExtractor import com.grab.grazel.gradle.ConfigurationDataSource import com.grab.grazel.gradle.ConfigurationScope import com.grab.grazel.gradle.RepositoryDataSource import com.grab.grazel.gradle.configurationScopes +import com.grab.grazel.gradle.variant.AndroidVariantsExtractor import com.grab.grazel.util.GradleProvider import org.gradle.api.Project import org.gradle.api.artifacts.Configuration @@ -41,7 +41,7 @@ import javax.inject.Singleton /** * Maven group names for artifacts that should be excluded from dependencies calculation everywhere. */ -internal val DEP_GROUP_EMBEDDED_BY_RULES = listOf( +internal val IGNORED_ARTIFACT_GROUPS = listOf( "com.android.tools.build", "org.jetbrains.kotlin" ) @@ -296,7 +296,7 @@ internal class DefaultDependenciesDataSource @Inject constructor( override fun hasIgnoredArtifacts(project: Project): Boolean { return project.firstLevelModuleDependencies() .flatMap { (listOf(it) + it.children).asSequence() } - .filter { !DEP_GROUP_EMBEDDED_BY_RULES.contains(it.moduleGroup) } + .filter { !IGNORED_ARTIFACT_GROUPS.contains(it.moduleGroup) } .any { MavenArtifact(it.moduleGroup, it.moduleName).isIgnored } } @@ -317,7 +317,7 @@ internal class DefaultDependenciesDataSource @Inject constructor( } } .map { it.second } - .filter { it.group != null && !DEP_GROUP_EMBEDDED_BY_RULES.contains(it.group) } + .filter { it.group != null && !IGNORED_ARTIFACT_GROUPS.contains(it.group) } .filter { val artifact = MavenArtifact(it.group, it.name) !artifact.isExcluded && !artifact.isIgnored @@ -384,7 +384,7 @@ internal class DefaultDependenciesDataSource @Inject constructor( /** * Collects first level module dependencies from their resolved configuration. Additionally, excludes any artifacts - * that are not meant to be used in Bazel as defined by [DEP_GROUP_EMBEDDED_BY_RULES] + * that are not meant to be used in Bazel as defined by [IGNORED_ARTIFACT_GROUPS] * * @return Sequence of [DefaultResolvedDependency] in the first level */ @@ -401,7 +401,7 @@ internal class DefaultDependenciesDataSource @Inject constructor( sequenceOf() } }.filterIsInstance() - .filter { !DEP_GROUP_EMBEDDED_BY_RULES.contains(it.moduleGroup) } + .filter { !IGNORED_ARTIFACT_GROUPS.contains(it.moduleGroup) } } internal fun firstLevelModuleDependencies(project: Project) = diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/DependenciesGraphsBuilder.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/DependenciesGraphsBuilder.kt index aa9d37cf..d4b3dd89 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/DependenciesGraphsBuilder.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/DependenciesGraphsBuilder.kt @@ -21,12 +21,12 @@ import com.google.common.graph.MutableValueGraph import com.google.common.graph.ValueGraphBuilder import com.grab.grazel.di.qualifiers.RootProject import com.grab.grazel.extension.TestExtension -import com.grab.grazel.gradle.AndroidVariantDataSource import com.grab.grazel.gradle.ConfigurationDataSource import com.grab.grazel.gradle.ConfigurationScope import com.grab.grazel.gradle.isAndroid import com.grab.grazel.gradle.isJava import com.grab.grazel.gradle.isKotlinJvm +import com.grab.grazel.gradle.variant.AndroidVariantDataSource import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import javax.inject.Inject diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/AndroidVariants.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/AndroidVariants.kt new file mode 100644 index 00000000..4e583d5a --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/AndroidVariants.kt @@ -0,0 +1,262 @@ +package com.grab.grazel.gradle.variant + +import com.android.build.gradle.api.BaseVariant +import com.android.builder.model.BaseConfig +import com.android.builder.model.BuildType +import com.android.builder.model.ProductFlavor +import com.google.common.base.MoreObjects +import com.grab.grazel.gradle.variant.Classpath.Compile +import com.grab.grazel.gradle.variant.Classpath.Runtime +import com.grab.grazel.gradle.variant.DefaultVariants.Default +import com.grab.grazel.gradle.variant.VariantType.AndroidBuild +import com.grab.grazel.gradle.variant.VariantType.AndroidTest +import com.grab.grazel.gradle.variant.VariantType.JvmBuild +import com.grab.grazel.gradle.variant.VariantType.Test +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration + +/** + * [Variant] implementation used to represent a Variant created by Android Gradle plugin which is + * usually a mix of flavor + build type. + */ +class AndroidVariant( + override val project: Project, + override val backingVariant: BaseVariant, +) : ConfigurationParsingVariant { + + override val name: String = backingVariant.name + override val variantType: VariantType = backingVariant.toVariantType() + + /** + * Calculate the base name from variant. Useful for parsing since configuration + * names are typically in camel case. + * + * Eg: + * androidTestPaidDebug -> PaidDebug + */ + override val baseName = backingVariant.baseName + .split('-') + .dropLast(1) + .joinToString(separator = "", transform = String::capitalize) + + override val extendsFrom: Set by lazy { + buildList { + add(Default.toString()) + addAll(backingVariant.productFlavors.map { it.name }) + add(backingVariant.buildType.name) + if (variantType.isTest) { + add(DefaultVariants.Test.toString()) + add(backingVariant.buildType.name + variantType.testSuffix) + } + }.filter { it != name }.toSet() + } + + override val compileConfiguration get() = setOf(backingVariant.compileConfiguration) + + override val runtimeConfiguration get() = setOf(backingVariant.runtimeConfiguration) + + override val annotationProcessorConfiguration + get() = parseAnnotationProcessorConfigurations( + fallback = backingVariant.annotationProcessorConfiguration + ) + + override fun toString() = MoreObjects.toStringHelper(this) + .add("project", project.path) + .add("name", name) + .add("variantType", variantType) + .toString() +} + +/** + * Type to represent a non Android's [BaseVariant] type. Both flavors and buildTypes are permuted + * to create variants, but we also need non-variant type that represents each [BuildType] and + * [ProductFlavor] but not the variants created by them, this type represents that. + * + * For parsing, [toIgnoreKeywords] is used to filter out any configuration that appears to configuration + * from a variant. For [BuildType] it will be all productFlavors and vice versa. + */ +abstract class AndroidNonVariant( + override val project: Project, + override val backingVariant: T, + override val variantType: VariantType, + private val toIgnoreKeywords: Set +) : ConfigurationParsingVariant where T : BaseConfig { + + override val name + get() = backingVariant.name + when (variantType) { + AndroidTest -> AndroidTest.name + Test -> Test.name + else -> "" + } + + override val baseName get() = backingVariant.name.capitalize() + + override val variantConfigurations: Set + get() = super.variantConfigurations + .asSequence() + .filter { config -> toIgnoreKeywords.none { config.name.contains(it, true) } } + .toSet() + + override val compileConfiguration by lazy { classpathConfiguration(classpath = Compile) } + + override val runtimeConfiguration by lazy { classpathConfiguration(classpath = Runtime) } + + override val annotationProcessorConfiguration: Set + get() { + val apConfig = "%sAnnotationProcessor" + val buildTypeConfigs = parseAnnotationProcessorConfigurations( + fallback = project.configurations[apConfig.format(baseName.toLowerCase())]!!, + ) + val flavorConfig = toIgnoreKeywords.flatMap { flavor -> + val namePattern = flavor + baseName + project.configurations[apConfig.format(namePattern)]?.let { fallback -> + parseAnnotationProcessorConfigurations( + fallback = fallback, + namePattern = namePattern, + basePattern = namePattern + ) + } ?: emptySet() + } + return (buildTypeConfigs + flavorConfig).toSet() + } + + override val kotlinCompilerPluginConfiguration get() = emptySet() + + override fun toString() = MoreObjects.toStringHelper(this) + .add("project", project.path) + .add("name", name) + .add("variantType", variantType) + .toString() +} + +/** + * A [Variant] implementation to denote a [BuildType] with [toIgnoreKeywords] set to product flavors + * + * @param project The project this build type belongs to + * @param backingVariant The [BuildType] of this variant + * @param variantType [BuildType] typically is not associated with certain source set alone however + * this is used to hint the type of source set for parsing. + * @param flavors The set of [ProductFlavor] names contained in the project, used for parsing to filter + * out any permutation of buildType + flavors + */ +class AndroidBuildType( + override val project: Project, + override val backingVariant: BuildType, + override val variantType: VariantType, + private val flavors: Set +) : AndroidNonVariant( + project = project, + backingVariant = backingVariant, + variantType = variantType, + toIgnoreKeywords = flavors +) { + override val extendsFrom: Set = buildList { + add(Default.toString()) + if (variantType.isTest) add(backingVariant.name) + if (variantType == Test) add(DefaultVariants.Test.toString()) + }.toSet() +} + +/** + * A [Variant] implementation to denote a [ProductFlavor] with [toIgnoreKeywords] set to buildTypes + * + * @param project The project this flavor belongs to + * @param backingVariant The [ProductFlavor] of this variant + * @param variantType [ProductFlavor] typically is not associated with certain source set type alone however + * this is used to hint the type of source set for parsing. + * @param buildTypes The set of [BuildType] names contained in the project, used for parsing to filter + * out any permutation of buildType + flavors + */ +class AndroidFlavor( + override val project: Project, + override val backingVariant: ProductFlavor, + override val variantType: VariantType, + private val buildTypes: Set +) : AndroidNonVariant( + project = project, + backingVariant = backingVariant, + variantType = variantType, + toIgnoreKeywords = buildTypes +) { + override val extendsFrom: Set = buildList { + add(Default.toString()) + if (variantType.isTest) add(backingVariant.name) + if (variantType == Test) add(DefaultVariants.Test.toString()) + }.toSet() +} + +data class DefaultVariantData( + val project: Project, + val variantType: VariantType, + val ignoreKeywords: Set, + val name: String = when (variantType) { + AndroidBuild -> Default.toString() + else -> DefaultVariants.Test.toString() + }, +) + +fun AndroidDefaultVariant( + project: Project, + variantType: VariantType, + ignoreKeywords: Set, +) = AndroidDefaultVariant(DefaultVariantData(project, variantType, ignoreKeywords)) + +/** + * A [Variant] implementation to denote the default type i.e. without any build type or variant + * specific data. + */ +class AndroidDefaultVariant( + private val defaultVariantData: DefaultVariantData +) : ConfigurationParsingVariant { + override val name: String get() = defaultVariantData.name + override val baseName: String get() = "" + override val backingVariant: DefaultVariantData get() = defaultVariantData + override val project: Project get() = defaultVariantData.project + override val variantType: VariantType get() = defaultVariantData.variantType + override val extendsFrom: Set = setOf(Default.toString()) + + private val ignoreKeywords get() = defaultVariantData.ignoreKeywords + + override val variantConfigurations: Set + get() = project.configurations + .asSequence() + .filter { config -> + val name = config.name + ignoreKeywords.none { ignore -> name.contains(ignore, true) } + }.filter { + val configName = it.name + when (variantType) { + AndroidBuild -> !configName.isTest() + AndroidTest -> configName.isAndroidTest() + Test -> configName.isUnitTest() + JvmBuild -> error("Invalid variant type ${JvmBuild.name} for Android variant") + } + }.toSet() + + override val compileConfiguration: Set by lazy { + classpathConfiguration(Compile, namePattern = "", basePattern = "") + } + + override val runtimeConfiguration: Set by lazy { + classpathConfiguration(Runtime, namePattern = "", basePattern = "") + } + + override val annotationProcessorConfiguration: Set + get() = parseAnnotationProcessorConfigurations( + fallback = project.configurations["annotationProcessor"]!!, + "", + "" + ) + + override val kotlinCompilerPluginConfiguration: Set + get() = buildList { + project.configurations["kotlinCompilerPluginClasspath"]?.let(::add) + project.configurations["kotlin-extension"]?.let(::add) + }.toSet() + + override fun toString(): String = MoreObjects.toStringHelper(this) + .add("project", project.path) + .add("name", name) + .add("variantType", variantType) + .toString() +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/AndroidVariantsExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/AndroidVariantsExtractor.kt new file mode 100644 index 00000000..d281bc1a --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/AndroidVariantsExtractor.kt @@ -0,0 +1,76 @@ +package com.grab.grazel.gradle.variant + +import com.android.build.gradle.AppExtension +import com.android.build.gradle.LibraryExtension +import com.android.build.gradle.api.BaseVariant +import com.android.build.gradle.api.UnitTestVariant +import com.android.builder.model.BuildType +import com.android.builder.model.ProductFlavor +import com.grab.grazel.gradle.isAndroidApplication +import com.grab.grazel.gradle.isAndroidDynamicFeature +import com.grab.grazel.gradle.isAndroidLibrary +import org.gradle.api.Project +import org.gradle.kotlin.dsl.the +import javax.inject.Inject +import javax.inject.Singleton + +internal interface AndroidVariantsExtractor { + fun allVariants(project: Project): Set + fun getUnitTestVariants(project: Project): Set + fun getTestVariants(project: Project): Set + fun getVariants(project: Project): Set + fun getFlavors(project: Project): Set + fun getBuildTypes(project: Project): Set +} + +@Singleton +internal class DefaultAndroidVariantsExtractor +@Inject +constructor() : AndroidVariantsExtractor { + + private val Project.isAndroidAppOrDynFeature get() = project.isAndroidApplication || project.isAndroidDynamicFeature + + override fun allVariants(project: Project): Set { + return getVariants(project) + getTestVariants(project) + getUnitTestVariants(project) + } + + override fun getVariants(project: Project): Set { + return when { + project.isAndroidAppOrDynFeature -> project.the().applicationVariants + project.isAndroidLibrary -> project.the().libraryVariants + else -> emptySet() + } + } + + override fun getTestVariants(project: Project): Set { + return when { + project.isAndroidAppOrDynFeature -> project.the().testVariants + project.isAndroidLibrary -> project.the().testVariants + else -> emptySet() + } + } + + override fun getUnitTestVariants(project: Project): Set { + return when { + project.isAndroidAppOrDynFeature -> project.the().unitTestVariants + project.isAndroidLibrary -> project.the().unitTestVariants + else -> emptySet() + } + } + + override fun getFlavors(project: Project): Set { + return when { + project.isAndroidAppOrDynFeature -> project.the().productFlavors + project.isAndroidLibrary -> project.the().productFlavors + else -> emptySet() + } + } + + override fun getBuildTypes(project: Project): Set { + return when { + project.isAndroidAppOrDynFeature -> project.the().buildTypes + project.isAndroidLibrary -> project.the().buildTypes + else -> emptySet() + } + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/ConfigurationParsingVariant.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/ConfigurationParsingVariant.kt new file mode 100644 index 00000000..0581fb7a --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/ConfigurationParsingVariant.kt @@ -0,0 +1,122 @@ +package com.grab.grazel.gradle.variant + +import com.grab.grazel.gradle.hasKapt +import com.grab.grazel.gradle.variant.VariantType.AndroidBuild +import com.grab.grazel.gradle.variant.VariantType.AndroidTest +import com.grab.grazel.gradle.variant.VariantType.JvmBuild +import com.grab.grazel.gradle.variant.VariantType.Test +import com.grab.grazel.util.addTo +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ConfigurationContainer + +/** + * [Variant] extension that builds [Variant] configuration by parsing them via their names. + * + * Android Gradle Plugin creates configurations for permutations of build types and flavors, while + * there are certain patterns followed when naming these variants there is no type safe API to map + * a particular `variant` to the `configurations` belong to that variant. This class tries to parse + * this information by manually accounting for the configuration name patterns for known configuration + * types. + * + * @see AndroidVariant + * @see AndroidBuildType + * @see AndroidFlavor + * @see AndroidDefaultVariant + * @see AndroidNonVariant + */ +interface ConfigurationParsingVariant : Variant { + + /** + * The base name of [Variant], this is the actual name of the variant without any type information + * associate with it. For example, [Variant.name] of `androidTestPaidDebug` and `debug` would be\ + * `PaidDebug` and `Debug`. `androidTest` in this example is a variant type, implementing classes + * should accordingly filter types or any non relevant data and return only the actual name + * of the variant. + * + * Can return empty if baseName is not needed for parsing. + */ + val baseName: String + + operator fun ConfigurationContainer.get(name: String) = findByName(name) + + override val variantConfigurations: Set + get() = project.configurations.asSequence().filter { config -> + val configName = config.name + + val variantNameMatches = configName.contains(name) + || configName.contains(name.capitalize()) + + val androidTestMatches = configName.contains("AndroidTest$baseName", true) + val testMatches = configName.contains("UnitTest$baseName", true) || + configName.startsWith("test$baseName") || + configName.startsWith("kaptTest$baseName") || + configName.contains("TestFixtures", true) && configName.contains(baseName) + + when (variantType) { + AndroidBuild -> !configName.isTest() && variantNameMatches + AndroidTest -> configName.isAndroidTest() && (variantNameMatches || androidTestMatches) + Test -> configName.isUnitTest() && (variantNameMatches || testMatches) + else -> variantNameMatches + } + }.toSet() + + override val kotlinCompilerPluginConfiguration: Set + get() = buildList { + project.configurations["kotlinCompilerPluginClasspath${name.capitalize()}"]?.let(::add) + project.configurations["kotlin-extension"]?.let(::add) + }.toSet() + + fun parseAnnotationProcessorConfigurations( + fallback: Configuration, + namePattern: String = name, + basePattern: String = baseName, + ) = buildSet { + if (project.hasKapt) { + variantConfigurations.filter { configuration -> + val configName = configuration.name + when (variantType) { + AndroidBuild -> configName.startsWith("kapt${namePattern.capitalize()}") + AndroidTest -> configName.startsWith("kaptAndroidTest${basePattern.capitalize()}") + Test -> configName.startsWith("kaptTest${basePattern.capitalize()}") + JvmBuild -> error("Invalid variant type ${JvmBuild.name} for Android variant") + } + }.addTo(this) + } else add(fallback) + } + + fun classpathConfiguration( + classpath: Classpath, + namePattern: String = name, + basePattern: String = baseName, + ): Set { + val onlyConfig = when (classpath) { + Classpath.Runtime -> "RuntimeOnly" + Classpath.Compile -> "CompileOnly" + } + val dm = "DependenciesMetadata" + return variantConfigurations.filter { + val configName = it.name.toLowerCase() + when (variantType) { + AndroidBuild -> configName == "${namePattern}${onlyConfig}$dm".toLowerCase() + || configName == "${namePattern}Implementation$dm".toLowerCase() + + AndroidTest -> configName == "androidTest${basePattern}${onlyConfig}$dm".toLowerCase() + || configName == "androidTest${basePattern}Implementation$dm".toLowerCase() + + Test -> configName == "test${basePattern}${onlyConfig}$dm".toLowerCase() + || configName == "test${basePattern}Implementation$dm".toLowerCase() + + else -> error("$JvmBuild invalid for build type runtime configuration") + } + }.toSet() + } + + fun String.isAndroidTest() = startsWith("androidTest") + || contains("androidTest", true) + + fun String.isUnitTest() = startsWith("test") + || startsWith("kaptTest") + || contains("UnitTest") + + fun String.isTest() = isAndroidTest() || isUnitTest() || contains("Test") +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/Variant.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/Variant.kt new file mode 100644 index 00000000..7bbfd55c --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/Variant.kt @@ -0,0 +1,214 @@ +package com.grab.grazel.gradle.variant + +import com.android.build.gradle.api.ApplicationVariant +import com.android.build.gradle.api.BaseVariant +import com.android.build.gradle.api.LibraryVariant +import com.android.build.gradle.api.TestVariant +import com.android.build.gradle.api.UnitTestVariant +import com.google.common.base.MoreObjects +import com.grab.grazel.gradle.ConfigurationScope +import com.grab.grazel.gradle.ConfigurationScope.ANDROID_TEST +import com.grab.grazel.gradle.ConfigurationScope.BUILD +import com.grab.grazel.gradle.ConfigurationScope.TEST +import com.grab.grazel.gradle.hasKapt +import com.grab.grazel.gradle.isAndroid +import com.grab.grazel.gradle.variant.VariantType.AndroidBuild +import com.grab.grazel.gradle.variant.VariantType.AndroidTest +import com.grab.grazel.gradle.variant.VariantType.JvmBuild +import com.grab.grazel.gradle.variant.VariantType.Test +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration + +/** + * Base marker interface that denotes a variant that needs to be migrated and is used to + * encapsulate both Android and Jvm variants. + * + * Variants are meant to be the first extracted item from a [Project] instance for migration. + * @see VariantBuilder + * + * @param T The original backing variant type + */ +interface Variant { + val name: String + val backingVariant: T + + val project: Project + + val variantType: VariantType + + /** + * Variants can have a hierarchy and `extendsFrom` denotes the parent variants of this variant. + * + * For example, `debugUnitTest` extends from `debug`, 'default', and `test` variant. + */ + val extendsFrom: Set + + /** + * Return [Configuration]'s belonging only to this variant + */ + val variantConfigurations: Set + + val compileConfiguration: Set + + val runtimeConfiguration: Set + + val annotationProcessorConfiguration: Set + + val kotlinCompilerPluginConfiguration: Set +} + +enum class DefaultVariants(val variantName: String) { + Default("default") { + override fun toString() = variantName + }, + Test("test") { + override fun toString() = variantName + } +} + +enum class VariantType { + AndroidBuild, + AndroidTest, + Test, + JvmBuild, +} + +fun BaseVariant.toVariantType(): VariantType = when (this) { + is ApplicationVariant, is LibraryVariant -> AndroidBuild + is TestVariant -> AndroidTest + is UnitTestVariant -> Test + else -> error("Cannot parse $name to VariantType") +} + +/** + * Bridge function to map [ConfigurationScope] to [VariantType] + * Not required once fully migrated to [Variant] APIs + * + * @return whether this [VariantType] corresponds to [ConfigurationScope] + */ +fun VariantType.isConfigScope( + project: Project, + configurationScope: ConfigurationScope +) = when (configurationScope) { + BUILD -> this == if (project.isAndroid) AndroidBuild else JvmBuild + TEST -> this == Test + ANDROID_TEST -> this == AndroidTest +} + +val VariantType.isTest get() = this == Test || this == AndroidTest + +val VariantType.testSuffix + get() = when { + this == Test -> "UnitTest" + this == AndroidTest -> "AndroidTest" + else -> error("$this is not a test type!") + } + +/** + * Return the migratable configurations for this variant. Currently all configurations are merged. + * TODO("Migrate runtime, annotation processor and Kotlin compiler plugin configuration separately") + */ +val Variant<*>.migratableConfigurations + get() = (compileConfiguration + + runtimeConfiguration + + annotationProcessorConfiguration + + kotlinCompilerPluginConfiguration).toSet() + +enum class Classpath { + Runtime, + Compile +} + +class JvmVariantData( + val project: Project, + val variantType: VariantType, + val name: String = when (variantType) { + JvmBuild -> DefaultVariants.Default.toString() + else -> DefaultVariants.Test.toString() + } +) + +fun JvmVariant(project: Project, variantType: VariantType) = JvmVariant( + JvmVariantData( + project, + variantType + ) +) + +/** + * Jvm libraries don't have variants like Android projects do hence this type is used to encapsulate + * Jvm specific information in `Variant` class. + * + * @see DefaultVariants + */ +class JvmVariant( + private val jvmVariantData: JvmVariantData +) : Variant { + override val name: String get() = jvmVariantData.name + override val backingVariant: JvmVariantData get() = jvmVariantData + override val project: Project get() = jvmVariantData.project + override val variantType: VariantType get() = jvmVariantData.variantType + + override val variantConfigurations: Set + get() = project.configurations.filter { + when (variantType) { + Test -> it.name.contains("test", true) + else -> !it.name.contains("test", true) + } + }.toSet() + + override val extendsFrom: Set = emptySet() + + // Store name to configurations to avoid lookup cost for below configurations parsing + private val configurationNameMap = project.configurations.associateBy { it.name } + + override val compileConfiguration: Set + get() = setOf( + configurationNameMap.getValue( + when { + variantType.isTest -> "testCompileClasspath" + else -> "compileClasspath" + } + ) + ) + + override val runtimeConfiguration: Set + get() = setOf( + configurationNameMap.getValue( + when { + variantType.isTest -> "testRuntimeClasspath" + else -> "runtimeClasspath" + } + ) + ) + + override val annotationProcessorConfiguration: Set + get() = buildSet { + add( + if (project.hasKapt) when (variantType) { + JvmBuild -> configurationNameMap.getValue("kapt") + else -> configurationNameMap.getValue("kaptTest") + } else when (variantType) { + JvmBuild -> configurationNameMap.getValue("testAnnotationProcessor") + else -> configurationNameMap.getValue("annotationProcessor") + } + ) + } + + override val kotlinCompilerPluginConfiguration: Set + get() = buildSet { + val configName = "kotlinCompilerPluginClasspath" + add( + when (variantType) { + Test -> configurationNameMap.getValue("${configName}Test") + else -> configurationNameMap.getValue("${configName}Main") + } + ) + } + + override fun toString(): String = MoreObjects.toStringHelper(this) + .add("project", project.name) + .add("name", name) + .add("variantType", variantType) + .toString() +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantBuilder.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantBuilder.kt new file mode 100644 index 00000000..effb5c14 --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantBuilder.kt @@ -0,0 +1,103 @@ +package com.grab.grazel.gradle.variant + +import com.grab.grazel.gradle.isAndroid +import com.grab.grazel.gradle.isJvm +import com.grab.grazel.gradle.variant.VariantType.AndroidBuild +import com.grab.grazel.gradle.variant.VariantType.JvmBuild +import com.grab.grazel.gradle.variant.VariantType.Test +import org.gradle.api.Project +import java.util.concurrent.ConcurrentHashMap +import javax.inject.Inject +import javax.inject.Singleton + +/** + * [VariantBuilder] is used to construct unified [Set] of [Variant] types for Android/Jvm [Project] + * + * [VariantBuilder.build] caches constructed Variants and can be called multiple times for a project. + * + * @see Variant + */ +internal interface VariantBuilder { + fun build(project: Project): Set> +} + +@Singleton +internal class DefaultVariantBuilder +@Inject +constructor( + private val variantDataSource: AndroidVariantDataSource +) : VariantBuilder { + + /** + * [Variant] specific APIs can be often invoked at multiple places during migration hence + * we cache constructed [Variant]s and reuse when needed. + */ + private val variantCache = ConcurrentHashMap>>() + + override fun build(project: Project): Set> { + if (variantCache.contains(project.path)) return variantCache.getValue(project.path) else { + val variants = if (project.isAndroid) { + val migratableVariants = variantDataSource.getMigratableVariants(project) + val flavors = migratableVariants + .flatMap { it.productFlavors } + .map { it.name } + .toSet() + val buildTypes = migratableVariants + .map { it.buildType.name } + .toSet() + val flavorsBuildTypes = (flavors + buildTypes).toSet() + + val defaultVariants = listOf>( + AndroidDefaultVariant( + project = project, + variantType = AndroidBuild, + ignoreKeywords = flavorsBuildTypes + ), + AndroidDefaultVariant( + project = project, + variantType = Test, + ignoreKeywords = flavorsBuildTypes + ) + ) + + val parsedAndroidVariants: List> = + migratableVariants.flatMap { baseVariant -> + listOf( + AndroidVariant(project, baseVariant), + AndroidBuildType( + project, + baseVariant.buildType, + baseVariant.toVariantType(), + flavors + ) + ) + baseVariant.productFlavors.map { flavor -> + AndroidFlavor( + project, + flavor, + baseVariant.toVariantType(), + buildTypes + ) + } + } + (parsedAndroidVariants + defaultVariants) + .asSequence() + .distinctBy { it.name + it.variantType } + .sortedBy { it.name.length } + .toSet() + } else if (project.isJvm) { + setOf>( + JvmVariant( + project = project, + variantType = JvmBuild + ), + JvmVariant( + project = project, + variantType = Test + ) + ) + } else emptySet() + variantCache[project.path] = variants + return variants + } + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantDataSource.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantDataSource.kt new file mode 100644 index 00000000..d9fdac97 --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantDataSource.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2022 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.gradle.variant + +import com.android.build.gradle.api.BaseVariant +import com.android.build.gradle.api.TestVariant +import com.android.build.gradle.api.UnitTestVariant +import com.android.builder.model.ProductFlavor +import com.grab.grazel.extension.DefaultVariantFilter +import com.grab.grazel.extension.VariantFilter +import com.grab.grazel.gradle.ConfigurationScope +import org.gradle.api.Action +import org.gradle.api.Project + + +internal interface AndroidVariantDataSource { + /** + * Variant filter instance to filter out unsupported variants + */ + val variantFilter: Action? + + /** + * This method will return the flavors which are ignored after evaluate the ignore variants + * determined by [variantFilter] + */ + fun getIgnoredFlavors(project: Project): List + + /** + * This method will return the variants which are ignored by the configuration determined by [variantFilter] + */ + fun getIgnoredVariants(project: Project): List + + /** + * @return The list of variants that can be migrated. + */ + fun getMigratableVariants(project: Project): List + + /** + * @return all variants minus the ones that declared in filtered variants + */ + fun getMigratableVariants( + project: Project, + configurationScope: ConfigurationScope? + ): Set +} + +internal class DefaultAndroidVariantDataSource( + private val androidVariantsExtractor: AndroidVariantsExtractor, + override val variantFilter: Action? = null, +) : AndroidVariantDataSource { + + private fun Project.androidVariants() = + androidVariantsExtractor.getVariants(this) + + androidVariantsExtractor.getUnitTestVariants(this) + + androidVariantsExtractor.getTestVariants(this) + + override fun getMigratableVariants( + project: Project, + configurationScope: ConfigurationScope? + ): Set { + return when (configurationScope) { + ConfigurationScope.TEST -> androidVariantsExtractor.getUnitTestVariants(project) + ConfigurationScope.ANDROID_TEST -> androidVariantsExtractor.getTestVariants(project) + else -> androidVariantsExtractor.getVariants(project) + }.filterNot(::ignoredVariantFilter).toSet() + } + + override fun getIgnoredFlavors(project: Project): List { + val supportedFlavors = getMigratableVariants(project) + .flatMap(BaseVariant::getProductFlavors) + .map { it.name } + .distinct() + return androidVariantsExtractor.getFlavors(project) + .filter { flavor -> !supportedFlavors.any { it == flavor.name } } + } + + override fun getIgnoredVariants(project: Project): List { + return project.androidVariants().filter(::ignoredVariantFilter) + } + + override fun getMigratableVariants(project: Project): List { + return project.androidVariants().filterNot(::ignoredVariantFilter) + } + + private fun ignoredVariantFilter( + variant: BaseVariant + ): Boolean = DefaultVariantFilter(variant) + .apply { variantFilter?.execute(this) } + .ignored +} + +internal fun AndroidVariantDataSource.getMigratableBuildVariants(project: Project): List = + getMigratableVariants(project) + .filter { it !is UnitTestVariant && it !is TestVariant } \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantModule.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantModule.kt new file mode 100644 index 00000000..ce4d99b7 --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantModule.kt @@ -0,0 +1,10 @@ +package com.grab.grazel.gradle.variant + +import dagger.Binds +import dagger.Module + +@Module +internal interface VariantModule { + @Binds + fun DefaultVariantBuilder.bind(): VariantBuilder +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidBinaryDataExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidBinaryDataExtractor.kt index 81565dff..ab704d48 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidBinaryDataExtractor.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidBinaryDataExtractor.kt @@ -22,11 +22,11 @@ import com.grab.grazel.GrazelExtension import com.grab.grazel.bazel.rules.DATABINDING_ARTIFACTS import com.grab.grazel.bazel.rules.Multidex import com.grab.grazel.bazel.starlark.BazelDependency -import com.grab.grazel.gradle.AndroidVariantDataSource -import com.grab.grazel.gradle.getMigratableBuildVariants import com.grab.grazel.gradle.hasCrashlytics import com.grab.grazel.gradle.hasDatabinding import com.grab.grazel.gradle.hasGooglePlayServicesPlugin +import com.grab.grazel.gradle.variant.AndroidVariantDataSource +import com.grab.grazel.gradle.variant.getMigratableBuildVariants import org.gradle.api.Project import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency import org.gradle.kotlin.dsl.getByType diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidInstrumentationBinaryDataExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidInstrumentationBinaryDataExtractor.kt index 17a96f87..db9f3cfe 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidInstrumentationBinaryDataExtractor.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidInstrumentationBinaryDataExtractor.kt @@ -19,14 +19,14 @@ package com.grab.grazel.migrate.android import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.AndroidSourceSet import com.grab.grazel.bazel.starlark.BazelDependency -import com.grab.grazel.gradle.AndroidVariantDataSource import com.grab.grazel.gradle.ConfigurationScope import com.grab.grazel.gradle.dependencies.BuildGraphType import com.grab.grazel.gradle.dependencies.DependenciesDataSource import com.grab.grazel.gradle.dependencies.DependencyGraphs import com.grab.grazel.gradle.dependencies.GradleDependencyToBazelDependency import com.grab.grazel.gradle.dependencies.variantNameSuffix -import com.grab.grazel.gradle.getMigratableBuildVariants +import com.grab.grazel.gradle.variant.AndroidVariantDataSource +import com.grab.grazel.gradle.variant.getMigratableBuildVariants import dagger.Lazy import org.gradle.api.Project import org.gradle.kotlin.dsl.getByType diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidLibraryDataExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidLibraryDataExtractor.kt index 69ee7758..9f3d1878 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidLibraryDataExtractor.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidLibraryDataExtractor.kt @@ -24,7 +24,6 @@ import com.grab.grazel.bazel.rules.ANNOTATION_ARTIFACT import com.grab.grazel.bazel.rules.DAGGER_GROUP import com.grab.grazel.bazel.rules.DATABINDING_GROUP import com.grab.grazel.bazel.starlark.BazelDependency -import com.grab.grazel.gradle.AndroidVariantDataSource import com.grab.grazel.gradle.ConfigurationScope import com.grab.grazel.gradle.dependencies.BuildGraphType import com.grab.grazel.gradle.dependencies.DependenciesDataSource @@ -32,6 +31,7 @@ import com.grab.grazel.gradle.dependencies.DependencyGraphs import com.grab.grazel.gradle.dependencies.GradleDependencyToBazelDependency import com.grab.grazel.gradle.hasDatabinding import com.grab.grazel.gradle.isAndroid +import com.grab.grazel.gradle.variant.AndroidVariantDataSource import com.grab.grazel.migrate.dependencies.calculateDirectDependencyTags import com.grab.grazel.migrate.kotlin.kotlinParcelizeDeps import dagger.Lazy diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidUnitTestDataExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidUnitTestDataExtractor.kt index d6ebda7e..2acaafe7 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidUnitTestDataExtractor.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidUnitTestDataExtractor.kt @@ -19,14 +19,14 @@ import com.android.build.gradle.api.AndroidSourceSet import com.grab.grazel.GrazelExtension import com.grab.grazel.bazel.starlark.BazelDependency import com.grab.grazel.extension.KotlinExtension -import com.grab.grazel.gradle.AndroidVariantDataSource import com.grab.grazel.gradle.ConfigurationScope import com.grab.grazel.gradle.dependencies.BuildGraphType import com.grab.grazel.gradle.dependencies.DependenciesDataSource import com.grab.grazel.gradle.dependencies.DependencyGraphs import com.grab.grazel.gradle.dependencies.GradleDependencyToBazelDependency import com.grab.grazel.gradle.dependencies.variantNameSuffix -import com.grab.grazel.gradle.getMigratableBuildVariants +import com.grab.grazel.gradle.variant.AndroidVariantDataSource +import com.grab.grazel.gradle.variant.getMigratableBuildVariants import com.grab.grazel.migrate.common.calculateTestAssociate import com.grab.grazel.migrate.dependencies.calculateDirectDependencyTags import com.grab.grazel.migrate.kotlin.kotlinParcelizeDeps diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/Crashlytics.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/Crashlytics.kt index aeb1c918..8dc0e05f 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/Crashlytics.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/Crashlytics.kt @@ -19,8 +19,8 @@ package com.grab.grazel.migrate.android import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.AndroidSourceSet import com.grab.grazel.GrazelExtension -import com.grab.grazel.gradle.AndroidVariantDataSource -import com.grab.grazel.gradle.getMigratableBuildVariants +import com.grab.grazel.gradle.variant.AndroidVariantDataSource +import com.grab.grazel.gradle.variant.getMigratableBuildVariants import org.gradle.api.Project import org.gradle.kotlin.dsl.getByType import javax.inject.Inject diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/ManifestValuesBuilder.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/ManifestValuesBuilder.kt index f9c06d44..9e598fe0 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/ManifestValuesBuilder.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/ManifestValuesBuilder.kt @@ -19,12 +19,12 @@ package com.grab.grazel.migrate.android import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.internal.dsl.DefaultConfig -import com.grab.grazel.gradle.AndroidVariantDataSource import com.grab.grazel.gradle.ConfigurationScope import com.grab.grazel.gradle.dependencies.BuildGraphType import com.grab.grazel.gradle.dependencies.DependencyGraphs -import com.grab.grazel.gradle.getMigratableBuildVariants +import com.grab.grazel.gradle.variant.getMigratableBuildVariants import com.grab.grazel.gradle.isAndroid +import com.grab.grazel.gradle.variant.AndroidVariantDataSource import dagger.Lazy import org.gradle.api.Project import org.gradle.kotlin.dsl.the diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/VariantsMerger.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/VariantsMerger.kt index a9fcd48e..98451da7 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/VariantsMerger.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/VariantsMerger.kt @@ -21,9 +21,9 @@ import com.android.build.gradle.api.BaseVariant import com.android.builder.model.BuildType import com.android.builder.model.ProductFlavor import com.grab.grazel.di.qualifiers.RootProject -import com.grab.grazel.gradle.AndroidVariantDataSource import com.grab.grazel.gradle.ConfigurationScope import com.grab.grazel.gradle.isAndroidApplication +import com.grab.grazel.gradle.variant.AndroidVariantDataSource import org.gradle.api.Project import org.gradle.kotlin.dsl.the import javax.inject.Inject diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallData.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallData.kt new file mode 100644 index 00000000..e8c346c0 --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallData.kt @@ -0,0 +1,20 @@ +package com.grab.grazel.migrate.dependencies + +import com.grab.grazel.bazel.rules.MavenInstallArtifact +import com.grab.grazel.bazel.rules.MavenRepository +import com.grab.grazel.migrate.android.JetifierConfig + +internal data class MavenInstallData( + val name: String, + val artifacts: Set, + val externalArtifacts: Set, + val repositories: Set, + val externalRepositories: Set, + val jetifierData: JetifierConfig, + val failOnMissingChecksum: Boolean, + val resolveTimeout: Int, + val overrideTargets: Map, + val excludeArtifacts: Set, + val artifactPinning: Boolean, + val versionConflictPolicy: String? +) \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenRepo.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenRepo.kt new file mode 100644 index 00000000..85048979 --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenRepo.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2023 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.migrate.dependencies + +import com.grab.grazel.gradle.variant.DefaultVariants.Default + +fun String.toMavenRepoName() = when (this) { + Default.toString() -> "maven" + else -> replace("([a-z])([A-Z]+)".toRegex(), "\$1_\$2") + .toLowerCase() + "_maven" +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/ExcludeRule.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/ExcludeRule.kt new file mode 100644 index 00000000..745c1e1e --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/ExcludeRule.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2022 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.migrate.dependencies.model + +internal data class ExcludeRule( + val group: String, + val artifact: String +) \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/MavenExternalArtifact.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/MavenExternalArtifact.kt new file mode 100644 index 00000000..86fa2687 --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/MavenExternalArtifact.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2022 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.migrate.dependencies.model + +import com.grab.grazel.bazel.starlark.BazelDependency.MavenDependency +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionComparator +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.Version +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser +import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult + +internal data class MavenExternalArtifact( + val group: String, + val name: String, + val version: String, + val repository: Repository, + val excludeRules: List, + val id: String = "$group:$name:$version", + val overrideTarget: OverrideTarget? = null +) : Versioned, Comparable { + // Declare as property to not pollute generated hashcode() equals() by data class + lateinit var componentResult: DefaultResolvedComponentResult + + private val parsedVersion = VersionParser().transform(version) + override fun getVersion(): Version = parsedVersion + + val shortId = "$group:$name" + + private val comparator = DefaultVersionComparator() + override fun compareTo(other: MavenExternalArtifact) = comparator.compare(this, other) + + override fun toString() = id +} + +internal data class OverrideTarget( + val artifactShortId: String, + val label: MavenDependency, +) + +internal fun MavenExternalArtifact.mergeWith(others: List): MavenExternalArtifact { + val current = this + val excludeRules = (others.flatMap { it.excludeRules } + current.excludeRules).distinct() + val overrideTarget = current.overrideTarget ?: others.map { it.overrideTarget } + .firstOrNull { it != null } + return current.copy( + overrideTarget = overrideTarget, + excludeRules = excludeRules + ).apply { + componentResult = current.componentResult + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/Repository.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/Repository.kt new file mode 100644 index 00000000..8ac4aaae --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/Repository.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2022 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.migrate.dependencies.model + +import com.google.common.base.Objects +import org.gradle.api.internal.artifacts.repositories.DefaultMavenArtifactRepository + +internal class Repository( + val name: String, + val repository: DefaultMavenArtifactRepository +) { + override fun equals(other: Any?) = when (other) { + is Repository -> Objects.equal(name, repository.name) + else -> false + } + + override fun hashCode() = Objects.hashCode(name) +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/util/Iterable.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/util/Iterable.kt new file mode 100644 index 00000000..177f3f8d --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/util/Iterable.kt @@ -0,0 +1,8 @@ +package com.grab.grazel.util + +fun > Iterable.addTo( + destination: C, +): C { + for (item in this) destination.add(item) + return destination +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/fake/FakeProductFlavor.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/fake/FakeProductFlavor.kt index caa96bf9..f0ee8e72 100644 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/fake/FakeProductFlavor.kt +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/fake/FakeProductFlavor.kt @@ -22,8 +22,8 @@ import com.android.builder.model.ClassField import com.android.builder.model.ProductFlavor import com.android.builder.model.VectorDrawablesOptions import com.grab.grazel.extension.VariantFilter -import com.grab.grazel.gradle.AndroidVariantDataSource import com.grab.grazel.gradle.ConfigurationScope +import com.grab.grazel.gradle.variant.AndroidVariantDataSource import org.gradle.api.Action import org.gradle.api.Project import java.io.File diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultAndroidVariantDataSourceTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultAndroidVariantDataSourceTest.kt index 82e5297c..ba11557a 100644 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultAndroidVariantDataSourceTest.kt +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultAndroidVariantDataSourceTest.kt @@ -30,6 +30,8 @@ import com.grab.grazel.fake.FakeProductFlavor import com.grab.grazel.fake.FakeVariant import com.grab.grazel.fake.RELEASE_FLAVOR1 import com.grab.grazel.fake.RELEASE_FLAVOR2 +import com.grab.grazel.gradle.variant.AndroidVariantsExtractor +import com.grab.grazel.gradle.variant.DefaultAndroidVariantDataSource import org.gradle.api.Project import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -100,6 +102,15 @@ class FakeAndroidVariantsExtractor : AndroidVariantsExtractor { return getVariants(project).map { it.buildType }.toSet() } + override fun allVariants(project: Project): Set { + return setOf( + FakeVariant(DEBUG_FLAVOR1, FLAVOR1), + FakeVariant(DEBUG_FLAVOR2, FLAVOR2), + FakeVariant(RELEASE_FLAVOR1, FLAVOR1), + FakeVariant(RELEASE_FLAVOR2, FLAVOR2) + ) + } + override fun getUnitTestVariants(project: Project): Set = emptySet() override fun getTestVariants(project: Project): Set = emptySet() diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultDependenciesDataSourceTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultDependenciesDataSourceTest.kt index 50bf55f1..811c5612 100644 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultDependenciesDataSourceTest.kt +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultDependenciesDataSourceTest.kt @@ -26,6 +26,8 @@ import com.grab.grazel.fake.FLAVOR1 import com.grab.grazel.fake.FLAVOR2 import com.grab.grazel.fake.FakeAndroidVariantDataSource import com.grab.grazel.gradle.dependencies.* +import com.grab.grazel.gradle.variant.AndroidVariantsExtractor +import com.grab.grazel.gradle.variant.DefaultAndroidVariantsExtractor import org.gradle.api.Project import org.gradle.kotlin.dsl.add import org.gradle.kotlin.dsl.configure @@ -207,7 +209,7 @@ class DefaultDependenciesDataSourceTest : GrazelPluginTest() { assertTrue( "First level module dependencies does not contain embedded artifacts", dependenciesDataSource.firstLevelModuleDependencies(subProject) - .none { DEP_GROUP_EMBEDDED_BY_RULES.contains(it.moduleGroup) }) + .none { IGNORED_ARTIFACT_GROUPS.contains(it.moduleGroup) }) } @Test diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/DefaultVariantBuilderTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/DefaultVariantBuilderTest.kt new file mode 100644 index 00000000..5b3a0382 --- /dev/null +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/DefaultVariantBuilderTest.kt @@ -0,0 +1,172 @@ +package com.grab.grazel.gradle.variant + +import com.grab.grazel.buildProject +import com.grab.grazel.di.GrazelComponent +import com.grab.grazel.util.addGrazelExtension +import com.grab.grazel.util.createGrazelComponent +import com.grab.grazel.util.truth +import org.gradle.api.Project +import org.junit.Before +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class DefaultVariantBuilderTest { + private lateinit var rootProject: Project + private lateinit var androidProject: Project + private lateinit var jvmProject: Project + + private lateinit var grazelComponent: GrazelComponent + private lateinit var variantBuilder: VariantBuilder + + @Before + fun setup() { + rootProject = buildProject("root").also { + it.addGrazelExtension() + } + androidProject = buildProject("android", rootProject).also { + setupAndroidVariantProject(it) + } + jvmProject = buildProject("java", rootProject).also { + setupJvmVariantProject(it) + } + + grazelComponent = rootProject.createGrazelComponent() + variantBuilder = grazelComponent.variantBuilder().get() + } + + private fun assert( + variants: List>, + size: Int, + message: String, + vararg items: String + ) { + assertEquals( + size, + variants.size, + message + ) + variants.map { it.name } + .truth() + .containsExactlyElementsIn(items.toList()) + } + + @Test + fun `assert android variants are built for android project`() { + val variants = variantBuilder.build(androidProject) + val androidVariants = variants.filterIsInstance() + assertEquals(10, androidVariants.size, "Android variant are built") + + assert( + variants = androidVariants.filter { it.variantType == VariantType.AndroidBuild }, + size = 4, + message = "Android build variants are built", + "paidDebug", + "freeDebug", + "paidRelease", + "freeRelease" + ) + + assert( + variants = androidVariants.filter { it.variantType == VariantType.AndroidTest }, + size = 2, + message = "Android test variants are built", + "paidDebugAndroidTest", "freeDebugAndroidTest" + ) + + assert( + variants = androidVariants.filter { it.variantType == VariantType.Test }, + size = 4, + message = "Test variants are built", + "paidDebugUnitTest", "freeDebugUnitTest", "paidReleaseUnitTest", "freeReleaseUnitTest" + ) + + assertEquals( + 0, + androidVariants.filter { it.variantType == VariantType.JvmBuild }.size, + "Pure Java Variants are not built for Android projects" + ) + } + + @Test + fun `assert android build type variants are built`() { + val variants = variantBuilder.build(androidProject) + val androidVariants = variants.filterIsInstance() + assert( + variants = androidVariants.filter { it.variantType == VariantType.AndroidBuild }, + size = 2, + message = "BuiltType build variants are built", + "debug", "release" + ) + assert( + variants = androidVariants.filter { it.variantType == VariantType.Test }, + size = 2, + message = "BuiltType build variants are built", + "debugTest", "releaseTest" + ) + + assertEquals( + 1, + androidVariants.filter { it.variantType == VariantType.AndroidTest }.size, + "Only debug Android Test types are built" + ) + + assert( + variants = androidVariants.filter { it.variantType == VariantType.AndroidTest }, + size = 1, + message = "Only debug Android Test types are built", + "debugAndroidTest" + ) + } + + @Test + fun `assert android flavor variants are built`() { + val variants = variantBuilder.build(androidProject) + val flavorVariants = variants.filterIsInstance() + assert( + variants = flavorVariants.filter { it.variantType == VariantType.AndroidBuild }, + size = 2, + message = "Flavor build variants are built", + "paid", "free" + ) + assert( + variants = flavorVariants.filter { it.variantType == VariantType.Test }, + size = 2, + message = "Flavor test variants are built", + "paidTest", "freeTest" + ) + + assert( + variants = flavorVariants.filter { it.variantType == VariantType.AndroidTest }, + size = 2, + message = "Flavor Android test variants are built", + "paidAndroidTest", "freeAndroidTest" + ) + + assertEquals( + 0, + flavorVariants.filter { it.variantType == VariantType.JvmBuild }.size, + "Jvm variants are not built", + ) + } + + @Test + fun `assert jvm variants are built`() { + val variants = variantBuilder.build(jvmProject) + assertEquals(2, variants.size) + assertTrue("No Android variants are built for Jvm project") { + variants.none { it is AndroidFlavor || it is AndroidBuildType || it is AndroidVariant } + } + + assertEquals( + 1, + variants.filter { it.variantType == VariantType.JvmBuild }.size, + "Jvm variants are built", + ) + assertEquals( + 1, + variants.filter { it.variantType == VariantType.Test }.size, + "Jvm test variants are built", + ) + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/VariantTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/VariantTest.kt new file mode 100644 index 00000000..2e77bc9d --- /dev/null +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/VariantTest.kt @@ -0,0 +1,454 @@ +package com.grab.grazel.gradle.variant + +import com.android.build.gradle.AppExtension +import com.android.build.gradle.api.BaseVariant +import com.google.common.truth.Truth.assertThat +import com.grab.grazel.buildProject +import com.grab.grazel.gradle.variant.Classpath.Compile +import com.grab.grazel.gradle.variant.VariantType.* +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.kotlin.dsl.the +import org.junit.Before +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class VariantTest { + + private lateinit var rootProject: Project + private lateinit var androidProject: Project + private lateinit var jvmProject: Project + + @Before + fun setup() { + rootProject = buildProject("root") + androidProject = buildProject("android", rootProject) + jvmProject = buildProject("java", rootProject) + } + + private val appExtension get() = androidProject.the() + + private fun allVariants() = appExtension.let { ext -> + ext.applicationVariants + ext.testVariants + ext.unitTestVariants + }.toSet() + + private fun androidVariant(baseVariant: BaseVariant) = AndroidVariant( + androidProject, + baseVariant + ) + + @Test + fun `assert android variant specific configurations are parsed`() { + setupAndroidVariantProject(androidProject) + + androidVariant(appExtension.applicationVariants.first()).let { buildVariant -> + assertEquals( + 25, + buildVariant.variantConfigurations.size, + "Variant configuration parsed for build variant" + ) + assertTrue("Variant configurations does not contain tests for build variant") { + buildVariant.variantConfigurations.all { + !it.name.contains("test") || !it.name.contains("androidTest") + } + } + } + + assertEquals( + 28, + androidVariant(appExtension.testVariants.first()).variantConfigurations.size, + "Variant configuration parsed for androidTest variant" + ) + + assertEquals( + 28, + androidVariant(appExtension.unitTestVariants.first()).variantConfigurations.size, + "Variant configuration parsed for unitTest variant" + ) + + val parsedConfigurations = allVariants() + .flatMap { androidVariant(it).variantConfigurations } + .map { it.name } + .toSet() + + val allConfigurations = androidProject.configurations.map { it.name } + + assertEquals( + 254, + (allConfigurations - parsedConfigurations).size, + "Remaining unparsed configurations size at 254" + ) + } + + @Test + fun `assert android variant extends from are parsed`() { + setupAndroidVariantProject(androidProject) + + androidVariant(appExtension.applicationVariants.first()).let { buildVariant -> + assertThat(buildVariant.extendsFrom).containsExactly( + "default", + "debug", + "paid" + ) + } + androidVariant(appExtension.testVariants.first()).let { androidTestVariant -> + assertThat(androidTestVariant.extendsFrom).containsExactly( + "default", + "debug", + "paid", + "test", + "debugAndroidTest" + ) + } + androidVariant(appExtension.unitTestVariants.first()).let { unitTestVariant -> + assertThat(unitTestVariant.extendsFrom).containsExactly( + "default", + "debug", + "paid", + "test", + "debugUnitTest" + ) + } + } + + + @Test + fun `assert android annotation processor configurations are parsed for variants`() { + setupAndroidVariantProject(androidProject) + fun assertAnnotationProcessorConfiguration( + configurations: Set, + name: String + ) { + assertTrue("$name config is parsed when kapt plugin is applied") { + configurations.size == 1 && configurations.firstOrNull { it.name == name } != null + } + } + + val buildVariant = androidVariant(appExtension.applicationVariants.first()) + assertAnnotationProcessorConfiguration( + buildVariant.annotationProcessorConfiguration, + "kaptPaidDebug" + ) + val androidTest = androidVariant(appExtension.testVariants.first()) + assertAnnotationProcessorConfiguration( + androidTest.annotationProcessorConfiguration, + "kaptAndroidTestPaidDebug" + ) + val unitTest = androidVariant(appExtension.unitTestVariants.first()) + assertAnnotationProcessorConfiguration( + unitTest.annotationProcessorConfiguration, + "kaptTestPaidDebug" + ) + } + + @Test + fun `assert android Kotlin compiler plugin configuration is parsed for variants`() { + setupAndroidVariantProject(androidProject) + androidVariant(appExtension.applicationVariants.first()) + .kotlinCompilerPluginConfiguration.let { + assertTrue("Kotlin compiler plugin classpath parsed for build variant") { + it.any { it.name == "kotlinCompilerPluginClasspathPaidDebug" } + } + } + + androidVariant(appExtension.testVariants.first()) + .kotlinCompilerPluginConfiguration.let { + assertTrue("Kotlin compiler plugin classpath parsed for androidTest variant") { + it.any { it.name == "kotlinCompilerPluginClasspathPaidDebugAndroidTest" } + } + } + + androidVariant(appExtension.unitTestVariants.first()) + .kotlinCompilerPluginConfiguration.let { + assertTrue("Kotlin compiler plugin classpath parsed for unitTest variant") { + it.any { it.name == "kotlinCompilerPluginClasspathPaidDebugUnitTest" } + } + } + } + + + private fun jvmVariant(project: Project, variantType: VariantType) = JvmVariant( + jvmVariantData = JvmVariantData( + project = project, + variantType = variantType + ) + ) + + @Test + fun `assert variant configurations parsed for jvm project`() { + setupJvmVariantProject(jvmProject) + val allConfigurations = jvmProject.configurations + + fun assertConfigurationHierarchy( + configurationName: String, + parsedConfigurations: Set, + message: String, + ) { + assertTrue(message) { + allConfigurations + .first { it.name == configurationName } + .extendsFrom.all(parsedConfigurations::contains) + } + } + + jvmVariant( + jvmProject, + variantType = JvmBuild + ).variantConfigurations.let { configurations -> + assertEquals( + 32, + configurations.size, + "Build configurations are parsed correctly for build variant" + ) + assertConfigurationHierarchy( + "compileClasspath", + configurations, + "Compile classpath configurations are parsed for build variant" + ) + assertConfigurationHierarchy( + "runtimeClasspath", + configurations, + "Runtime classpath configurations are parsed for build variant" + ) + } + jvmVariant( + jvmProject, + variantType = Test + ).variantConfigurations.let { configurations -> + assertEquals( + 19, + configurations.size, + "Build configurations are parsed correctly for test variant" + ) + + assertConfigurationHierarchy( + "testCompileClasspath", + configurations, + "Compile classpath configurations are parsed for test variant" + ) + assertConfigurationHierarchy( + "testRuntimeClasspath", + configurations, + "Runtime classpath configurations are parsed for test variant" + ) + } + } + + @Test + fun `assert annotation processor configurations are parsed for jvm variant`() { + setupJvmVariantProject(jvmProject) + fun assertAnnotationProcessorConfiguration( + configurations: Set, + name: String + ) { + assertTrue("$name config is parsed when kapt plugin is applied") { + configurations.firstOrNull { it.name == name } != null + } + } + + val buildVariant = jvmVariant( + jvmProject, + variantType = JvmBuild + ) + assertAnnotationProcessorConfiguration( + buildVariant.annotationProcessorConfiguration, + "kapt" + ) + val testVariant = jvmVariant( + jvmProject, + variantType = Test + ) + assertAnnotationProcessorConfiguration( + testVariant.annotationProcessorConfiguration, + "kaptTest" + ) + } + + @Test + fun `assert kotlin compiler classpath configuration parsed for jvm variant`() { + setupJvmVariantProject(jvmProject) + + jvmVariant( + jvmProject, + variantType = JvmBuild + ).kotlinCompilerPluginConfiguration.let { + assertTrue("Kotlin compiler plugin classpath parsed for build variant") { + it.any { it.name == "kotlinCompilerPluginClasspathMain" } + } + } + + jvmVariant( + jvmProject, + variantType = Test + ).kotlinCompilerPluginConfiguration.let { + assertTrue("Kotlin compiler plugin classpath parsed for test variant") { + it.any { it.name == "kotlinCompilerPluginClasspathTest" } + } + } + } + + private fun androidBuildType( + baseVariant: BaseVariant, + variantType: VariantType + ) = AndroidBuildType( + androidProject, + baseVariant.buildType, + variantType, + allVariants().map { it.flavorName }.toSet() + ) + + @Test + fun `assert android non extends from are parsed`() { + setupAndroidVariantProject(androidProject) + + androidBuildType( + appExtension.applicationVariants.first(), + AndroidBuild + ).let { buildVariant -> + assertThat(buildVariant.extendsFrom).containsExactly( + "default", + ) + } + androidBuildType( + appExtension.testVariants.first(), + AndroidTest + ).let { androidTestVariant -> + assertThat(androidTestVariant.extendsFrom).containsExactly( + "default", + "debug", + ) + } + androidBuildType( + appExtension.unitTestVariants.first(), + Test + ).let { unitTestVariant -> + assertThat(unitTestVariant.extendsFrom).containsExactly( + "default", + "debug", + "test", + ) + } + } + + @Test + fun `assert android build type specific variant are parsed with their configurations`() { + setupAndroidVariantProject(androidProject) + + androidBuildType(appExtension.applicationVariants.first(), AndroidBuild).let { buildType -> + val configurations = buildType.variantConfigurations + assertTrue("Variant specific configurations are parsed for build type build") { + configurations.size == 12 && configurations.all { it.name.contains("debug", true) } + } + } + + androidBuildType(appExtension.testVariants.first(), AndroidTest).let { buildType -> + val configurations = buildType.variantConfigurations + assertTrue("Variant specific configurations are parsed for build type") { + configurations.size == 12 && configurations.all { it.name.contains("debug", true) } + } + } + + androidBuildType(appExtension.unitTestVariants.first(), Test).let { buildType -> + val configurations = buildType.variantConfigurations + assertTrue("Variant specific configurations are parsed for build type") { + configurations.size == 24 && configurations.all { it.name.contains("debug", true) } + } + } + } + + @Test + fun `assert android build type's compileClasspath - runtimeClasspath is parsed`() { + setupAndroidVariantProject(androidProject) + + fun assert( + baseVariant: BaseVariant, + variantType: VariantType, + classpath: Classpath = Compile + ) { + androidBuildType( + baseVariant, + variantType + ).let { buildType -> + val configurations = when (classpath) { + Compile -> buildType.compileConfiguration + else -> buildType.runtimeConfiguration + } + val assertionMessage = "${classpath.name} configuration for " + + "${buildType.name} merges all flavor sub configurations - ${variantType.name}" + assertEquals(2, configurations.size, assertionMessage) + assertTrue(assertionMessage) { + configurations.all { it.name.contains("debug", true) } + } + } + } + + Classpath.values().forEach { classpath -> + assert(appExtension.applicationVariants.first(), AndroidBuild, classpath) + assert(appExtension.testVariants.first(), AndroidTest, classpath) + assert(appExtension.unitTestVariants.first(), Test, classpath) + } + } + + @Test + fun `assert android build type's annotationProcessor is parsed`() { + setupAndroidVariantProject(androidProject) + + fun assert( + baseVariant: BaseVariant, + variantType: VariantType, + ) { + androidBuildType( + baseVariant, + variantType + ).let { buildType -> + val configurations = buildType.annotationProcessorConfiguration + val assertionMessage = "Annotation processor for ${buildType.name} merges from " + + "flavor sub configurations - ${variantType.name}" + assertEquals(1, configurations.size, assertionMessage) + assertTrue(assertionMessage) { + configurations.all { + it.name.startsWith("kapt") && it.name.endsWith("Debug") + } + } + } + } + + assert(appExtension.applicationVariants.first(), AndroidBuild) + assert(appExtension.testVariants.first(), AndroidTest) + assert(appExtension.unitTestVariants.first(), Test) + } + + @Test + fun `assert android build type's compiler plugin configuration is parsed`() { + setupAndroidVariantProject(androidProject) + + fun assert( + baseVariant: BaseVariant, + variantType: VariantType, + ) { + androidBuildType( + baseVariant, + variantType + ).let { buildType -> + val configurations = buildType.kotlinCompilerPluginConfiguration + val assertion = "Kotlin compiler plugin classpath for ${buildType.name}" + + " parsed for ${variantType.name}" + assertEquals(0, configurations.size, assertion) + } + } + + assert( + appExtension.applicationVariants.first(), + AndroidBuild + ) + assert( + appExtension.testVariants.first(), + AndroidTest + ) + assert( + appExtension.unitTestVariants.first(), + Test + ) + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/VariantTestProject.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/VariantTestProject.kt new file mode 100644 index 00000000..0827a9f9 --- /dev/null +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/VariantTestProject.kt @@ -0,0 +1,93 @@ +package com.grab.grazel.gradle.variant + +import com.android.build.gradle.AppExtension +import com.grab.grazel.gradle.ANDROID_APPLICATION_PLUGIN +import com.grab.grazel.gradle.JAVA_LIBRARY_PLUGIN +import com.grab.grazel.gradle.KOTLIN_ANDROID_PLUGIN +import com.grab.grazel.gradle.KOTLIN_KAPT +import com.grab.grazel.gradle.KOTLIN_PLUGIN +import com.grab.grazel.util.doEvaluate +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.repositories + +const val TEST_FLAVOR_DIMENSION = "service" +const val TEST_FLAVOR_FREE = "paid" +const val TEST_FLAVOR_PAID = "free" +const val TEST_DEBUG = "debug" +const val TEST_RELEASE = "release" + +fun setupAndroidVariantProject(androidProject: Project) { + with(androidProject) { + with(plugins) { + apply(ANDROID_APPLICATION_PLUGIN) + apply(KOTLIN_ANDROID_PLUGIN) + apply(KOTLIN_KAPT) + } + repositories { + google() + mavenCentral() + } + configure { + defaultConfig { + compileSdkVersion(32) + } + flavorDimensions(TEST_FLAVOR_DIMENSION) + productFlavors { + create("paid") { + dimension = TEST_FLAVOR_DIMENSION + } + create("free") { + dimension = TEST_FLAVOR_DIMENSION + } + } + dataBinding.isEnabled = true + } + dependencies { + add( + "implementation", + "androidx.appcompat:appcompat:1.5.1" + ) + add( + "freeImplementation", + "androidx.constraintlayout:constraintlayout:2.1.3" + ) + add( + "paidImplementation", + "androidx.constraintlayout:constraintlayout:2.1.2" + ) + add( + "kapt", + "com.google.dagger:dagger:2.37" + ) + } + } + + androidProject.doEvaluate() +} + +fun setupJvmVariantProject(project: Project) { + with(project) { + with(plugins) { + apply(JAVA_LIBRARY_PLUGIN) + apply(KOTLIN_PLUGIN) + apply(KOTLIN_KAPT) + } + repositories { + google() + mavenCentral() + } + dependencies { + add( + "implementation", + "com.google.dagger:dagger:2.37" + ) + add( + "kapt", + "com.google.dagger:dagger:2.37" + ) + } + } + project.doEvaluate() +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/VariantTypeTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/VariantTypeTest.kt new file mode 100644 index 00000000..29a11f81 --- /dev/null +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/variant/VariantTypeTest.kt @@ -0,0 +1,62 @@ +package com.grab.grazel.gradle.variant + +import com.android.build.gradle.AppExtension +import com.grab.grazel.buildProject +import com.grab.grazel.gradle.variant.VariantType.* +import org.gradle.api.Project +import org.gradle.kotlin.dsl.the +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class VariantTypeTest { + + private lateinit var rootProject: Project + private lateinit var androidProject: Project + + @Before + fun setup() { + rootProject = buildProject("root") + androidProject = buildProject("android", rootProject) + setupAndroidVariantProject(androidProject) + } + + @Test + fun `assert android build variant type is parsed to AndroidBuild`() { + androidProject.the() + .applicationVariants + .forEach { variant -> + val variantType = variant.toVariantType() + assertTrue( + "Build variant is parsed for ${variant.name}", + variantType == AndroidBuild + ) + } + } + + @Test + fun `assert android test variant type is parsed to AndroidTest`() { + androidProject.the() + .testVariants + .forEach { variant -> + val variantType = variant.toVariantType() + assertTrue( + "Android Test variant is parsed for ${variant.name}", + variantType == AndroidTest + ) + } + } + + @Test + fun `assert unit test variant type is parsed to Test`() { + androidProject.the() + .unitTestVariants + .forEach { variant -> + val variantType = variant.toVariantType() + assertTrue( + "Android Test variant is parsed for ${variant.name}", + variantType == Test + ) + } + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/BuildConfigFieldsTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/BuildConfigFieldsTest.kt index b39ec325..a6c37968 100644 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/BuildConfigFieldsTest.kt +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/BuildConfigFieldsTest.kt @@ -25,9 +25,9 @@ import com.grab.grazel.GrazelPluginTest import com.grab.grazel.buildProject import com.grab.grazel.fake.FakeVariant import com.grab.grazel.gradle.ANDROID_APPLICATION_PLUGIN -import com.grab.grazel.gradle.AndroidVariantDataSource -import com.grab.grazel.gradle.DefaultAndroidVariantDataSource -import com.grab.grazel.gradle.DefaultAndroidVariantsExtractor +import com.grab.grazel.gradle.variant.AndroidVariantDataSource +import com.grab.grazel.gradle.variant.DefaultAndroidVariantDataSource +import com.grab.grazel.gradle.variant.DefaultAndroidVariantsExtractor import com.grab.grazel.migrate.android.extractBuildConfig import com.grab.grazel.util.doEvaluate import org.gradle.api.Project @@ -47,7 +47,9 @@ class BuildConfigFieldsTest : GrazelPluginTest() { val grazelGradlePluginExtension = GrazelExtension(rootProject) rootProject.extensions.add(GRAZEL_EXTENSION, grazelGradlePluginExtension) - androidVariantDataSource = DefaultAndroidVariantDataSource(DefaultAndroidVariantsExtractor()) + androidVariantDataSource = DefaultAndroidVariantDataSource( + DefaultAndroidVariantsExtractor() + ) androidBinary = buildProject("android-binary", rootProject) androidBinary.run { diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultAndroidUnitTestDataExtractorTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultAndroidUnitTestDataExtractorTest.kt index c33d7a22..3bfb0d8d 100644 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultAndroidUnitTestDataExtractorTest.kt +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultAndroidUnitTestDataExtractorTest.kt @@ -23,8 +23,6 @@ import com.grab.grazel.GrazelPluginTest import com.grab.grazel.buildProject import com.grab.grazel.fake.FakeDependencyGraphs import com.grab.grazel.gradle.ANDROID_LIBRARY_PLUGIN -import com.grab.grazel.gradle.DefaultAndroidVariantDataSource -import com.grab.grazel.gradle.DefaultAndroidVariantsExtractor import com.grab.grazel.gradle.DefaultConfigurationDataSource import com.grab.grazel.gradle.DefaultRepositoryDataSource import com.grab.grazel.gradle.FakeAndroidVariantsExtractor @@ -33,6 +31,8 @@ import com.grab.grazel.gradle.dependencies.ArtifactsConfig import com.grab.grazel.gradle.dependencies.DefaultDependenciesDataSource import com.grab.grazel.gradle.dependencies.DefaultDependencyResolutionService import com.grab.grazel.gradle.dependencies.GradleDependencyToBazelDependency +import com.grab.grazel.gradle.variant.DefaultAndroidVariantDataSource +import com.grab.grazel.gradle.variant.DefaultAndroidVariantsExtractor import com.grab.grazel.util.doEvaluate import org.gradle.api.Project import org.gradle.kotlin.dsl.configure diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultManifestValuesBuilderTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultManifestValuesBuilderTest.kt index 757ccc14..99f42d91 100644 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultManifestValuesBuilderTest.kt +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultManifestValuesBuilderTest.kt @@ -30,9 +30,9 @@ import com.grab.grazel.fake.FakeDependencyGraphs import com.grab.grazel.fake.FakeVariant import com.grab.grazel.gradle.ANDROID_APPLICATION_PLUGIN import com.grab.grazel.gradle.ANDROID_LIBRARY_PLUGIN -import com.grab.grazel.gradle.AndroidVariantDataSource -import com.grab.grazel.gradle.DefaultAndroidVariantDataSource -import com.grab.grazel.gradle.DefaultAndroidVariantsExtractor +import com.grab.grazel.gradle.variant.AndroidVariantDataSource +import com.grab.grazel.gradle.variant.DefaultAndroidVariantDataSource +import com.grab.grazel.gradle.variant.DefaultAndroidVariantsExtractor import com.grab.grazel.util.doEvaluate import org.gradle.api.Project import org.gradle.api.artifacts.* diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/VariantsMergerRobo.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/VariantsMergerRobo.kt index 350157ba..611ce682 100644 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/VariantsMergerRobo.kt +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/VariantsMergerRobo.kt @@ -24,9 +24,9 @@ import com.android.build.gradle.internal.dsl.ProductFlavor import com.grab.grazel.buildProject import com.grab.grazel.gradle.ANDROID_APPLICATION_PLUGIN import com.grab.grazel.gradle.ANDROID_LIBRARY_PLUGIN -import com.grab.grazel.gradle.AndroidVariantDataSource import com.grab.grazel.gradle.ConfigurationScope import com.grab.grazel.gradle.KOTLIN_ANDROID_PLUGIN +import com.grab.grazel.gradle.variant.AndroidVariantDataSource import com.grab.grazel.util.doEvaluate import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.whenever diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/util/Truth.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/util/Truth.kt new file mode 100644 index 00000000..d5858774 --- /dev/null +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/util/Truth.kt @@ -0,0 +1,6 @@ +package com.grab.grazel.util + +import com.google.common.truth.IterableSubject +import com.google.common.truth.Truth + +fun Collection<*>.truth(): IterableSubject = Truth.assertThat(this) \ No newline at end of file