Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add plugin support #230

Merged
merged 82 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
5616413
Add raw working impl
buenaflor Apr 24, 2024
4b48be5
Update
buenaflor Apr 25, 2024
51130b0
Update
buenaflor Apr 25, 2024
3410510
Update
buenaflor Apr 25, 2024
2499417
Update
buenaflor May 3, 2024
e1ba06b
Update plugin
buenaflor May 4, 2024
3937b01
Update
buenaflor May 4, 2024
5438066
Update
buenaflor May 15, 2024
f6b4e7c
Update
buenaflor Jun 5, 2024
429e3b1
Update
buenaflor Jun 5, 2024
ed5dce8
Update
buenaflor Jun 5, 2024
99104d4
Updatee
buenaflor Jun 5, 2024
0dbb7f6
Update doc
buenaflor Jun 5, 2024
454f85f
Update docs
buenaflor Jun 5, 2024
5bc7070
Remove print
buenaflor Jun 5, 2024
36aab7b
Update
buenaflor Jun 5, 2024
6ef48aa
Update
buenaflor Jun 6, 2024
0bd2eb9
Update comment
buenaflor Jun 6, 2024
18f7906
Update
buenaflor Jun 6, 2024
5b7ccc0
Update
buenaflor Jun 7, 2024
8f8c587
Update
buenaflor Jun 7, 2024
764eda2
Format
buenaflor Jun 7, 2024
de523ac
Format
buenaflor Jun 7, 2024
81b5ab9
Merge branch 'main' into feat/add-plugin
buenaflor Jun 10, 2024
8fa4104
Update plugin-build/plugin/src/main/java/io/sentry/kotlin/multiplatfo…
buenaflor Jun 10, 2024
0cdeace
check if isStatic in linking
buenaflor Jun 10, 2024
6fd5837
Update plugin build dir name and ID
buenaflor Jun 10, 2024
f1e31a3
Add deps only with compileOnly
buenaflor Jun 10, 2024
392d336
Update
buenaflor Jun 10, 2024
370a396
Updaet
buenaflor Jun 10, 2024
409ec2f
Include gradle plugin version bump
buenaflor Jun 10, 2024
abcce27
Update project name
buenaflor Jun 10, 2024
6108cda
Use buildconfig
buenaflor Jun 10, 2024
5dc9130
Remove todo
buenaflor Jun 10, 2024
38db5e8
Change sentry extension name to sentryKmp
buenaflor Jun 10, 2024
fb1ca4b
Set androidNative to unsupported targets
buenaflor Jun 10, 2024
d3e2891
add symlinks
buenaflor Jun 10, 2024
a2ce288
Update
buenaflor Jun 10, 2024
f6afac1
Update
buenaflor Jun 10, 2024
50a9e26
Update'
buenaflor Jun 10, 2024
8b718e3
Update
buenaflor Jun 10, 2024
51d5136
Add CI
buenaflor Jun 11, 2024
87f7a7f
Add to craft
buenaflor Jun 11, 2024
6b0eaa5
Remove package.resolved
buenaflor Jun 11, 2024
2f26925
Format
buenaflor Jun 11, 2024
bd2c537
Update
buenaflor Jun 11, 2024
38db1a9
Update changelog
buenaflor Jun 11, 2024
1317545
Enable cocoapods sample
buenaflor Jun 11, 2024
e96287a
Update
buenaflor Jun 11, 2024
b0b1050
Remove podspec from spm sample
buenaflor Jun 11, 2024
2a5cb18
Remove
buenaflor Jun 11, 2024
0e5253d
Format
buenaflor Jun 11, 2024
20f69c2
Update
buenaflor Jun 11, 2024
e2b0937
Fix deps
buenaflor Jun 11, 2024
97eeefe
update
buenaflor Jun 11, 2024
f8a1366
update
buenaflor Jul 31, 2024
56ab206
Update Changelog
buenaflor Jul 31, 2024
080ea92
add ~> to version
buenaflor Jul 31, 2024
f17244a
Add derived data value source for caching
buenaflor Jul 31, 2024
ec6cc7b
Merge branch 'main' into feat/add-plugin
buenaflor Jul 31, 2024
02f132d
Run test during ci
buenaflor Jul 31, 2024
c446cb1
Update
buenaflor Aug 1, 2024
a34cb65
formatting{
buenaflor Aug 1, 2024
e846c9c
format
buenaflor Aug 1, 2024
2c78dfd
Skip apple specific configs if there are not apple targets
buenaflor Aug 1, 2024
bdf62f7
Fix tests
buenaflor Aug 1, 2024
87e9183
Update CI and skip linker and cocoapods if not macOS host
buenaflor Aug 1, 2024
8698be5
Format code
getsentry-bot Aug 1, 2024
c3615e0
update
buenaflor Aug 1, 2024
9de03d2
Update
buenaflor Aug 1, 2024
af6385c
update
buenaflor Aug 1, 2024
6a981a8
Add compilation test through samples
buenaflor Aug 1, 2024
a6cf258
run allTests for every sample project
buenaflor Aug 1, 2024
2ddc1cd
Update CHANGELOG.md
buenaflor Aug 1, 2024
a1f2d67
Merge branch 'main' into feat/add-plugin
buenaflor Aug 1, 2024
ca88300
Upload codecov report
buenaflor Aug 1, 2024
d140c9a
Default working directory
buenaflor Aug 1, 2024
92db2b5
Merge branch 'main' into feat/add-plugin
buenaflor Aug 1, 2024
97f9bdd
Use kover 0.7.3
buenaflor Aug 1, 2024
d48a901
Update
buenaflor Aug 2, 2024
690eb1b
Update
buenaflor Aug 5, 2024
5720a9a
Update
buenaflor Aug 5, 2024
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
2 changes: 1 addition & 1 deletion .run/iosApp-spm.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="iosApp-spm" type="KmmRunConfiguration" factoryName="iOS Application" CONFIG_VERSION="1" EXEC_TARGET_ID="27607109-90E2-4A98-8165-C0FF206F6E6D" XCODE_PROJECT="$PROJECT_DIR$/sentry-samples/kmp-app-spm/iosApp.xcodeproj" XCODE_CONFIGURATION="Debug" XCODE_SCHEME="iosApp">
<configuration default="false" name="iosApp-spm" type="KmmRunConfiguration" factoryName="iOS Application" CONFIG_VERSION="1" XCODE_PROJECT="$PROJECT_DIR$/sentry-samples/kmp-app-spm/iosApp.xcodeproj" XCODE_CONFIGURATION="Debug" XCODE_SCHEME="iosApp">
<method v="2">
<option name="com.jetbrains.kmm.ios.BuildIOSAppTask" enabled="true" />
</method>
Expand Down
6 changes: 3 additions & 3 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
object Config {
val agpVersion = "7.4.2"
val kotlinVersion = "1.9.21"
val composeVersion = "1.5.11"
val kotlinVersion = "1.9.23"
val composeVersion = "1.6.1"
buenaflor marked this conversation as resolved.
Show resolved Hide resolved
val gradleMavenPublishPluginVersion = "0.18.0"

val multiplatform = "multiplatform"
Expand Down Expand Up @@ -36,7 +36,7 @@ object Config {
val sentryAndroid = "io.sentry:sentry-android:$sentryJavaVersion"
val sentryJava = "io.sentry:sentry:$sentryJavaVersion"

val sentryCocoaVersion = "8.20.0"
val sentryCocoaVersion = "8.25.0"
val sentryCocoa = "Sentry"

object Samples {
Expand Down
47 changes: 47 additions & 0 deletions plugin-build/build.gradle.kts
buenaflor marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import io.gitlab.arturbosch.detekt.Detekt

plugins {
alias(libs.plugins.kotlin) apply false
alias(libs.plugins.pluginPublish) apply false
alias(libs.plugins.detekt)
alias(libs.plugins.ktlint)
alias(libs.plugins.versionCheck)
}

allprojects {
group = property("GROUP").toString()
version = property("VERSION").toString()

apply {
plugin(rootProject.libs.plugins.detekt.get().pluginId)
plugin(rootProject.libs.plugins.ktlint.get().pluginId)
}

ktlint {
debug.set(false)
verbose.set(true)
android.set(false)
outputToConsole.set(true)
ignoreFailures.set(false)
enableExperimentalRules.set(true)
filter {
exclude("**/generated/**")
include("**/kotlin/**")
}
}

detekt {
config.setFrom(rootProject.files("../config/detekt/detekt.yml"))
}
}

tasks.withType<Detekt>().configureEach {
reports {
html.required.set(true)
html.outputLocation.set(file("build/reports/detekt.html"))
}
}

tasks.register("clean", Delete::class.java) {
delete(rootProject.layout.buildDirectory)
}
8 changes: 8 additions & 0 deletions plugin-build/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ID=io.sentry.kotlin.multiplatform.gradle.plugin
buenaflor marked this conversation as resolved.
Show resolved Hide resolved
VERSION=1.0.0
buenaflor marked this conversation as resolved.
Show resolved Hide resolved
GROUP=io.sentry
DISPLAY_NAME=An empty Gradle Plugin from a template
DESCRIPTION=An empty Gradle plugin created from a template
WEBSITE=https://github.com/cortinico/kotlin-gradle-plugin-template
VCS_URL=https://github.com/cortinico/kotlin-gradle-plugin-template
IMPLEMENTATION_CLASS=io.sentry.kotlin.multiplatform.gradle.plugin.SentryPlugin
17 changes: 17 additions & 0 deletions plugin-build/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[versions]
detekt = "1.23.6"
kotlin = "1.9.23"
ktlintGradle = "12.1.0"
pluginPublish = "1.2.1"
versionCheck = "0.51.0"

[plugins]
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt"}
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin"}
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradle"}
pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish"}
versionCheck = { id = "com.github.ben-manes.versions", version.ref = "versionCheck"}
vanniktech-publish = { id = "com.vanniktech.maven.publish", version = "0.18.0"}

[libraries]
junit = "junit:junit:4.13.2"
1 change: 1 addition & 0 deletions plugin-build/gradle/wrapper
1 change: 1 addition & 0 deletions plugin-build/gradlew
1 change: 1 addition & 0 deletions plugin-build/gradlew.bat
68 changes: 68 additions & 0 deletions plugin-build/plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import com.vanniktech.maven.publish.MavenPublishPluginExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm")
`java-gradle-plugin`
// alias(libs.plugins.pluginPublish)
id("de.undercouch.download") version "5.6.0"
alias(libs.plugins.vanniktech.publish)
id("distribution")
}

dependencies {
implementation(kotlin("stdlib"))
buenaflor marked this conversation as resolved.
Show resolved Hide resolved
implementation(gradleApi())
implementation(kotlin("gradle-plugin"))

testImplementation(libs.junit)
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}

gradlePlugin {
plugins {
create(property("ID").toString()) {
id = property("ID").toString()
implementationClass = property("IMPLEMENTATION_CLASS").toString()
version = property("VERSION").toString()
description = property("DESCRIPTION").toString()
displayName = property("DISPLAY_NAME").toString()
tags.set(listOf("plugin", "gradle", "sample", "template"))
}
}
}

gradlePlugin {
website.set(property("WEBSITE").toString())
vcsUrl.set(property("VCS_URL").toString())
}

val publish = extensions.getByType(MavenPublishPluginExtension::class.java)
// signing is done when uploading files to MC
// via gpg:sign-and-deploy-file (release.kts)
publish.releaseSigningEnabled = false

tasks.named("distZip").configure {
dependsOn("publishToMavenLocal")
}

val sep = File.separator

distributions {
main {
contents {
from("build${sep}libs")
from("build${sep}publications${sep}maven")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.sentry.kotlin.multiplatform.gradle.plugin

import org.gradle.api.Project
import org.gradle.api.provider.Property
import javax.inject.Inject


@Suppress("UnnecessaryAbstractClass")
abstract class LinkerExtension
@Inject
constructor(project: Project) {
private val objects = project.objects

val xcodeprojPath: Property<String> = objects.property(String::class.java)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.sentry.kotlin.multiplatform.gradle.plugin

import org.gradle.api.Project
import org.gradle.api.provider.Property
import javax.inject.Inject

@Suppress("UnnecessaryAbstractClass")
abstract class SentryExtension
@Inject
constructor(project: Project) {
private val objects = project.objects

/**
* Auto-installs the Sentry-Cocoa SDK pod if the cocoapods plugin is enabled.
*/
val autoInstallWithCocoapods: Property<Boolean> =
objects.property(Boolean::class.java).convention(true)

/**
* Linker configuration.
*
* If you use SPM this configuration is necessary for setting up linking the framework and test executable.
*/
val linker: LinkerExtension = objects.newInstance(LinkerExtension::class.java, project)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package io.sentry.kotlin.multiplatform.gradle.plugin

import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.ExtensionAware
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.Framework
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.TestExecutable
import java.io.ByteArrayOutputStream
import java.io.File

internal const val SENTRY_EXTENSION_NAME = "sentry"
buenaflor marked this conversation as resolved.
Show resolved Hide resolved
internal const val LINKER_EXTENSION_NAME = "linker"
internal const val COCOAPODS_PLUGIN_NAME = "org.jetbrains.kotlin.plugin.cocoapods"
internal const val KOTLIN_EXTENSION_NAME = "kotlin"

@Suppress("unused")
class SentryPlugin : Plugin<Project> {
override fun apply(project: Project): Unit = with(project) {
val extension =
project.extensions.create(SENTRY_EXTENSION_NAME, SentryExtension::class.java, project)
project.extensions.add(LINKER_EXTENSION_NAME, extension.linker)

afterEvaluate {
val hasCocoapodsPlugin = project.pluginManager.hasPlugin(COCOAPODS_PLUGIN_NAME)
if (hasCocoapodsPlugin && extension.autoInstallWithCocoapods.get()) {
installPod()
} else {
configureLinkingOptions(extension.linker)
}
}
}
}

private fun Project.configureLinkingOptions(linkerExtension: LinkerExtension) {
val kmpExtension = this.extensions.findByName(KOTLIN_EXTENSION_NAME)
if (kmpExtension !is KotlinMultiplatformExtension) {
// todo: log, not multiplatform found
return
}

val customXcodeprojPath = linkerExtension.xcodeprojPath.orNull
println("customXcodeprojPath: $customXcodeprojPath")

val derivedDataPath = findDerivedDataPath(customXcodeprojPath)

kmpExtension.appleTargets().all { target ->
val frameworkArchitecture = target.frameworkArchitecture()
val dynamicFrameworkPath =
"$derivedDataPath/SourcePackages/artifacts/sentry-cocoa/Sentry-Dynamic/Sentry-Dynamic.xcframework/$frameworkArchitecture"
val staticFrameworkPath =
"$derivedDataPath/SourcePackages/artifacts/sentry-cocoa/Sentry/Sentry.xcframework/$frameworkArchitecture"

target.binaries.all { binary ->
if (frameworkArchitecture == null) {
// todo: log, unsupported architecture
return@all
}

var path = dynamicFrameworkPath
if (!File(dynamicFrameworkPath).exists()) {
// if it doesn't exist still search for the static one since we need either one
// for test linking
// todo: log, dynamic framework not found, using static framework
path = staticFrameworkPath

if (!File(staticFrameworkPath).exists()) {
// todo: log, static framework also not found, error
return@all
}
}

val testExecutable = binary is TestExecutable
val dynamicFramework = binary is Framework && !binary.isStatic
if (testExecutable) {
binary.linkerOpts("-rpath", "$path/Sentry.framework")
binary.linkerOpts("-F$path")
} else if (dynamicFramework) {
binary.linkerOpts("-F$path")
}
}
}
}

private fun Project.findDerivedDataPath(customXcodeprojPath: String? = null): String {
val xcodeprojPath = customXcodeprojPath ?: findXcodeprojFile(rootDir)?.absolutePath
val buildDirOutput = ByteArrayOutputStream()
exec {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added fetching the derived data path as a value source

it.commandLine = listOf(
"xcodebuild", "-project", xcodeprojPath, "-showBuildSettings"
)
it.standardOutput = buildDirOutput
}
val buildSettings = buildDirOutput.toString("UTF-8")
val buildDirRegex = Regex("BUILD_DIR = (.+)")
buenaflor marked this conversation as resolved.
Show resolved Hide resolved
val buildDirMatch = buildDirRegex.find(buildSettings)
val buildDir = buildDirMatch?.groupValues?.get(1)
?: throw GradleException("BUILD_DIR not found in xcodebuild output")
val derivedDataPath = buildDir.replace("/Build/Products", "")
return derivedDataPath
}

private fun findXcodeprojFile(dir: File): File? {
val ignoredDirectories = listOf("build", "DerivedData")

fun searchDirectory(directory: File): File? {
val files = directory.listFiles() ?: return null

for (file in files) {
if (file.name in ignoredDirectories) {
continue
}

// Recursively search the subdirectory
val result = searchDirectory(file)
if (result != null) {
return result
}

if (file.extension == "xcodeproj") {
return file
}
}
return null
}

return searchDirectory(dir)
}

private fun KotlinMultiplatformExtension.appleTargets() =
targets.withType(KotlinNativeTarget::class.java)
.matching { it.konanTarget.family.isAppleFamily }

/**
* Installs the Sentry pod if not yet installed and configures the compiler options
*/
private fun Project.installPod() {
val kmpExtension = this.extensions.findByName(KOTLIN_EXTENSION_NAME)
if (kmpExtension !is KotlinMultiplatformExtension) {
return
}

(kmpExtension as ExtensionAware).extensions.configure(CocoapodsExtension::class.java) { cocoapods ->
val sentryPod = cocoapods.pods.findByName("Sentry")
if (sentryPod == null) {
cocoapods.pod("Sentry") {
version = "~> 8.25" // todo: check if this constraint is good enough
linkOnly = true
extraOpts += listOf("-compiler-option", "-fmodules")
}
}
}
}

/**
* Transforms a Kotlin Multiplatform target name to the architecture name that is found inside
* Sentry's framework directory.
*/
private fun KotlinNativeTarget.frameworkArchitecture(): String? {
return when (name) {
"iosSimulatorArm64" -> "ios-arm64_x86_64-simulator"
"iosX64" -> "ios-arm64_x86_64-simulator"
"iosArm64" -> "ios-arm64"
// todo: add more targets
else -> null
}
}
Loading