Is it possible to find unused libs in a `libs.vers...
# gradle
z
Is it possible to find unused libs in a
libs.versions.toml
somehow?
v
From Gradle perspective, not that I know of, and probably impossible to do reliably. You could for example use the type-unsafe string-y API to access it. But IntelliJ for example can tell you that entries are unused from the perspective of the type-safe accessors. The other usages are then like when using reflection to access something in Java where IntelliJ could also say it is unused while it is used through reflection, same here.
πŸ₯² 1
e
Intellij has some false positives with their unused checks so don't rely fully on that πŸ™ƒ
πŸ₯² 3
πŸ‘†πŸΌ 1
v
But report it if they do πŸ™‚
e
I reported through Android Studio and I believe they have plans to address it upstream
πŸ‘Œ 1
πŸ‘ŒπŸ½ 1
r
I wonder if the Dependency Analysis Gradle Plugin could be extended to do this... it will already tell you about unused dependencies in each sub-project, so in principle it could form an idea of the total set of dependencies in the entire project and compare that against the TOML?
v
I'm not sure whether it could know if a dependency was declared using the version catalog or not. And it might also be out-of-scope. But feel free to open a feature request and see what the developer thinks about it. :-) But it must in any way he configurable, because one version catalog could be used by multiple builds.
m
Out of curiosity, how bad is it to have unused entries?
n
This probably isn't quite what you're looking for but in my current role we had a initiative to migrate our main project off some bespoke in house dependency management system to Version Catalogs and as part of that we wrote a script to migrate all the repositories and part of the script would remove entries in the Version Catalog if it didn't find them in any of the build files. I could share the relevant snippets if you're interested, it's all in Kotlin
z
@Nicholas Doglio If its not too much of a hassle, please do, Id love to check how that might be done πŸ˜ƒ @mbonnin Its probably not bad at all really. My issue is that Im updating the unused dependency versions without knowing about it, which doesnt seem very productive!
v
You could use refreshVersions plugin. It will only write updates for used dependencies to it
πŸ™πŸ½ 1
n
Sorry it's been a crazy week and I haven't had to look at the code for that project since February so diving back in was a little difficult πŸ˜… I can't pull out everything since it's messy bit the gist is we use ktoml to parse the existing Version Catalog and then parse all the existing build.gradle files looking for any Version Catalog usages, we then compare our findings to the initial Version Catalog remove anything saved in the catalog that isn't used in the build files. This is the code for representing a Version Catalog in ktoml
Copy code
@Serializable
data class VersionCatalog(
    val versions: Map<String, String>,
    val plugins: Map<String, Plugin>,
    val libraries: Map<String, Library>,
)

@Serializable(with = LibrarySerializer::class)
@TomlInlineTable
sealed interface Library {
    val module: String
}

@Serializable
@TomlInlineTable
data class InlineVersionLibrary(
    override val module: String,
    val version: String,
) : Library

@Serializable
@TomlInlineTable
data class RefVersionLibrary(
    override val module: String,
    val version: Version,
) : Library

@Serializable @TomlInlineTable data class Version(val ref: String)

@Serializable(with = PluginSerializer::class)
@TomlInlineTable
sealed interface Plugin {
    val id: String
}

@Serializable
@TomlInlineTable
data class RefVersionPlugin(override val id: String, val version: Version) : Plugin

@Serializable
@TomlInlineTable
data class InlineVersionPlugin(override val id: String, val version: String) : Plugin

/**
 * A custom [KSerializer] so we can support the proper format for both inline and referenced
 * versions for libraries.
 */
private object LibrarySerializer : KSerializer<Library> {
    // Use the ref version as a default
    @OptIn(ExperimentalSerializationApi::class)
    override val descriptor = SerialDescriptor("Library", serialDescriptor<RefVersionLibrary>())

    override fun serialize(encoder: Encoder, value: Library) {
        when (value) {
            is InlineVersionLibrary -> InlineVersionLibrary.serializer().serialize(encoder, value)
            is RefVersionLibrary -> RefVersionLibrary.serializer().serialize(encoder, value)
        }
    }

    override fun deserialize(decoder: Decoder): Library {
        throw UnsupportedOperationException()
    }
}

/**
 * A custom [KSerializer] so we can support the proper format for both inline and referenced
 * versions for plugins.
 */
private object PluginSerializer : KSerializer<Plugin> {
    // Use the ref version as a default
    @OptIn(ExperimentalSerializationApi::class)
    override val descriptor = SerialDescriptor("Plugin", serialDescriptor<RefVersionLibrary>())

    override fun serialize(encoder: Encoder, value: Plugin) {
        when (value) {
            is InlineVersionPlugin -> InlineVersionPlugin.serializer().serialize(encoder, value)
            is RefVersionPlugin -> RefVersionPlugin.serializer().serialize(encoder, value)
        }
    }

    override fun deserialize(decoder: Decoder): Plugin {
        throw UnsupportedOperationException()
    }
}
315 Views