Skip to content
Merged
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
12 changes: 8 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
cache-cleanup: true

- name: Unit tests
run: ./gradlew allTests testDebugUnitTest validateDebugScreenshotTest verifyPaparazziDebug verifyRoborazziDebug verifyRoborazziJvm
run: ./gradlew allTests testDebugUnitTest validateDebugScreenshotTest verifyPaparazziDebug verifyRoborazziAndroidHostTest verifyRoborazziJvm

unit-tests-macos:
name: Unit tests (macOS)
Expand Down Expand Up @@ -122,7 +122,7 @@ jobs:
build-samples:
name: Build samples
runs-on: ubuntu-latest
timeout-minutes: 30
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
Expand All @@ -137,12 +137,16 @@ jobs:
cache-cleanup: true

- name: Build Compose and View samples
run: ./gradlew samples:compose:assembleDebug samples:view:assembleDebug
# Run the tasks separately to work around mergeDebugResources hanging.
run: |
./gradlew samples:compose:assemble
./gradlew samples:compose-android:assembleDebug
./gradlew samples:view:assembleDebug

build-samples-macos:
name: Build samples (macOS)
runs-on: macos-latest
timeout-minutes: 30
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ site
*.xcuserstate
**/xcuserdata

# Claude
settings.local.json

# https://github.com/square/okio/issues/1163
**/webpack.config.d/applyNodePolyfillPlugin.js
22 changes: 10 additions & 12 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ import coil3.groupId
import coil3.publicModules
import coil3.versionName
import com.diffplug.gradle.spotless.SpotlessExtension
import com.diffplug.gradle.spotless.SpotlessExtensionPredeclare
import dev.drewhamilton.poko.gradle.PokoPluginExtension
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension
import org.jetbrains.kotlin.gradle.dsl.abi.AbiValidationMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.abi.AbiValidationExtension
import org.jetbrains.kotlin.gradle.dsl.abi.AbiValidationMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.abi.AbiValidationVariantSpec
import org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile

