Skip to content

Commit

Permalink
Merge pull request #225 from LouisCAD/androidx-artifacts-to-constants…
Browse files Browse the repository at this point in the history
…-mapping

Mapping of AndroidX artifacts to constants (from Splitties Gradle plugin)
  • Loading branch information
Jean-Michel Fayard authored Nov 11, 2019
2 parents eae2e08 + 74699b7 commit c9e6f3d
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 87 deletions.
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 needs 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"

internal var ALREADY_RUN: Boolean = false
}
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

0 comments on commit c9e6f3d

Please sign in to comment.