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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ jobs:
pip install --upgrade --requirement tests/requirements.txt
pytest --capture=no --verbose tests
(cd ndk && ./gradlew :sentry-native-ndk:connectedDebugAndroidTest --stacktrace)
(cd ndk && ./gradlew :sentry-native-ndk-outproc:connectedDebugAndroidTest --stacktrace)

- name: Retry emulator (Android)
if: ${{ steps.android-test.outcome == 'failure' && env.ANDROID_BOOTED != 'true' }}
Expand Down Expand Up @@ -637,4 +638,6 @@ jobs:
sentry-native-*.zip
sentry-native.zip
ndk/lib/build/distributions/*.zip
ndk/native/build/distributions/*.zip
ndk/lib/build/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib/*
ndk/native/build/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib/*
68 changes: 68 additions & 0 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,71 @@ jobs:
SENTRY_E2E_ORG: ${{ secrets.SENTRY_E2E_ORG }}
SENTRY_E2E_PROJECT: ${{ secrets.SENTRY_E2E_PROJECT }}
run: pytest --capture=no --verbose tests/test_e2e_sentry.py

android:
name: ${{ matrix.name }}
strategy:
fail-fast: false
matrix:
include:
- name: E2E Test (Android API 36, NDK 30)
ANDROID_API: 36
ANDROID_NDK_VERSION: 30.0.16138531
runs-on: ubuntu-latest
env:
ANDROID_API: ${{ matrix.ANDROID_API }}
ANDROID_ARCH: x86_64
ANDROID_NDK_VERSION: ${{ matrix.ANDROID_NDK_VERSION }}

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
submodules: recursive

- name: Check out sentry-java
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: getsentry/sentry-java
ref: jpnurmi/feat/native-android
path: sentry-java
Comment thread
jpnurmi marked this conversation as resolved.
Resolved

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"

- name: Install test dependencies
run: pip install -r tests/requirements.txt

- name: Set up Java
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: "temurin"
java-version: "21"

- name: Set up Gradle
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # pin@v6.3.0

- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

- name: Set up and run Android E2E tests
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
env:
SENTRY_E2E_DSN: ${{ secrets.SENTRY_E2E_DSN }}
SENTRY_E2E_AUTH_TOKEN: ${{ secrets.SENTRY_E2E_AUTH_TOKEN }}
SENTRY_E2E_ORG: ${{ secrets.SENTRY_E2E_ORG }}
SENTRY_E2E_PROJECT: ${{ secrets.SENTRY_E2E_PROJECT }}
with:
api-level: ${{ env.ANDROID_API }}
ndk: ${{ env.ANDROID_NDK_VERSION }}
arch: ${{ env.ANDROID_ARCH }}
target: google_apis
emulator-boot-timeout: 1200
script: |
export ANDROID_NDK="$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION"
python scripts/setup-android-e2e.py sentry-java
pytest --capture=no --verbose tests/test_e2e_sentry.py
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- Add `sentry_scope_set_trace` for per-scope traces. ([#2236](https://github.com/getsentry/sentry-native/pull/2236))
- Add `sentry_continue_trace` to continue incoming traces without a transaction. ([#2237](https://github.com/getsentry/sentry-native/pull/2237))
- Add `sentry_iter_trace_propagation_headers` to propagate traces without a span. ([#2240](https://github.com/getsentry/sentry-native/pull/2240))
- Native: add Android support. ([#1725](https://github.com/getsentry/sentry-native/pull/1725))

**Fixes**:

Expand Down
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ elseif(SENTRY_BACKEND STREQUAL "inproc")
elseif(SENTRY_BACKEND STREQUAL "breakpad")
set(SENTRY_BACKEND_BREAKPAD TRUE)
elseif(SENTRY_BACKEND STREQUAL "native")
if(ANDROID)
message(WARNING "The 'native' backend on Android is experimental. "
"It may have incomplete features or unexpected behavior.")
endif()
set(SENTRY_BACKEND_NATIVE TRUE)
elseif(SENTRY_BACKEND STREQUAL "none")
set(SENTRY_BACKEND_NONE TRUE)
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`.
- **native**: An out-of-process crash handler that uses a lightweight daemon
to monitor the application, generate minidumps, and send crash reports.
Supports Linux, macOS, and Windows, where it is the default. Compatible with TSAN and ASAN
sanitizers.
sanitizers. Support on Android is **experimental**.
- **none**: This builds `sentry-native` without a backend, so it does not handle
crashes. It is primarily used for tests.

Expand Down Expand Up @@ -364,7 +364,7 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`.
| | | | | | |
| Backends | | | | | |
| - crashpad | ✓ | ✓ | ✓ | | |
| - native | ☑ | ☑ | ☑ | | |
| - native | ☑ | ☑ | ☑ | (✓)*** | |
| - breakpad | ✓ | ✓ | ✓ | (✓)* | (✓)* |
| - inproc | ✓ | ✓ | ✓ | ☑ | |
| - none | ✓ | ✓ | ✓ | ✓ | |
Expand All @@ -376,6 +376,7 @@ Legend:
- (✓) supported with limitations
- `*`: `breakpad` on Android and iOS builds and should work according to upstream but is untested.
- `**`: `curl` as a transport works on Android but isn't used in any supported configuration to reduce the size of our artifacts.
- `***`: The `native` backend on Android is experimental.

In addition to platform support, the "Advanced Usage" section of the SDK docs now [describes the tradeoffs](https://docs.sentry.io/platforms/native/advanced-usage/backend-tradeoffs/) involved in choosing a suitable backend for a particular use case.

Expand Down
2 changes: 1 addition & 1 deletion ndk/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ api:

# Run tests and lint
check:
./gradlew :sentry-native-ndk:check
./gradlew :sentry-native-ndk:check :sentry-native-ndk-outproc:check
14 changes: 14 additions & 0 deletions ndk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,17 @@ The `ndk` project uses the Gradle build system in combination with CMake. You ca
## Development

Please see the [contribution guide](../CONTRIBUTING.md).

## Crash backends

`sentry-native-ndk` uses the inproc backend. The opt-in
`sentry-native-ndk-outproc` package includes the **experimental** native backend
and crash daemon. Both provide `libsentry.so` and must not be used together.

Android SDK apps select `sentry-android-ndk-outproc` through the Sentry Gradle plugin:

```kotlin
sentry {
ndkBackend.set("native")
}
```
210 changes: 209 additions & 1 deletion ndk/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import com.android.build.api.dsl.LibraryExtension
import com.diffplug.gradle.spotless.SpotlessPlugin
import com.diffplug.spotless.LineEnding
import com.vanniktech.maven.publish.MavenPublishBaseExtension
Expand Down Expand Up @@ -73,6 +74,11 @@ subprojects {
}
}
}
if (name == "sentry-native-ndk-outproc") {
plugins.withId("com.android.library") {
configureNdkLibrary()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Inproc module lost backend capability

Medium Severity

configureNdkLibrary() now runs only for sentry-native-ndk-native, so sentry-native-ndk no longer publishes the sentry-ndk-backend capability. Gradle can no longer reject depending on both AARs, which both ship libsentry.so and must stay mutually exclusive.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c451ff8. Configure here.

}

plugins.withId("io.gitlab.arturbosch.detekt") {
configure<DetektExtension> {
Expand Down Expand Up @@ -224,5 +230,207 @@ fun MavenPublishBaseExtension.assignAarTypes() {
}

apiValidation {
ignoredProjects.addAll(listOf("sample"))
ignoredProjects.addAll(listOf("sample", "sentry-native-ndk-outproc"))
}

fun Project.configureNdkLibrary() {
var sentryNativeSrc: String = "${project.projectDir}/../.."
val nativeBackend = project.name == "sentry-native-ndk-outproc"
val sentryBackend = if (nativeBackend) "native" else providers.gradleProperty("sentryBackend").orElse("inproc").get()
val libDir = rootProject.file("lib")
val sanitizer =
System.getenv("RUN_ANALYZER").orEmpty().split(',')
.firstOrNull { it == "asan" || it == "tsan" }

configure<LibraryExtension> {
compileSdk = 37
// retain AGP 8.7.3's default NDK to avoid changing the compiler and libc++ in a hotfix
ndkVersion = System.getenv("ANDROID_NDK")?.let { File(it).name } ?: "27.0.12077973"
namespace = "io.sentry.ndk"

testBuildType = "debug"

defaultConfig {
minSdk = 21

aarMetadata {
// avoid requiring consumers to compile against API 37
minCompileSdk = 1
}

externalNativeBuild {
cmake {
arguments.add(0, "-DSENTRY_BACKEND=$sentryBackend")
if (sentryBackend == "inproc") {
arguments.add(0, "-DSENTRY_TRANSPORT=none")
}
arguments.add(0, "-DANDROID_STL=${if (sanitizer != null) "c++_shared" else "c++_static"}")
arguments.add(0, "-DSENTRY_NATIVE_SRC=$sentryNativeSrc")
if (sanitizer == "asan") {
arguments.add(0, "-DWITH_ASAN_OPTION=ON")
}
if (sanitizer == "tsan") {
arguments.add(0, "-DWITH_TSAN_OPTION=ON")
}
}
}

ndk {
abiFilters.addAll(
if (sanitizer == "tsan") {
listOf("x86_64", "arm64-v8a")
} else {
listOf("x86", "armeabi-v7a", "x86_64", "arm64-v8a")
},
)
}

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

if (nativeBackend) {
sourceSets.all { setRoot("$libDir/src/$name") }
}

if (sanitizer != null) {
sourceSets.getByName("androidTest") {
jniLibs.srcDir("$libDir/build/$sanitizer/jniLibs")
resources.srcDir("$libDir/build/$sanitizer/resources")
}
}

// we use the default NDK and CMake versions based on the AGP's version
// https://developer.android.com/studio/projects/install-ndk#apply-specific-version
externalNativeBuild {
cmake {
path("$libDir/CMakeLists.txt")
}
}

buildTypes {
getByName("debug") {
externalNativeBuild {
cmake {
arguments.add(0, "-DENABLE_TESTS=ON")
}
}
}
getByName("release") {
consumerProguardFiles("$libDir/proguard-rules.pro")
}
}

buildFeatures {
prefab = true
prefabPublishing = true
buildConfig = true
}

// creates
// lib.aar/prefab/modules/sentry-android/libs/<arch>/<lib>.so
// lib.aar/prefab/modules/sentry-android/include/sentry.h
prefab {
create("sentry-android") {}
create("sentry") {
headers = "../../include"
}
}

testOptions {
animationsDisabled = true
unitTests.apply {
isReturnDefaultValues = true
isIncludeAndroidResources = true
}
}

lint {
warningsAsErrors = true
checkDependencies = true
checkReleaseBuilds = true
disable.add("NewerVersionAvailable")
}

packaging {
jniLibs {
useLegacyPackaging = true
}
}
}

// legacy pre-prefab support
// creates lib.aar/jni/include/sentry.h alongside AGP's lib.aar/jni/<arch>/<lib>.so
tasks.withType<Zip>().configureEach {
if (name.startsWith("bundle") && name.endsWith("Aar")) {
from("../../include") {
into("jni/include")
}
}
}

dependencies {
"compileOnly"("org.jetbrains:annotations:23.0.0")

"testImplementation"("androidx.test.ext:junit:1.3.0")

"androidTestImplementation"("androidx.test:runner:1.7.0")
"androidTestImplementation"("androidx.test.ext:junit:1.3.0")
"androidTestImplementation"("androidx.test:rules:1.7.0")
}

/*
* Prefab doesn't support c++_static, so we need to change it to none.
* This should be fine, as we don't expose any conflicting symbols.
* Based on: https://github.com/bugsnag/bugsnag-android/blob/59460018551750dfcce4fd4e9f612eae7826559e/bugsnag-plugin-android-ndk/build.gradle.kts
*
* Copyright (c) 2012 Bugsnag

* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:

* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
afterEvaluate {
configurations.matching {
it.name.startsWith("releaseVariant") && it.name.endsWith("Publication")
}.configureEach {
outgoing.capability("$group:${project.name}:$version")
outgoing.capability("$group:sentry-ndk-backend:$version")
}

tasks.matching { it.name.startsWith("prefab") && it.name.endsWith("Package") }.configureEach {
doLast {
project.fileTree("build/intermediates/") {
include("**/abi.json")
include("**/prefab.json")
include("**/prefab_publication.json/*")
}.forEach { file ->
val contents = file.readText().replace("c++_static", "none")
file.writeText(
if (nativeBackend) {
contents.replace("\"name\": \"${project.name}\"", "\"name\": \"sentry-native-ndk\"")
.replace("\"packageName\": \"${project.name}\"", "\"packageName\": \"sentry-native-ndk\"")
} else {
contents
},
)
}
}
}
}
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
}
Loading
Loading