buildscript {
repositories {
Expand Down Expand Up @@ -66,12 +65,12 @@ allprojects {

// Target JVM 8.
tasks.withType<JavaCompile>().configureEach {
sourceCompatibility = JavaVersion.VERSION_1_8.toString()
targetCompatibility = JavaVersion.VERSION_1_8.toString()
sourceCompatibility = JavaVersion.VERSION_11.toString()
targetCompatibility = JavaVersion.VERSION_11.toString()
options.compilerArgs = options.compilerArgs + "-Xlint:-options"
}
tasks.withType<KotlinJvmCompile>().configureEach {
compilerOptions.jvmTarget = JvmTarget.JVM_1_8
compilerOptions.jvmTarget = JvmTarget.JVM_11
}

// Uninstall test APKs after running instrumentation tests.
Expand All @@ -88,7 +87,7 @@ allprojects {

apply(plugin = "com.diffplug.spotless")

val configureSpotless: SpotlessExtension.() -> Unit = {
extensions.configure<SpotlessExtension> {
kotlin {
target("**/*.kt", "**/*.kts")
ktlint(libs.versions.ktlint.get()).editorConfigOverride(ktlintRules)
Expand All @@ -98,11 +97,10 @@ allprojects {
}
}

if (project === rootProject) {
spotless { predeclareDeps() }
extensions.configure<SpotlessExtensionPredeclare>(configureSpotless)
} else {
extensions.configure(configureSpotless)
tasks.configureEach {
if (name.startsWith("spotless")) {
notCompatibleWithConfigurationCache("https://github.com/diffplug/spotless/issues/2459")
}
}

plugins.withId("org.jetbrains.kotlin.plugin.compose") {
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/coil3/LinkSkikoBuiltinsTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ abstract class LinkSkikoBuiltinsTask @Inject constructor() : DefaultTask() {

for ((bits, cType) in typeMap) {
for ((suffix, orderConst) in memoryOrders) {
builder.appendLine("__attribute__((visibility(\"default\"))) $cType __aarch64_ldadd${bits}_${suffix}($cType value, $cType *ptr) {")
builder.appendLine("__attribute__((visibility(\"default\"))) $cType __aarch64_ldadd${bits}_$suffix($cType value, $cType *ptr) {")
builder.appendLine(" return __atomic_fetch_add(ptr, value, $orderConst);")
builder.appendLine("}")
builder.appendLine()
Expand Down
8 changes: 6 additions & 2 deletions buildSrc/src/main/kotlin/coil3/hierarchyTemplate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ private fun KotlinHierarchyBuilder.groupNonJsCommon() {

private fun KotlinHierarchyBuilder.groupJvmCommon() {
group("jvmCommon") {
withAndroidTarget()
withJvm()
// Use withCompilations predicate to match Android targets from both
// the old plugin (androidTarget()) and the new plugin (com.android.kotlin.multiplatform.library)
withCompilations { compilation ->
compilation.target.platformType.name == "androidJvm" ||
compilation.target.platformType.name == "jvm"
}
}
}

Expand Down
10 changes: 0 additions & 10 deletions buildSrc/src/main/kotlin/coil3/multiplatform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,6 @@ fun Project.addAllMultiplatformTargets(
extensions.configure<KotlinMultiplatformExtension> {
applyCoilHierarchyTemplate()

val isAndroidApp = plugins.hasPlugin("com.android.application")
val isAndroidLibrary = plugins.hasPlugin("com.android.library")
if (isAndroidApp || isAndroidLibrary) {
androidTarget {
if (isAndroidLibrary) {
publishLibraryVariants("release")
}
}
}

jvm()

js {
Expand Down
185 changes: 118 additions & 67 deletions buildSrc/src/main/kotlin/coil3/projects.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package coil3

import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import com.android.build.api.dsl.Lint
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
Expand All @@ -21,6 +22,71 @@ import org.jetbrains.dokka.gradle.engine.parameters.KotlinPlatform
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

private val DISABLED_LINT_RULES = listOf(
"ComposableNaming",
"UnknownIssueId",
"UnsafeOptInUsageWarning",
"UnusedResources",
"UseSdkSuppress",
"VectorPath",
"VectorRaster",
)

private val RESOURCE_DUPLICATE_OVERRIDES = listOf(
"META-INF/AL2.0",
"META-INF/LGPL2.1",
"META-INF/*kotlin_module",
)

private fun Project.configureKotlinMultiplatform() {
plugins.withId("org.jetbrains.kotlin.multiplatform") {
extensions.configure<KotlinMultiplatformExtension> {
sourceSets.configureEach {
languageSettings {
optIn("coil3.annotation.DelicateCoilApi")
optIn("coil3.annotation.ExperimentalCoilApi")
optIn("coil3.annotation.InternalCoilApi")
}
}
compilerOptions {
freeCompilerArgs.addAll(
// https://kotlinlang.org/docs/compiler-reference.html#progressive
"-progressive",
// https://youtrack.jetbrains.com/issue/KT-61573
"-Xexpect-actual-classes",
)
}
}
}
}

private fun Project.configureKotlinCompile() {
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
allWarningsAsErrors.set(System.getenv("CI").toBoolean())

val arguments = mutableListOf(
// https://kotlinlang.org/docs/compiler-reference.html#progressive
"-progressive",
// Enable Java default method generation.
"-jvm-default=no-compatibility",
// Generate smaller bytecode by not generating runtime not-null assertions.
"-Xno-call-assertions",
"-Xno-param-assertions",
"-Xno-receiver-assertions",
)

if (project.name != "benchmark") {
arguments += "-opt-in=coil3.annotation.DelicateCoilApi"
arguments += "-opt-in=coil3.annotation.ExperimentalCoilApi"
arguments += "-opt-in=coil3.annotation.InternalCoilApi"
}

freeCompilerArgs.addAll(arguments)
}
}
}

fun Project.androidLibrary(
name: String,
config: Boolean = false,
Expand Down Expand Up @@ -58,6 +124,52 @@ fun Project.androidLibrary(
action()
}

fun Project.multiplatformAndroidLibrary(
name: String,
action: KotlinMultiplatformAndroidLibraryTarget.() -> Unit = {},
) {
configureKotlinMultiplatform()
configureKotlinCompile()

plugins.withId("org.jetbrains.kotlin.multiplatform") {
extensions.configure<KotlinMultiplatformExtension> {
targets.withType<KotlinMultiplatformAndroidLibraryTarget>().configureEach {
namespace = name
compileSdk = project.compileSdk
minSdk = project.minSdk

withHostTest {
isIncludeAndroidResources = true
}

withDeviceTest {
instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

lint {
warningsAsErrors = true
disable += DISABLED_LINT_RULES
}

packaging {
resources.pickFirsts += RESOURCE_DUPLICATE_OVERRIDES
}

action()
}
}
}

if (project.name in publicModules) {
apply(plugin = "org.jetbrains.dokka")
apply(plugin = "com.vanniktech.maven.publish.base")
setupDokka()
setupPublishing {
configure(KotlinMultiplatform(Dokka("dokkaGenerate")))
}
}
}

fun Project.setupPublishing(
action: MavenPublishBaseExtension.() -> Unit = {},
) {
Expand Down Expand Up @@ -146,6 +258,9 @@ private fun <T : BaseExtension> Project.androidBase(
name: String,
action: T.() -> Unit,
) {
configureKotlinMultiplatform()
configureKotlinCompile()

android<T> {
namespace = name
compileSdkVersion(project.compileSdk)
Expand All @@ -155,87 +270,23 @@ private fun <T : BaseExtension> Project.androidBase(
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
packagingOptions {
resources.pickFirsts += listOf(
"META-INF/AL2.0",
"META-INF/LGPL2.1",
"META-INF/*kotlin_module",
)
resources.pickFirsts += RESOURCE_DUPLICATE_OVERRIDES
}
testOptions {
unitTests.isIncludeAndroidResources = true
}
lint {
warningsAsErrors = true
disable += listOf(
"ComposableNaming",
"UnknownIssueId",
"UnsafeOptInUsageWarning",
"UnusedResources",
"UseSdkSuppress",
"VectorPath",
"VectorRaster",
)
disable += DISABLED_LINT_RULES
}
action()
}
plugins.withId("org.jetbrains.kotlin.multiplatform") {
extensions.configure<KotlinMultiplatformExtension> {
sourceSets.configureEach {
languageSettings {
optIn("coil3.annotation.DelicateCoilApi")
optIn("coil3.annotation.ExperimentalCoilApi")
optIn("coil3.annotation.InternalCoilApi")
}
}
targets.configureEach {
compilations.configureEach {
// https://youtrack.jetbrains.com/issue/KT-61573#focus=Comments-27-9822729.0-0
@Suppress("DEPRECATION")
compilerOptions.configure {
val arguments = listOf(
// https://kotlinlang.org/docs/compiler-reference.html#progressive
"-progressive",
// https://youtrack.jetbrains.com/issue/KT-61573
"-Xexpect-actual-classes",
)
freeCompilerArgs.addAll(arguments)
}
}
}
}
}
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
allWarningsAsErrors.set(System.getenv("CI").toBoolean())

val arguments = mutableListOf<String>()

// https://kotlinlang.org/docs/compiler-reference.html#progressive
arguments += "-progressive"

// Enable Java default method generation.
arguments += "-jvm-default=no-compatibility"

// Generate smaller bytecode by not generating runtime not-null assertions.
arguments += "-Xno-call-assertions"
arguments += "-Xno-param-assertions"
arguments += "-Xno-receiver-assertions"

if (project.name != "benchmark") {
arguments += "-opt-in=coil3.annotation.DelicateCoilApi"
arguments += "-opt-in=coil3.annotation.ExperimentalCoilApi"
arguments += "-opt-in=coil3.annotation.InternalCoilApi"
}

freeCompilerArgs.addAll(arguments)
}
}
}

private fun <T : BaseExtension> Project.android(action: T.() -> Unit) {
extensions.configure("android", action)
}

private fun BaseExtension.lint(action: Lint.() -> Unit) {
(this as CommonExtension<*, *, *, *, *, *>).lint(action)
(this as CommonExtension).lint.apply(action)
}
Loading