Files
LibreMediaConverter/gradle/libs.versions.toml
T
JMR-devandClaude Opus 5 fd6e5325cf Raise Kotlin to 2.4.10 so the bytecode can join the toolchain on Java 25
The previous commit settled for Java 24 everywhere because Kotlin 2.2.10 refuses
jvmTarget 25. That was the wrong constraint to accept, for two reasons.

The first is that 24 turned out to be unbuyable. Adoptium's repository carries
8, 11, 17, 21, 25 and 26 -- no 24, because it is a non-LTS that went end of life
in July 2025. The builds passed only because Gradle quietly auto-provisioned
24.0.2+12 through foojay, and .idea/misc.xml had been pointed at a temurin-24
that cannot be installed. A toolchain nobody can install is not pinned, it is
lucky.

The second is that the cap was never on the toolchain at all. Kotlin's ceiling
applies to jvmTarget -- the bytecode -- and the JDK running the build is a
separate axis. Conflating them is what steered this at 24 in the first place.

So the fix is the one the sibling repo already uses: put KGP on the root
buildscript classpath, where AGP's built-in Kotlin picks it up instead of the
2.2.10 it bundles. Kotlin 2.4.10 supports jvmTarget through 26, which lifts the
ceiling above the toolchain rather than under it. The Compose compiler plugin is
versioned in lockstep and reads the same catalog entry, so the two cannot drift,
and the module now applies both by id() because they come from the classpath
rather than from plugin resolution.

Checked rather than assumed, since a silent downgrade would look identical to
success: compiled classes report major version 69, which is Java 25. D8 dexes
them, R8 minifies them, and ktlint, detekt, lint, the unit tests and the
androidTest compile are all green on top.

25 is the right landing place independent of all this: it is LTS, it is in the
Adoptium repository, and temurin-25-jdk is already installed here -- so the
daemon runs on a real system JDK rather than a provisioned copy of an unpatched
one.

Two catalog plugin aliases went with it. android-application and kotlin-compose
now resolve from the buildscript classpath, so leaving aliases behind would have
left two entries that read like the source of truth and control nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 15:05:54 -05:00

129 lines
7.7 KiB
TOML

[versions]
# Build tooling. Pinned, never floating. agp and kotlin are coupled (below); ksp is
# staged for a later phase and unapplied today -- KSP2 versions independently of Kotlin.
#
# AGP 9 has BUILT-IN Kotlin: it compiles Kotlin itself rather than delegating to the
# org.jetbrains.kotlin.android plugin, which is incompatible with its DSL and fails the
# build if applied. DO NOT add it.
#
# By default AGP compiles with the KGP it bundles -- 2.2.10 for AGP 9.3.1, per its POM --
# and that version caps jvmTarget at 24, which would cap the app's bytecode below the JDK
# everything else runs on. The root build.gradle.kts therefore puts KGP on the buildscript
# classpath explicitly, which AGP's built-in Kotlin picks up instead. That is the only
# reason `kotlin` here can be ahead of what AGP ships.
#
# The Compose compiler plugin is versioned in lockstep with Kotlin and is read from this
# same `kotlin` entry, so the two cannot drift. Raising `kotlin` means checking that a
# matching compose-compiler-gradle-plugin exists.
agp = "9.3.1"
kotlin = "2.4.10"
ksp = "2.3.11"
# AndroidX / Compose -- floating on minor + patch. The prerelease guard in
# app/build.gradle.kts is what keeps `+` from selecting an alpha: several of these
# (lifecycle, navigation, work, datastore, annotation) publish alphas and RCs with
# version numbers ABOVE their newest stable, and Gradle's `+` would take them.
# Bare `+`, not "2026.+": the year is the major in this scheme (YYYY.MM.PP), so a
# 2026-prefixed float would quietly stop finding releases on 1 January and keep
# building green against a frozen BOM.
composeBom = "+"
coreKtx = "1.+"
activityCompose = "1.+"
lifecycle = "2.+"
navigation = "2.+"
work = "2.+"
datastore = "1.+"
media3 = "1.+"
room = "2.+"
documentfile = "1.+"
annotation = "1.+"
# Test -- floating, same rules as above.
junit = "4.+"
androidxJunit = "1.+"
espressoCore = "3.+"
# PINNED, unlike its neighbours. Under semver a 0.x minor is allowed to break, and
# this library is load-bearing exactly where breakage is hardest to see: the wrapper
# reaches for smartexception.java.Exceptions only when an FFmpeg call FAILS, so a
# moved class surfaces as an R8 missing-class error at release time, or as a crash on
# the error path -- the least-exercised code in the app. Bump it deliberately.
smartException = "0.2.1"
# Lint/format. PINNED, deliberately, while the libraries above float.
#
# A library bump that misbehaves usually still compiles. An analysis-tool bump does
# something worse: a new rule in ktlint or detekt makes files nobody touched stop
# passing, so CI goes red on a PR whose diff cannot explain it. Upgrading these is
# therefore an act with its own commit -- run the tool, read the new findings, fix or
# relax them -- which is exactly the reviewable step floating is meant to skip.
#
# ktlint owns formatting; detekt owns static analysis (its formatting ruleset stays
# off, so the two can never disagree about the same line).
# detekt 2.0 is the only line with Gradle 9 support -- stable 1.23.x tops out at
# Gradle 8.12, and this project is on 9.7.1.
ktlint = "14.2.0"
detekt = "2.0.0-alpha.6"
# JaCoCo coverage agent. Pinned rather than inheriting whatever Gradle 9.7.1 bundles,
# so the agent version that reads Kotlin 2.2.10 bytecode is stated, not implied.
jacoco = "0.8.15"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
# Compose — versions come from the BOM, so no version.ref here.
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
compose-material3-windowsize = { group = "androidx.compose.material3", name = "material3-window-size-class" }
compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
compose-ui = { group = "androidx.compose.ui", name = "ui" }
compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }
androidx-work-testing = { group = "androidx.work", name = "work-testing", version.ref = "work" }
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" }
androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
# Media3 — the hardware fast path (Apache-2.0).
media3-transformer = { group = "androidx.media3", name = "media3-transformer", version.ref = "media3" }
media3-effect = { group = "androidx.media3", name = "media3-effect", version.ref = "media3" }
media3-common = { group = "androidx.media3", name = "media3-common", version.ref = "media3" }
media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
media3-muxer = { group = "androidx.media3", name = "media3-muxer", version.ref = "media3" }
# Room — job history. Added in a later phase; KSP plugin stays unapplied until then.
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
# Required at runtime by the ffmpeg-kit-next wrapper: AbstractSession.fail()
# references smartexception.java.Exceptions. Debug builds tolerate its absence through
# lazy class loading, so this only surfaces as a crash on the first FFmpeg error --
# or, as it did here, as an R8 missing-class error.
smart-exception-java = { group = "com.arthenica", name = "smart-exception-java", version.ref = "smartException" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
[plugins]
# com.android.application and org.jetbrains.kotlin.plugin.compose are deliberately absent.
# They come from the root buildscript classpath (see build.gradle.kts) so that a newer KGP
# can override AGP's bundled one, and the module applies them by id(). An alias here would
# be dead weight that reads like the source of truth.
#
# ktlint and detekt are ordinary plugin-portal resolutions and stay aliases.
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
detekt = { id = "dev.detekt", version.ref = "detekt" }
# DO NOT add org.jetbrains.kotlin.android — AGP 9 built-in Kotlin makes it a build failure.