gradle / gradle

Adaptable, fast automation for all
https://gradle.org
Apache License 2.0
16.89k stars 4.73k forks source link

Can't cast `VersionCatalogsExtension>().named("libs")` to `org.gradle.accessors.dm.LibrariesForLibs` anymore #19813

Open radzio opened 2 years ago

radzio commented 2 years ago

In Gradle 7.0.2 I have something like this which works:

buildscript {

    repositories {
        google()
        mavenCentral()
        maven(url = "https://plugins.gradle.org/m2/")
    }
 dependencies {
        val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs") as 
 org.gradle.accessors.dm.LibrariesForLibs

        classpath(libs.androidGradle)
        classpath(libs.kotlin.gradle)
}

When I updated to Gradle 7.3.3 I got this error:

class org.gradle.api.internal.catalog.VersionCatalogView cannot be cast to class org.gradle.accessors.dm.LibrariesForLibs (org.gradle.api.internal.catalog.VersionCatalogView is in unnamed module of loader org.gradle.initialization.MixInLegacyTypesClassLoader @9cb8225; org.gradle.accessors.dm.LibrariesForLibs is in unnamed module of loader org.gradle.internal.classloader.VisitableURLClassLoader @4de33f48)

Expected Behavior

Casting works or other solutions provided.

Current Behavior

Casting does not work.

Context

I can't migrate to [plugins] in toml file yet since not all plugins are available in gradle plugin central and I don't want to implement plugin resolution strategy.

Steps to Reproduce

Your Environment

Build scan URL:

asodja commented 2 years ago

We changed this since if library of type LibrariesForLibs was also an VersionCatalogsExtension (or vice versa) we had to do bunch of checks that users' aliases don't conflict with VersionCatalogsExtension API. With that change we were also able to remove some of restrictions for aliases that were present before (e.g. before aliaswas not allowed to use plugin or version suffix, but now that is possible).

So now API and accessors are separated. I believe that in 7.3.3 aliases are anyway accessible in buildscript block, so you can just do:

buildscript {
    repositories {
        google()
        mavenCentral()
        maven(url = "https://plugins.gradle.org/m2/")
    }
    dependencies {
        classpath(libs.androidGradle)
        classpath(libs.kotlin.gradle)
    }
}

Worst case you can workaround it with:

dependencies {
    val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
    // Note in Gradle 7.4 (currently 7.4-rc-2 is out) 
    // findDependency is deprecated and findLibrary should be used
    classpath(libs.findDependency("androidGradle").get())
    classpath(libs.findDependency("kotlin.gradle").get()) 
}
dvaske commented 2 years ago

I had the same problem using the LibrariesForLibs in buildSrc and couldn't access it any more after upgrading 7.2 -> 7.4 I changed val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs") as org.gradle.accessors.dm.LibrariesForLibs to val libs = project.extensions.getByName("libs") as org.gradle.accessors.dm.LibrariesForLibs And it works

melix commented 2 years ago

Note that if this works, this is not intentional. You should consider LibrariesForLibs as an internal implementation detail.

handstandsam commented 2 years ago

Thanks @asodja, I used your solution and expanded on it. Here is what I ended up with. I don't like the extra boilerplate, but I'm happy that my libs.versions.toml is the source of truth.

[versions]
android-build-tools = "30.0.3"
android-compilesdk = "31"
android-minsdk = "23"
android-targetsdk = "30"
kotlin = "1.6.10"
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.getByType

class VersionCatalogExt(project: Project) {
    private val libs: VersionCatalog = project
        .extensions
        .getByType<VersionCatalogsExtension>()
        .named("libs")

    private fun version(key: String): String = libs.findVersion(key).get().requiredVersion

    private fun versionInt(key: String): Int = version(key).toInt()

    val ANDROID_BUILD_TOOLS_VERSION = version("android.build.tools")
    val ANDROID_COMPILE_SDK_VERSION = versionInt("android.compilesdk")
    val ANDROID_MIN_SDK_VERSION = versionInt("android.minsdk")
    val ANDROID_TARGET_SDK_VERSION = versionInt("android.targetsdk")
    val KOTLIN_VERSION = versionInt("kotlin")
}
plugins {
    id("kotlin-android")
}

val libs = VersionCatalogExt(project)

android {
    buildToolsVersion = libs.ANDROID_BUILD_TOOLS_VERSION
    compileSdk = libs.ANDROID_COMPILE_SDK_VERSION

    compileOptions {
        targetCompatibility = JavaVersion.VERSION_1_8
        sourceCompatibility = JavaVersion.VERSION_1_8
    }

    defaultConfig {
        minSdk = libs.ANDROID_MIN_SDK_VERSION
        targetSdk = libs.ANDROID_TARGET_SDK_VERSION
    }
}
handstandsam commented 2 years ago

Found a slightly cleaner syntax:

import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.getByType

internal fun Project.version(key: String): String = extensions
    .getByType<VersionCatalogsExtension>()
    .named("libs")
    .findVersion(key)
    .get()
    .requiredVersion

internal fun Project.versionInt(key: String) = version(key).toInt()

internal val Project.COMPOSE_VERSION get() = version("compose")
internal val Project.ANDROID_BUILD_TOOLS_VERSION get() = version("android.build.tools")
internal val Project.ANDROID_COMPILE_SDK_VERSION get() = versionInt("android.compilesdk")
internal val Project.ANDROID_MIN_SDK_VERSION get() = versionInt("android.minsdk")
internal val Project.ANDROID_TARGET_SDK_VERSION get() = versionInt("android.targetsdk")
plugins {
    id("kotlin-android")
}

android {
    buildToolsVersion = ANDROID_BUILD_TOOLS_VERSION
    compileSdk = ANDROID_COMPILE_SDK_VERSION

    compileOptions {
        targetCompatibility = JavaVersion.VERSION_1_8
        sourceCompatibility = JavaVersion.VERSION_1_8
    }

    defaultConfig {
        minSdk = ANDROID_MIN_SDK_VERSION
        targetSdk = ANDROID_TARGET_SDK_VERSION
    }
}
mateot1 commented 2 years ago

For me the remaining problem (after using the libs extension directly in our app build.gradle file) was that we were creating a catalog manually from the toml file in buildSrc/settings.gradle to be able to use the same source of truth in buildSrc, but it was using the name "libs" which was clashing with the updated extension. Renaming that to something different fixed the issue.

dependencyResolutionManagement {
  versionCatalogs {
    create("buildSrcLibs") { // previously named "libs"
      from(files("../gradle/libs.versions.toml"))
    }
  }
}
CListery commented 2 years ago

For me the remaining problem (after using the libs extension directly in our app build.gradle file) was that we were creating a catalog manually from the toml file in buildSrc/settings.gradle to be able to use the same source of truth in buildSrc, but it was using the name "libs" which was clashing with the updated extension. Renaming that to something different fixed the issue.

dependencyResolutionManagement {
  versionCatalogs {
    create("buildSrcLibs") { // previously named "libs"
      from(files("../gradle/libs.versions.toml"))
    }
  }
}

mateot1's approach worked for me, especially when you have the same toml file and also a buildSrc

PS: In other words, it must be ensured that the names of multiple VersionCatalogBuilders are not repeated

HarryJhin commented 1 year ago

@handstandsam very very thx