Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). All scales should have the 'format' parameter.

## [Unreleased]

### Changed

- Migrated to Gradle Version Catalog (`libs.versions.toml`) for centralized dependency management
- Updated Kotlin from 2.1.0 to 2.2.20
- Updated Gradle wrapper from 8.6 to 8.13.2
- Updated Android Gradle Plugin from 8.6.0 to 8.13.2
- Updated AndroidX Compose BOM to 2025.12.01
- Updated AndroidX Activity Compose to 1.12.2

### Internal

- Centralized version management in `gradle/libs.versions.toml`
- Removed version declarations from `gradle.properties`
- Updated all subproject build scripts to use version catalog references
- Updated Gradle wrapper scripts and binary to 8.13.2

## [3.0.2] - 2025-12-22

### Compatibility
Expand Down
251 changes: 134 additions & 117 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,151 +3,168 @@
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/

@file:Suppress("UnstableApiUsage")

// okhttp3 added for publishing to the Sonatype Central Repository:
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.asRequestBody
import java.util.*
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import java.util.Base64
import java.util.Properties

buildscript {
dependencies {
classpath("com.squareup.okhttp3:okhttp:4.12.0")
}
}
buildscript { dependencies { classpath(libs.okhttp) } }

plugins {
// this is necessary to avoid the plugins to be loaded multiple times
// in each subproject's classloader
kotlin("multiplatform").apply(false)
kotlin("plugin.compose").apply(false)
kotlin("jvm").apply(false)
id("org.jetbrains.compose").apply(false)

kotlin("android").apply(false)
id("com.android.application").apply(false)
id("com.android.library").apply(false)

id("io.codearte.nexus-staging").apply(false)
id("io.github.gradle-nexus.publish-plugin")
}

val localProps = Properties()
if (project.file("local.properties").exists()) {
localProps.load(project.file("local.properties").inputStream())
} else {
error("Couldn't read local.properties")
alias(libs.plugins.kotlin.multiplatform) apply false
alias(libs.plugins.kotlin.compose.compiler) apply false
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.multiplatform.android.library) apply false
alias(libs.plugins.nexus.staging) apply false
alias(libs.plugins.nexus.publish)
}

allprojects {
group = "org.jetbrains.lets-plot"
version = "3.0.3-SNAPSHOT"
// version = "0.0.0-SNAPSHOT" // for local publishing only
// =============================
// Properties & Config
// =============================

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().all {
kotlinOptions {
jvmTarget = "11"
}
val localProps =
Properties().apply {
val localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
load(localPropertiesFile.inputStream())
}
}

tasks.withType<JavaCompile>().all {
sourceCompatibility = "11"
targetCompatibility = "11"
}
}
val javaVersion: String = libs.versions.java.get()
val javaLanguageVersion: JavaLanguageVersion = JavaLanguageVersion.of(javaVersion)

// define the Maven Repository URL. Currently set to a local path for uploading
// artifacts to the Sonatype Central Repository.
val mavenReleasePublishUrl by extra { layout.buildDirectory.dir("maven/artifacts").get().toString() }
// define Maven Snapshot repository URL.
val mavenSnapshotPublishUrl by extra { "https://central.sonatype.com/repository/maven-snapshots/" }
// =============================
// Maven Publishing Config
// =============================

// define Sonatype Central Repository settings:
val mavenReleasePublishUrl by extra {
layout.buildDirectory.dir("maven/artifacts").get().toString()
}
val mavenSnapshotPublishUrl by extra { "https://central.sonatype.com/repository/maven-snapshots/" }
val sonatypeUsername by extra { localProps["sonatype.username"] ?: "" }
val sonatypePassword by extra { localProps["sonatype.password"] ?: "" }

