Configurando la extensión kotlin {}: toolchains y opciones del compilador
Fija un JDK con jvmToolchain, modela el bytecode con compilerOptions y haz que cada build sea reproducible.
Cuando aplicas el plugin `org.jetbrains.kotlin.jvm`, Gradle expone un bloque de configuración llamado la extensión `kotlin {}`. Este es el lugar único y oficial para indicarle al Kotlin Gradle Plugin cómo debe compilarse tu código: contra qué JDK construir, qué versión de bytecode de la JVM emitir y qué flags adicionales pasar al compilador. Todo lo que configures aquà lo leen las tareas de compilación de Kotlin, asà que se aplica de forma consistente a tus fuentes principales, tus pruebas y cualquier otro source set que gestione el plugin.
La lÃnea más importante para la mayorÃa de los proyectos es `jvmToolchain(17)`. Un toolchain es la abstracción de Gradle para 'el JDK exacto usado para compilar y ejecutar este código'. Cuando solicitas un toolchain, Gradle no usa ciegamente el `java` que esté en el `PATH` del desarrollador; en su lugar localiza un JDK instalado que coincida con esa versión (o, con el plugin Foojay resolver, descarga uno automáticamente) y fija tanto las tareas de compilación de Kotlin como las de Java a él. Esta es la clave de los builds reproducibles: un compañero con JDK 21, un runner de CI con JDK 17 y un portátil recién estrenado compilan todos contra el mismÃsimo JDK 17, produciendo bytecode idéntico en lugar de salidas sutilmente distintas que 'funcionan en mi máquina'.
Dentro de `compilerOptions { }` ajustas con precisión lo que hace el compilador. `jvmTarget.set(JvmTarget.JVM_17)` controla la versión de bytecode que realmente se escribe en los archivos `.class` — es conceptualmente distinto del toolchain (qué JDK ejecuta el compilador) aunque normalmente los mantengas alineados. `freeCompilerArgs.add("-Xjsr305=strict")` te permite pasar flags crudos del compilador que no tienen una propiedad dedicada en el DSL, como opt-ins para funciones experimentales o el manejo estricto de nulabilidad para la interoperabilidad con Java. Y `allWarningsAsErrors.set(true)` convierte cada advertencia del compilador en un fallo de build, una excelente práctica de higiene para librerÃas y módulos compartidos porque evita que APIs obsoletas y casts arriesgados se acumulen en silencio. Ten en cuenta que `compilerOptions` es la API moderna que reemplaza al antiguo bloque `kotlinOptions`, ahora obsoleto.
Conviene entender por qué el toolchain y `jvmTarget` son perillas distintas. El toolchain responde '¿con qué JDK ejecutamos javac y kotlinc?', mientras que `jvmTarget` responde '¿cuál es la JVM mÃnima que puede ejecutar los archivos .class resultantes?'. PodrÃas, por ejemplo, compilar con un toolchain de JDK 21 pero apuntar a bytecode de JVM 17 para que el artefacto siga ejecutándose en servidores con Java 17. En la práctica, cuando defines `jvmToolchain(17)` el plugin de Kotlin ya infiere un `jvmTarget` coincidente, asà que solo sobrescribes `jvmTarget` explÃcitamente cuando quieres deliberadamente que ambos difieran. Mantenerlos iguales es la opción por defecto más segura y evita sorprendentes `UnsupportedClassVersionError` en tiempo de ejecución por bytecode más nuevo aterrizando en una JVM más antigua.
En una configuración moderna de Gradle 8 estos ajustes viven en `build.gradle.kts` usando el Kotlin DSL, mientras que las versiones de plugins y dependencias se centralizan en un catálogo de versiones `gradle/libs.versions.toml` que Gradle detecta automáticamente, y `settings.gradle.kts` conecta los repositorios y la resolución. Añadir el plugin `org.gradle.toolchains.foojay-resolver-convention` en `settings.gradle.kts` es muy recomendable: le enseña a Gradle a aprovisionar automáticamente el JDK solicitado cuando no está ya instalado, de modo que un clon nuevo compila sin que nadie tenga que descargar Java manualmente primero. Juntas, estas piezas convierten 'instala el JDK correcto y luego compila' en un único `./gradlew build` que se comporta igual en todas partes.
La recompensa es un build portable, determinista y autodocumentado. Cualquiera que lea `build.gradle.kts` puede ver exactamente a qué JDK y nivel de bytecode se compromete el proyecto, CI y las máquinas locales convergen en un comportamiento idéntico del compilador, y las polÃticas de advertencia-como-error mantienen limpio el código con el tiempo. Como Gradle cachea los toolchains y las tareas de compilación son deterministas dados los mismos inputs, también obtienes builds incrementales más rápidos y un build cache fiable — la reproducibilidad y la velocidad terminan reforzándose mutuamente.
// build.gradle.kts — configuring the kotlin {} extensionimport org.jetbrains.kotlin.gradle.dsl.JvmTargetplugins {alias(libs.plugins.kotlin.jvm) // version comes from the catalog}kotlin {jvmToolchain(17) // pin compile + run to JDK 17 for reproducible buildscompilerOptions {jvmTarget.set(JvmTarget.JVM_17) // bytecode version emitted to .class filesfreeCompilerArgs.add("-Xjsr305=strict") // strict nullability for Java interopallWarningsAsErrors.set(true) // fail the build on any compiler warning}}
The kotlin {} block pins the JDK, the bytecode target, and compiler flags in one place. compilerOptions is the modern replacement for the deprecated kotlinOptions.
# gradle/libs.versions.toml — centralized version catalog (auto-detected by Gradle)[versions]kotlin = "2.1.0"[plugins]# referenced as libs.plugins.kotlin.jvm in build.gradle.ktskotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
Keep the Kotlin version in one catalog so every module stays in sync.
// settings.gradle.kts — auto-provision the requested JDK toolchainplugins {// downloads the requested JDK automatically if it isn't installed locallyid("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"}dependencyResolutionManagement {repositories { mavenCentral() } // where dependencies resolve from}rootProject.name = "my-kotlin-app"
The Foojay resolver lets a fresh clone build without manually installing Java first.
🧠Comprueba tu comprensión
0/1 · 0/1 answered1. Why does calling jvmToolchain(17) make a Kotlin build more reproducible across different machines?