Skip to content

Commit 3ceb048

Browse files
committed
Update
1 parent 24c509f commit 3ceb048

File tree

2 files changed

+146
-20
lines changed

2 files changed

+146
-20
lines changed

sentry-kotlin-multiplatform-gradle-plugin/src/main/java/io/sentry/kotlin/multiplatform/gradle/SentryPlugin.kt

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@ import org.gradle.api.plugins.ExtensionAware
77
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
88
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension
99
import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin
10+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
11+
import org.jetbrains.kotlin.gradle.utils.named
1012
import org.jetbrains.kotlin.konan.target.HostManager
1113
import org.slf4j.LoggerFactory
14+
import java.util.Locale
1215

1316
internal const val SENTRY_EXTENSION_NAME = "sentryKmp"
1417
internal const val LINKER_EXTENSION_NAME = "linker"
1518
internal const val AUTO_INSTALL_EXTENSION_NAME = "autoInstall"
1619
internal const val COCOAPODS_AUTO_INSTALL_EXTENSION_NAME = "cocoapods"
1720
internal const val COMMON_MAIN_AUTO_INSTALL_EXTENSION_NAME = "commonMain"
1821
internal const val KOTLIN_EXTENSION_NAME = "kotlin"
22+
internal const val LINK_SENTRY_COCOA_FRAMEWORKS_TASK_NAME = "linkSentryCocoaFrameworks"
1923

2024
@Suppress("unused")
2125
class SentryPlugin : Plugin<Project> {
@@ -43,9 +47,13 @@ class SentryPlugin : Plugin<Project> {
4347
}
4448
}
4549