val packageMavenArtifacts by tasks.registering(Zip::class) {
// =============================
// All Projects Config
// =============================

from(mavenReleasePublishUrl)
archiveFileName.set("${project.name}-artifacts.zip")
destinationDirectory.set(layout.buildDirectory)
allprojects {
group = "org.jetbrains.lets-plot"
version = "3.0.3-SNAPSHOT"
}
val uploadMavenArtifacts by tasks.registering {
dependsOn(packageMavenArtifacts)

doLast {
val uriBase = "https://central.sonatype.com/api/v1/publisher/upload"
val publishingType = "USER_MANAGED"
val deploymentName = "${project.name}-$version"
val uri = "$uriBase?name=$deploymentName&publishingType=$publishingType"

val userName = sonatypeUsername as String
val password = sonatypePassword as String
val base64Auth = Base64.getEncoder().encode("$userName:$password".toByteArray()).toString(Charsets.UTF_8)
val bundleFile = packageMavenArtifacts.get().archiveFile.get().asFile

println("Sending request to $uri...")

val client = OkHttpClient()
val request = Request.Builder()
.url(uri)
.header("Authorization", "Bearer $base64Auth")
.post(
MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("bundle", bundleFile.name, bundleFile.asRequestBody())
.build()
)
.build()

client.newCall(request).execute().use { response ->
val statusCode = response.code
println("Upload status code: $statusCode")
println("Upload result: ${response.body!!.string()}")
if (statusCode != 201) {
error("Upload error to Central repository. Status code $statusCode.")
}
}
// =============================
// Subprojects Config
// =============================

subprojects {
// Kotlin Configuration
plugins.withType<KotlinBasePlugin> {
extensions.configure<KotlinProjectExtension> {
jvmToolchain { languageVersion.set(javaLanguageVersion) }
}

tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions {
apiVersion.set(KotlinVersion.KOTLIN_2_2)
languageVersion.set(KotlinVersion.KOTLIN_2_2)
allWarningsAsErrors.set(false)
optIn.addAll("kotlin.RequiresOptIn", "kotlin.ExperimentalStdlibApi")
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
}

// Java Configuration
plugins.withType<JavaPlugin> {
tasks.withType<JavaCompile>().configureEach {
sourceCompatibility = JavaVersion.toVersion(javaVersion).majorVersion
targetCompatibility = JavaVersion.toVersion(javaVersion).majorVersion

options.apply {
encoding = Charsets.UTF_8.name()
isFork = true
isIncremental = true
}
}
}

// Javadoc JAR for publishing
val jarJavaDocs by
tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
group = "publishing"
from(rootProject.file("README.md"))
}

// Workaround for signing issue: https://github.com/gradle/gradle/issues/26091
tasks.withType<AbstractPublishToMaven>().configureEach { mustRunAfter(tasks.withType<Sign>()) }
}

// =============================
// Publishing Tasks
// =============================

val packageMavenArtifacts by
tasks.registering(Zip::class) {
group = "publishing"
description = "Packages Maven artifacts for upload to Central Repository"
from(mavenReleasePublishUrl)
archiveFileName.set("${rootProject.name}-artifacts.zip")
destinationDirectory.set(layout.buildDirectory)
}

subprojects {
repositories {
mavenCentral()
google()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")

// Repositories where other projects publish their artifacts locally to.
localProps["maven.repo.local"]?.let {
(it as String).split(",").forEach { repo ->
mavenLocal {
url = uri(repo)
}
}
val uploadMavenArtifacts by
tasks.registering {
group = "publishing"
description = "Uploads Maven artifacts to Sonatype Central Repository"
dependsOn(packageMavenArtifacts)

doLast {
val uploadUrl = buildString {
append("https://central.sonatype.com/api/v1/publisher/upload")
append("?name=${rootProject.name}-$version")
append("&publishingType=USER_MANAGED")
}

// SNAPSHOTS
maven(url = mavenSnapshotPublishUrl)
val credentials = "$sonatypeUsername:$sonatypePassword"
val base64Auth = Base64.getEncoder().encodeToString(credentials.toByteArray())
val bundleFile = packageMavenArtifacts.get().archiveFile.get().asFile

mavenLocal()
}
logger.lifecycle("Uploading to: $uploadUrl")

val jarJavaDocs by tasks.creating(Jar::class) {
archiveClassifier.set("javadoc")
group = "lets plot"
from("$rootDir/README.md")
}
val request =
Request.Builder()
.url(uploadUrl)
.header("Authorization", "Bearer $base64Auth")
.post(
MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("bundle", bundleFile.name, bundleFile.asRequestBody())
.build()
)
.build()

// ------------------------------------------
// Workaround for the error when signing published artifacts.
// It seems to appear after switching to Gradle 8.3
// For details see: https://github.com/gradle/gradle/issues/26091 :
// Publishing a KMP project with signing fails with "Task ... uses this output of task ... without declaring an explicit or implicit dependency"
// https://github.com/gradle/gradle/issues/26091
tasks.withType<AbstractPublishToMaven>().configureEach {
val signingTasks = tasks.withType<Sign>()
mustRunAfter(signingTasks)
OkHttpClient().newCall(request).execute().use { response ->
val statusCode = response.code
val responseBody = response.body?.string() ?: ""

logger.lifecycle("Upload status: $statusCode")
logger.lifecycle("Response: $responseBody")

check(statusCode == 201) { "Upload failed with status $statusCode: $responseBody" }
}
}
}
}
48 changes: 21 additions & 27 deletions demo/plot/compose-android-median/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
*/

plugins {
kotlin("android")
id("com.android.application")
kotlin("plugin.compose")
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose.compiler)
}


android {
compileSdk = (findProperty("android.compileSdk") as String).toInt()
compileSdk = libs.versions.android.compileSdk.get().toInt()
namespace = "demo.plot.CanvasDemo"

buildFeatures {
Expand All @@ -21,8 +20,8 @@ android {
defaultConfig {
applicationId = "demo.plot.CanvasDemo"

minSdk = (findProperty("android.minSdk") as String).toInt()
targetSdk = (findProperty("android.targetSdk") as String).toInt()
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.compileSdk.get().toInt()

versionCode = 1
versionName = "1.0"
Expand All @@ -35,31 +34,26 @@ android {
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

kotlin {
jvmToolchain(11)
jvmToolchain(21)
}
}

val androidComposeBom = extra["androidx.compose.bom"] as String
val androidxActivityCompose = extra["androidx.activity.compose"] as String
val letsPlotVersion = extra["letsPlot.version"] as String
val letsPlotKotlinVersion = extra["letsPlotKotlin.version"] as String

dependencies {
implementation(platform("androidx.compose:compose-bom:$androidComposeBom"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material:material")
implementation("androidx.activity:activity-compose:$androidxActivityCompose")

implementation("org.jetbrains.lets-plot:lets-plot-kotlin-kernel:$letsPlotKotlinVersion")
implementation("org.jetbrains.lets-plot:lets-plot-common:$letsPlotVersion")
implementation("org.jetbrains.lets-plot:canvas:$letsPlotVersion")
implementation("org.jetbrains.lets-plot:plot-raster:$letsPlotVersion")

implementation(project(":lets-plot-compose"))
implementation(project(":demo-plot-shared"))
implementation(project.dependencies.platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.material)
implementation(libs.androidx.activity.compose)

implementation(libs.letsplot.kotlin.kernel)
implementation(libs.letsplot.common)
implementation(libs.letsplot.canvas)
implementation(libs.letsplot.plot.raster)

implementation(projects.letsPlotCompose)
implementation(projects.demo.plot.shared)
}
Loading