Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapping of AndroidX artifacts to constants (from Splitties Gradle plugin) #225

Merged
merged 6 commits into from
Nov 11, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = "com.louiscad.splitties"
version = "0.1.2"
version = "0.1.3"

gradlePlugin {
plugins {
Expand Down Expand Up @@ -40,6 +40,8 @@ dependencies {

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
val experimentalAnnotations = listOf("kotlin.Experimental")
kotlinOptions.freeCompilerArgs = experimentalAnnotations.map { "-Xuse-experimental=$it"}
}

tasks.withType<Test>().configureEach {
Expand Down
32 changes: 4 additions & 28 deletions plugin/src/main/kotlin/com/louiscad/splitties/AndroidX.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,7 @@ package com.louiscad.splitties
**/

object AndroidX {
val GROUPS = arrayOf(
"android.arch.core", "android.arch.lifecycle", "android.arch.navigation", "android.arch.paging",
"android.arch.persistence", "android.arch.persistence.room", "android.arch.work",
"androidx.activity", "androidx.ads", "androidx.annotation", "androidx.appcompat",
"androidx.arch.core", "androidx.asynclayoutinflater", "androidx.autofill",
"androidx.benchmark", "androidx.biometric", "androidx.browser", "androidx.camera",
"androidx.car", "androidx.cardview", "androidx.collection", "androidx.compose",
"androidx.concurrent", "androidx.constraintlayout", "androidx.contentpager",
"androidx.coordinatorlayout", "androidx.core", "androidx.cursoradapter", "androidx.customview",
"androidx.databinding", "androidx.documentfile", "androidx.drawerlayout", "androidx.dynamicanimation",
"androidx.emoji", "androidx.enterprise", "androidx.exifinterface", "androidx.fragment",
"androidx.gridlayout", "androidx.heifwriter", "androidx.interpolator", "androidx.leanback",
"androidx.legacy", "androidx.lifecycle", "androidx.loader", "androidx.localbroadcastmanager",
"androidx.media", "androidx.media2", "androidx.mediarouter", "androidx.multidex",
"androidx.navigation", "androidx.paging", "androidx.palette", "androidx.percentlayout",
"androidx.preference", "androidx.print", "androidx.recommendation", "androidx.recyclerview",
"androidx.remotecallback", "androidx.room", "androidx.savedstate", "androidx.security",
"androidx.sharetarget", "androidx.slice", "androidx.slidingpanelayout", "androidx.sqlite",
"androidx.swiperefreshlayout", "androidx.test", "androidx.test.espresso", "androidx.test.espresso.idling",
"androidx.test.ext", "androidx.test.janktesthelper", "androidx.test.services",
"androidx.test.uiautomator", "androidx.textclassifier", "androidx.transition", "androidx.tvprovider",
"androidx.ui", "androidx.vectordrawable", "androidx.versionedparcelable", "androidx.viewpager",
"androidx.viewpager2", "androidx.wear", "androidx.webkit", "androidx.work"
)
const val placeholderVersion = "+"
private const val placeholderVersion = "+"
private object Versions {
const val core = placeholderVersion // "1.0.1"
const val multidex = placeholderVersion // "2.0.0"
Expand Down Expand Up @@ -113,7 +89,7 @@ object AndroidX {
const val viewPager = "androidx.viewpager:viewpager:$placeholderVersion" // 1.0.0"
const val wear = "androidx.wear:wear:$placeholderVersion" // 1.0.0"
const val webkit = "androidx.webkit:webkit:$placeholderVersion" // 1.0.0"


/**
* The actual dependency version comes from `gradle.properties`
Expand Down Expand Up @@ -145,7 +121,7 @@ object AndroidX {
val room = Room

object Room {
const val artifact = "androidx.room:room"
private const val artifact = "androidx.room:room"
private const val version = placeholderVersion // "2.0.0"
const val common = "$artifact-common:$version"
const val compiler = "$artifact-compiler:$version"
Expand Down Expand Up @@ -264,7 +240,7 @@ object AndroidX {

const val uiAutomator = "androidx.test.uiautomator:uiautomator:$placeholderVersion" // "2.2.0"
const val uiAutomatorV18 = "androidx.test.uiautomator:uiautomator-v18:2.2.0-alpha1"

/**
* The actual dependency version comes from `gradle.properties`
* from `version.androidx.test.espresso=xxx` or `version.androidx.test.espresso..$NAME=xxx`
Expand Down
6 changes: 3 additions & 3 deletions plugin/src/main/kotlin/com/louiscad/splitties/Kotlin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package com.louiscad.splitties
* from either `version.org.jetbrains.kotlin=xxx` or `version.$NAME=xxx` or `version.org.jetbrains.kotlin..$NAME=xxx`
**/
object Kotlin {
val kotlinVersion = "1.3.50"
val stdlibJdk7 = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
val testJunit = "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion"
private const val kotlinVersion = "1.3.50"
const val stdlibJdk7 = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
const val testJunit = "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion"
}
142 changes: 91 additions & 51 deletions plugin/src/main/kotlin/com/louiscad/splitties/MigrateAndroidxTask.kt
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
package com.louiscad.splitties

import com.louiscad.splitties.AndroidxMigrator.artifact
import com.louiscad.splitties.AndroidxMigrator.gradleSyntax
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.UnknownProjectException
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.tasks.TaskAction
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.KVisibility
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaField
import kotlin.reflect.typeOf


open class MigrateAndroidxTask: DefaultTask() {
init {
group = "help"
description = "Migrate package to AndroidX"
}
open class MigrateAndroidxTask : DefaultTask() {
init {
group = "help"
description = "Migrate package to AndroidX"
}

@TaskAction
fun migratePackages() = with(AndroidxMigrator) {
Expand All @@ -26,16 +27,18 @@ open class MigrateAndroidxTask: DefaultTask() {
println("$OK Parsing file androidx-artifact-mapping.csv")
val artifacts: List<ArtifactMapping> = readArtifactMappings()
val androidSupportDependencies = findAndroidSupportDependencies(project, artifacts)
printGradleSyntax(androidSupportDependencies.map { "${it.group}:${it.name}:${it.version}" })
println(gradleSyntax(androidSupportDependencies.map { "${it.group}:${it.name}:${it.version}" }, emptyMap()))

println("## Checking that you use compileSdkVersion 28")
val detected = tryDetectCompileSdkVersion(project.rootDir)
val version = detected.mapNotNull { detectVersion(it) }.firstOrNull()
println(when(version) {
null -> "⚠️ Make sure you are using compileSdkVersion 28. See $MIGRATE_TO_28"
28 -> "$OK You are using compileSdkVersion 28"
else -> throw GradleException("You should migrate first to compileSdkVersion 28. See $MIGRATE_TO_28")
})
println(
when (version) {
null -> "⚠️ Make sure you are using compileSdkVersion 28. See $MIGRATE_TO_28"
28 -> "$OK You are using compileSdkVersion 28"
else -> throw GradleException("You should migrate first to compileSdkVersion 28. See $MIGRATE_TO_28")
}
)

println()
println("## Migrating classes from support libraries to AndroidX.")
Expand All @@ -49,23 +52,26 @@ open class MigrateAndroidxTask: DefaultTask() {
println("$OK Found ${sourceFiles.size} source files that may need migration")

val supportLibsToAndroidXMappings = supportLibsToAndroidXMappings(androidxClassMappings)
val rawSupportLibsToAndroidXPackageMappings = rawSupportLibsToAndroidXPackageMappings(supportLibsToAndroidXMappings)
val supportLibsToAndroidXStarImportMappings = supportLibsToAndroidXStarImportMappings(rawSupportLibsToAndroidXPackageMappings)
val rawSupportLibsToAndroidXPackageMappings =
rawSupportLibsToAndroidXPackageMappings(supportLibsToAndroidXMappings)
val supportLibsToAndroidXStarImportMappings =
supportLibsToAndroidXStarImportMappings(rawSupportLibsToAndroidXPackageMappings)
println("$OK File androidx-class-mapping.csv parsed correctly")

val replaces: List<Pair<String, String>> = supportLibsToAndroidXMappings + supportLibsToAndroidXStarImportMappings
val replaces: List<Pair<String, String>> =
supportLibsToAndroidXMappings + supportLibsToAndroidXStarImportMappings

println("$OK Starting batch migration...")
val editedSourceFilesCount = sourceFiles.count { it.migrateToAndroidX(replaces) }
val editedGradleFilesCount = gradleFiles.count { it.migrateToAndroidX(replaces) }

println(
"\n$OK $editedSourceFilesCount source files (${sourceExtensions.joinToString(",") { it }}) " +
"have been migrated (${sourceFiles.count() - editedSourceFilesCount} didn't need it)."
"\n$OK $editedSourceFilesCount source files (${sourceExtensions.joinToString(",") { it }}) " +
"have been migrated (${sourceFiles.count() - editedSourceFilesCount} didn't need it)."
)
println(
"$OK $editedGradleFilesCount gradle files have been migrated " +
"(${gradleFiles.count() - editedGradleFilesCount} didn't need it)."
"$OK $editedGradleFilesCount gradle files have been migrated " +
"(${gradleFiles.count() - editedGradleFilesCount} didn't need it)."
)

println()
Expand All @@ -82,7 +88,9 @@ open class MigrateAndroidxTask: DefaultTask() {
println()
println("## Your turn: use instead those Androidx libraries")
val map = artifacts.associate { it.supportArtifact to it.androidXArtifact }
printGradleSyntax(androidSupportDependencies.mapNotNull { map[it.artifact] })
val splitties = getArtifactNameToSplittiesConstantMapping()

println(gradleSyntax(androidSupportDependencies.mapNotNull { map[it.artifact] }, splitties))
}
}

Expand All @@ -101,17 +109,23 @@ internal object AndroidxMigrator {
val Dependency.artifact: String
get() = "$group:$name"

fun gradleSyntax(artifact: String) : String {
val configuration = if (artifact.contains("test")) "androidTestImplementation" else "implementation"
return """$configuration("$artifact")"""
}

fun printGradleSyntax(artifacts: List<String>) {
println(artifacts.joinToString("\n", prefix = "\n", postfix = "\n") { gradleSyntax(it) })
fun gradleSyntax(artifacts: List<String>, splitties: Map<String, String>): String {
if (artifacts.isEmpty()) {
return "$OK No Android support dependency need to be migrated"
}
return artifacts.joinToString("\n", prefix = "\n", postfix = "\n") { artifact ->
val configuration = if (artifact.contains("test")) "androidTestImplementation" else "implementation"
if (artifact in splitties) {
"$configuration(${splitties[artifact]})"
} else {
"""$configuration("$artifact")"""
}
}
}

fun readArtifactMappings(): List<ArtifactMapping> {
val lines: List<String> = this::class.java.getResourceAsStream("/androidx-artifact-mapping.csv").reader().readLines()
val lines: List<String> =
this::class.java.getResourceAsStream("/androidx-artifact-mapping.csv").reader().readLines()
return lines
.drop(1)
.filter { it.contains(",") }
Expand Down Expand Up @@ -195,26 +209,31 @@ internal object AndroidxMigrator {

fun supportLibsToAndroidXMappings(androidXClassMapping: List<String>): List<Pair<String, String>> {
return androidXClassMapping.asSequence().drop(1)
.map { line ->
val (supportLibClassName, androidXClassName) = line.split(",").also { check(it.size == 2) }
check(supportLibClassName.isLegalClassName()) { "Illegal entry in csv: $supportLibClassName" }
check(androidXClassName.isLegalClassName()) { "Illegal entry in csv: $androidXClassName" }
supportLibClassName to androidXClassName
}.sortedByDescending { (supportLibClassName, _) -> supportLibClassName }.toList()
}
fun rawSupportLibsToAndroidXPackageMappings(supportLibsToAndroidXMappings: List<Pair<String, String>> ): List<Pair<String, String>> =
supportLibsToAndroidXMappings.asSequence().map { (supportLibClassName, androidXClassName) ->
supportLibClassName.packageNameFromClassName() to androidXClassName.packageNameFromClassName()
}.distinct().toList()

fun supportLibsToAndroidXStarImportMappings(rawSupportLibsToAndroidXPackageMappings: List<Pair<String, String>>): List<Pair<String, String>> =
rawSupportLibsToAndroidXPackageMappings.map { (supportLibPackageName, _/*androidXPackageName*/) ->
val supportLibStarImport = "import $supportLibPackageName.*"
val androidXStarImports = rawSupportLibsToAndroidXPackageMappings.filter { (slpn, _) ->
supportLibPackageName == slpn
}.joinToString("\n") { (_, axpn) -> "import $axpn.*" }
supportLibStarImport to androidXStarImports
}
.map { line ->
val (supportLibClassName, androidXClassName) = line.split(",").also { check(it.size == 2) }
check(supportLibClassName.isLegalClassName()) { "Illegal entry in csv: $supportLibClassName" }
check(androidXClassName.isLegalClassName()) { "Illegal entry in csv: $androidXClassName" }
supportLibClassName to androidXClassName
}.sortedByDescending { (supportLibClassName, _) -> supportLibClassName }.toList()
}

fun rawSupportLibsToAndroidXPackageMappings(
supportLibsToAndroidXMappings: List<Pair<String, String>>
): List<Pair<String, String>> =
supportLibsToAndroidXMappings.asSequence().map { (supportLibClassName, androidXClassName) ->
supportLibClassName.packageNameFromClassName() to androidXClassName.packageNameFromClassName()
}.distinct().toList()

fun supportLibsToAndroidXStarImportMappings(
rawSupportLibsToAndroidXPackageMappings: List<Pair<String, String>>
): List<Pair<String, String>> =
rawSupportLibsToAndroidXPackageMappings.map { (supportLibPackageName, _/*androidXPackageName*/) ->
val supportLibStarImport = "import $supportLibPackageName.*"
val androidXStarImports = rawSupportLibsToAndroidXPackageMappings.filter { (slpn, _) ->
supportLibPackageName == slpn
}.joinToString("\n") { (_, axpn) -> "import $axpn.*" }
supportLibStarImport to androidXStarImports
}


fun String.simpleNameFromFullyQualified(): String = substring(indexOfFirst { it.isUpperCase() })
Expand Down Expand Up @@ -247,4 +266,25 @@ internal object AndroidxMigrator {
}
}

fun getArtifactNameToSplittiesConstantMapping(): Map<String, String> {
return listOf(AndroidX, Google, Kotlin, KotlinX, Splitties, Square, Testing).flatMap { objectInstance ->
(objectInstance::class).getArtifactNameToSplittiesConstantMapping(objectInstance::class.simpleName!!)
}.toMap()
}

@UseExperimental(ExperimentalStdlibApi::class)
private fun KClass<*>.getArtifactNameToSplittiesConstantMapping(prefix: String): List<Pair<String, String>> {
return nestedClasses.filter { it.visibility == KVisibility.PUBLIC }.flatMap { kClass ->
val propertyName = kClass.simpleName!!.let { c -> "${c.first().toLowerCase()}${c.substring(1)}"}
kClass.getArtifactNameToSplittiesConstantMapping("$prefix.$propertyName")
} + this.memberProperties.filter {
it.isConst &&
it.visibility == KVisibility.PUBLIC &&
it.returnType == typeOf<String>()
}.map {
val artifactName = it.javaField!!.get(null).toString().substringBeforeLast(':')
val constantName = "$prefix.${it.name}"
artifactName to constantName
}
}
}
2 changes: 2 additions & 0 deletions plugin/src/main/kotlin/com/louiscad/splitties/PluginConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ object PluginConfig {
const val GROUP = "com.louiscad.splitties"
const val GRADLE_PROPERTY = "version.$GROUP"
const val SPLITTIES_VERSION = "3.0.0-alpha06"

var ALREADY_RUN: Boolean = false
LouisCAD marked this conversation as resolved.
Show resolved Hide resolved
}
11 changes: 7 additions & 4 deletions plugin/src/main/kotlin/com/louiscad/splitties/SplittiesPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ import org.gradle.api.Project

open class SplittiesPlugin : Plugin<Project> {

override fun apply(project: Project) = project.run {
tasks.register("migrateToAndroidX", MigrateAndroidxTask::class.java)
splittiesVersionComesFromGradleProperties()
override fun apply(project: Project) = project.rootProject.run {
if (PluginConfig.ALREADY_RUN.not()) {
PluginConfig.ALREADY_RUN = true
tasks.register("migrateToAndroidX", MigrateAndroidxTask::class.java)
splittiesVersionComesFromGradleProperties()
}
Unit
}

}

fun Project.splittiesVersionComesFromGradleProperties() = with(PluginConfig) {
val splittiesVersion = findProperty(GRADLE_PROPERTY) as? String ?: SPLITTIES_VERSION
allprojects {
rootProject.allprojects {
configurations.all {
if (name.contains("copy")) return@all
resolutionStrategy {
Expand Down