46-
internal fun executeConfiguration(project: Project, hostIsMac: Boolean = HostManager.hostIsMac) {
50+
internal fun executeConfiguration(
51+
project: Project,
52+
hostIsMac: Boolean = HostManager.hostIsMac
53+
) {
4754
val sentryExtension = project.extensions.getByType(SentryExtension::class.java)
48-
val hasCocoapodsPlugin = project.plugins.findPlugin(KotlinCocoapodsPlugin::class.java) != null
55+
val hasCocoapodsPlugin =
56+
project.plugins.findPlugin(KotlinCocoapodsPlugin::class.java) != null
4957

5058
if (sentryExtension.autoInstall.enabled.get()) {
5159
val autoInstall = sentryExtension.autoInstall
@@ -65,8 +73,9 @@ class SentryPlugin : Plugin<Project> {
6573
// executing the (potentially expensive) path-resolution logic when the build is only
6674
// concerned with non-Apple targets such as Android.
6775

68-
val kmpExtension = project.extensions.findByName(KOTLIN_EXTENSION_NAME) as? KotlinMultiplatformExtension
69-
?: throw GradleException("Error fetching Kotlin Multiplatform extension.")
76+
val kmpExtension =
77+
project.extensions.findByName(KOTLIN_EXTENSION_NAME) as? KotlinMultiplatformExtension
78+
?: throw GradleException("Error fetching Kotlin Multiplatform extension.")
7079

7180
val appleTargets = kmpExtension.appleTargets().toList()
7281

@@ -75,30 +84,37 @@ class SentryPlugin : Plugin<Project> {
7584
return
7685
}
7786

78-
project.gradle.taskGraph.whenReady { taskGraph ->
79-
val targetsInGraph = appleTargets.filter { target ->
80-
taskGraph.allTasks.any { task -> task.path.contains(target.name, ignoreCase = true) }
87+
project.gradle.taskGraph.whenReady { graph ->
88+
// Check which of the Kotlin/Native targets are actually in the graph
89+
val activeTargets = appleTargets.filter { target ->
90+
val targetName = target.name.replaceFirstChar {
91+
it.uppercase()
92+
}
93+
val path = if (project.path == ":")
94+
":compileKotlin$targetName"
95+
else
96+
"${project.path}:compileKotlin$targetName"
97+
// Will throw if it doesn't exist
98+
graph.hasTask(path)
8199
}
82100

83-
if (targetsInGraph.isEmpty()) {
84-
project.logger.info("No Apple target tasks requested – skipping Sentry Cocoa framework linking.")
101+
if (activeTargets.isEmpty()) {
102+
project.logger.lifecycle("No Apple compile task scheduled for this build - skipping Sentry Cocoa framework linking")
85103
return@whenReady
86104
}
87105

88-
project.logger.info(
89-
"Configuring Sentry Cocoa framework linking for Apple targets present in the task graph: " +
90-
targetsInGraph.joinToString { it.name }
91-
)
106+
project.logger.lifecycle("Set up Sentry Cocoa linking for target: ${activeTargets.first().name}")
92107

93108
CocoaFrameworkLinker(
94-
logger = project.logger,
95-
pathResolver = FrameworkPathResolver(project),
96-
binaryLinker = FrameworkLinker(project.logger)
97-
).configure(targetsInGraph)
109+
logger = project.logger,
110+
pathResolver = FrameworkPathResolver(project),
111+
binaryLinker = FrameworkLinker(project.logger)
112+
).configure(appleTargets = activeTargets)
98113
}
99114
}
100115
}
101116

117+
102118
companion object {
103119
internal val logger by lazy {
104120
LoggerFactory.getLogger(SentryPlugin::class.java)
@@ -120,9 +136,9 @@ internal fun Project.installSentryForKmp(
120136
if (unsupportedTargets.any { unsupported -> target.name.contains(unsupported) }) {
121137
throw GradleException(
122138
"Unsupported target: ${target.name}. " +
123-
"Cannot auto install in commonMain. " +
124-
"Please create an intermediate sourceSet with targets that the Sentry SDK " +
125-
"supports (apple, jvm, android) and add the dependency manually."
139+
"Cannot auto install in commonMain. " +
140+
"Please create an intermediate sourceSet with targets that the Sentry SDK " +
141+
"supports (apple, jvm, android) and add the dependency manually."
126142
)
127143
}
128144
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package io.sentry.kotlin.multiplatform.gradle
2+
3+
import java.io.ByteArrayOutputStream
4+
import java.io.File
5+
import java.io.OutputStreamWriter
6+
import org.gradle.testkit.runner.GradleRunner
7+
import org.gradle.testkit.runner.TaskOutcome
8+
import org.gradle.testkit.runner.internal.PluginUnderTestMetadataReading
9+
import org.gradle.testkit.runner.internal.io.SynchronizedOutputStream
10+
import org.junit.jupiter.api.Test
11+
import org.junit.jupiter.api.condition.EnabledOnOs
12+
import org.junit.jupiter.api.condition.OS
13+
import org.junit.jupiter.api.io.TempDir
14+
import kotlin.test.assertContains
15+
import kotlin.test.assertEquals
16+
17+
@EnabledOnOs(OS.MAC) // the Cocoa linker only runs on macOS hosts
18+
class CocoaFrameworkLinkerIT {
19+
20+
private companion object {
21+
private const val KOTLIN_VERSION = "1.9.24"
22+
}
23+
24+
/**
25+
* Verifies that the Cocoa linker is **not** configured when the task graph
26+
* contains only non-Apple targets (here: `compileKotlinJvm`).
27+
*/
28+
@Test
29+
fun `linker is not configured when only non-Apple tasks are requested`(@TempDir projectDir: File) {
30+
writeBuildFiles(projectDir)
31+
32+
val output = ByteArrayOutputStream()
33+
defaultRunner(projectDir, output)
34+
.withArguments("compileKotlinJvm", "--dry-run", "--info")
35+
.build()
36+
37+
assertContains(output.toString(), "No Apple compile task scheduled for this build - skipping Sentry Cocoa framework linking")
38+
}
39+
40+
/**
41+
* Verifies that the Cocoa linker **is** configured when at least one Apple
42+
* task is present in the task graph (here: `compileKotlinIosX64`).
43+
*/
44+
@Test
45+
fun `linker is configured when an Apple task is requested`(@TempDir projectDir: File) {
46+
writeBuildFiles(projectDir)
47+
48+
val output = ByteArrayOutputStream()
49+
val runner = defaultRunner(projectDir, output)
50+
.withArguments("compileKotlinIosSimulatorArm64", "--dry-run", "--info")
51+
.build()
52+
53+
println("-----")
54+
println(output.toString());
55+
assertContains(output.toString(), "Set up Sentry Cocoa linking for target: iosSimulatorArm64")
56+
assertContains(output.toString(), "Start resolving Sentry Cocoa framework paths for target: iosSimulatorArm64")
57+
}
58+
59+
// ---------------------------------------------------------------------
60+
// test-fixture helpers
61+
// ---------------------------------------------------------------------
62+
63+
private fun writeBuildFiles(dir: File) {
64+
File(dir, "settings.gradle").writeText("""rootProject.name = "fixture"""")
65+
66+
val pluginClasspath = PluginUnderTestMetadataReading
67+
.readImplementationClasspath()
68+
.joinToString(", ") { "\"${it.absolutePath.replace('\\', '/')}\"" }
69+
70+
File(dir, "build.gradle").writeText(
71+
"""
72+
buildscript {
73+
repositories {
74+
google()
75+
mavenCentral()
76+
gradlePluginPortal()
77+
}
78+
dependencies {
79+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
80+
classpath files($pluginClasspath)
81+
}
82+
}
83+
84+
apply plugin: 'org.jetbrains.kotlin.multiplatform'
85+
apply plugin: 'io.sentry.kotlin.multiplatform.gradle'
86+
87+
repositories {
88+
google()
89+
mavenCentral()
90+
}
91+
92+
kotlin {
93+
jvm() // non-Apple target
94+
iosSimulatorArm64() // Apple target
95+
}
96+
"""
97+
.trimIndent()
98+
)
99+
}
100+
101+
/** Returns a pre-configured [GradleRunner] that logs into [out]. */
102+
private fun defaultRunner(projectDir: File, out: ByteArrayOutputStream): GradleRunner =
103+
GradleRunner.create()
104+
.withProjectDir(projectDir)
105+
.withPluginClasspath()
106+
.withGradleVersion(org.gradle.util.GradleVersion.current().version)
107+
.forwardStdOutput(OutputStreamWriter(SynchronizedOutputStream(out)))
108+
.forwardStdError(OutputStreamWriter(SynchronizedOutputStream(out)))
109+
.withArguments("--stacktrace")
110+
}

0 commit comments

Comments
 (0)