Skip to content
Draft
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
14 changes: 13 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ Key targets defined in `Directory.Build.targets`:
| `BuildAndroidSDK` | Builds Android SDK via Gradle |
| `BuildLinuxSDK` | Builds Linux SDK via CMake |
| `BuildWindowsSDK` | Builds Windows SDK via CMake (Crashpad) |
| `BuildCocoaSDK` | Downloads iOS/macOS SDKs from releases |
| `BuildCocoaSDK` | Builds iOS/macOS SDKs via Xcode |
| `UnityEditModeTest` | Runs edit-mode unit tests |
| `UnityPlayModeTest` | Runs play-mode tests |

Expand Down Expand Up @@ -320,6 +320,18 @@ modules/
└── sentry-cocoa/ # iOS/macOS (prebuilt XCFramework)
```

### Local Android NDK Development

When iterating on `modules/sentry-native/ndk` together with `modules/sentry-java`, publish the local NDK build to `~/.m2` so sentry-java picks it up instead of mavenCentral:

```bash
pwsh scripts/build-native-ndk-local.ps1 # publish only
pwsh scripts/build-native-ndk-local.ps1 -BuildJava # publish + rebuild :sentry-android-ndk
pwsh scripts/build-native-ndk-local.ps1 -PurgeCache -BuildJava # first switch from central, or after stale builds
```

Prerequisite: `mavenLocal()` must precede `mavenCentral()` in `modules/sentry-java/settings.gradle.kts` (`dependencyResolutionManagement` block). The script aborts otherwise.

### Key Source Files

**Android (`src/Sentry.Unity.Android/`):**
Expand Down
105 changes: 87 additions & 18 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -117,33 +117,57 @@
<Exec Command="pwsh &quot;$(RepoRoot)scripts/download-sentry-cli.ps1&quot;"></Exec>
</Target>

<!-- Build the Cocoa SDK: dotnet msbuild /t:BuildCocoaSDK src/Sentry.Unity -->
<!-- Builds the sentry-cocoa submodule from source and extracts iOS and macOS artifacts -->
<!--
Build the Cocoa SDK:
dotnet msbuild /t:BuildCocoaSDK src/Sentry.Unity
Force a clean rebuild (bypass the "xcframework already built" short-circuit and
wipe the sentry-cocoa XCFrameworkBuildPath/ cache before building):
dotnet msbuild /t:BuildCocoaSDK src/Sentry.Unity -p:RebuildCocoaSdk=true
Builds the sentry-cocoa submodule from source and replaces the iOS xcframework
and macOS dylib in package-dev/Plugins/.
-->
<Target Name="BuildCocoaSDK"
Condition="!$([MSBuild]::IsOSPlatform('Windows')) AND '$(MSBuildProjectName)' == 'Sentry.Unity'
And (!Exists('$(SentryiOSArtifactsDestination)') Or !Exists('$(SentrymacOSArtifactsDestination)Sentry.dylib'))"
And ('$(RebuildCocoaSdk)' == 'true' Or !Exists('$(SentryiOSArtifactsDestination)') Or !Exists('$(SentrymacOSArtifactsDestination)Sentry.dylib'))"
BeforeTargets="BeforeBuild">

<Error Condition="!Exists('$(SentryCocoaRoot)Sentry.xcodeproj')" Text="sentry-cocoa submodule not checked out at $(SentryCocoaRoot). Run: git submodule update --init modules/sentry-cocoa" />

<Message Importance="High" Text="Building Cocoa SDK from submodule." />

<Exec Command="pwsh &quot;$(RepoRoot)scripts/build-cocoa-sdk.ps1&quot; -CocoaRoot &quot;$(SentryCocoaRoot)&quot; -iOSDestination &quot;$(SentryiOSArtifactsDestination)&quot; -macOSDestination &quot;$(SentrymacOSArtifactsDestination)Sentry.dylib&quot;" />
<PropertyGroup>
<BuildCocoaSdkScriptArgs Condition="'$(RebuildCocoaSdk)' == 'true'">-Clean</BuildCocoaSdkScriptArgs>
</PropertyGroup>

<Exec Command="pwsh &quot;$(RepoRoot)scripts/build-cocoa-sdk.ps1&quot; -CocoaRoot &quot;$(SentryCocoaRoot)&quot; -iOSDestination &quot;$(SentryiOSArtifactsDestination)&quot; -macOSDestination &quot;$(SentrymacOSArtifactsDestination)Sentry.dylib&quot; $(BuildCocoaSdkScriptArgs)" />

<Error Condition="(!Exists('$(SentryiOSArtifactsDestination)'))" Text="Failed to set up the iOS SDK." />
<Error Condition="(!Exists('$(SentrymacOSArtifactsDestination)Sentry.dylib') Or !Exists('$(SentrymacOSArtifactsDestination)Sentry.dylib.dSYM'))" Text="Failed to set up the macOS SDK." />
</Target>

<!-- Build the Android SDK: dotnet msbuild /t:BuildAndroidSDK src/Sentry.Unity -->
<!--
Build the Android SDK:
dotnet msbuild /t:BuildAndroidSDK src/Sentry.Unity
Force a clean rebuild (bypass the "Sentry~/ already has 4 files" short-circuit).
Also republishes sentry-native-ndk to mavenLocal so sentry-java consumes the
locally-built NDK instead of the mavenCentral release:
dotnet msbuild /t:BuildAndroidSDK src/Sentry.Unity -p:RebuildAndroidSdk=true
-->
<Target Name="BuildAndroidSDK"
DependsOnTargets="DownloadCLI"
Condition="'$(MSBuildProjectName)' == 'Sentry.Unity' And (!Exists('$(SentryAndroidArtifactsDestination)') Or (Exists('$(SentryAndroidArtifactsDestination)') And $([System.IO.Directory]::GetFiles('$(SentryAndroidArtifactsDestination)').Length) != 4))"
Condition="'$(MSBuildProjectName)' == 'Sentry.Unity' And ('$(RebuildAndroidSdk)' == 'true' Or !Exists('$(SentryAndroidArtifactsDestination)') Or (Exists('$(SentryAndroidArtifactsDestination)') And $([System.IO.Directory]::GetFiles('$(SentryAndroidArtifactsDestination)').Length) != 4))"
BeforeTargets="BeforeBuild">
<Error Condition="!Exists('$(SentryAndroidRoot)')" Text="Couldn't find the Android root at $(SentryAndroidRoot)." />

<!-- Clean incomplete artifacts directory before building -->
<RemoveDir Directories="$(SentryAndroidArtifactsDestination)" Condition="Exists('$(SentryAndroidArtifactsDestination)')" ContinueOnError="true" />

<!-- When forcing a rebuild, also publish a fresh sentry-native-ndk to mavenLocal so
sentry-java's :sentry-android-ndk resolves it locally. The same local AAR is also
copied over the bundled sentry-native-ndk-release.aar below, keeping the Java/Kotlin
and native sides in sync. -->
<CallTarget Condition="'$(RebuildAndroidSdk)' == 'true'" Targets="PublishNativeNdkLocal" />

<Message Importance="High" Text="Building Sentry Android SDK." />

<!-- IgnoreExitCode: Gradle may report BUILD SUCCESSFUL yet the JVM exits with code -1 due to
Expand All @@ -169,41 +193,81 @@

<Copy SourceFiles="@(AndroidJarArtifacts)" DestinationFiles="$(SentryAndroidArtifactsDestination)sentry.jar" />

<Message Importance="High" Text="Reading the sentry-native-ndk version from the 'libs.versions.toml' file." />

<PropertyGroup>
<SentryNativeNdkLocalAar>$(SentryNativeRoot)ndk/lib/build/outputs/aar/sentry-native-ndk-release.aar</SentryNativeNdkLocalAar>
</PropertyGroup>

<!-- Prefer the locally-built sentry-native-ndk AAR when present
(see scripts/build-native-ndk-local.ps1). Falls back to the
released artifact below when no local build exists. -->
<Message Condition="Exists('$(SentryNativeNdkLocalAar)')" Importance="High" Text="Using locally-built sentry-native-ndk AAR from '$(SentryNativeNdkLocalAar)'." />
<Copy Condition="Exists('$(SentryNativeNdkLocalAar)')" SourceFiles="$(SentryNativeNdkLocalAar)" DestinationFiles="$(SentryAndroidArtifactsDestination)sentry-native-ndk-release.aar" />

<Message Condition="!Exists('$(SentryNativeNdkLocalAar)')" Importance="High" Text="Reading the sentry-native-ndk version from the 'libs.versions.toml' file." />

<PropertyGroup Condition="!Exists('$(SentryNativeNdkLocalAar)')">
<PropertiesContent>$([System.IO.File]::ReadAllText("$(RepoRoot)modules/sentry-java/gradle/libs.versions.toml"))</PropertiesContent>
<NativeVersion>$([System.Text.RegularExpressions.Regex]::Match($(PropertiesContent), 'sentry-native-ndk\s*=\s*\{[^}]*version\s*=\s*"([^"]+)"').Groups[1].Value)</NativeVersion>
</PropertyGroup>

<!-- Clean cache if version does not exist to get rid of old versions -->
<RemoveDir
Condition="!Exists('$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion).zip')"
Condition="!Exists('$(SentryNativeNdkLocalAar)') And !Exists('$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion).zip')"
Directories="$(SentryNativeNdkCache)" />

<!-- Create cache directory -->
<MakeDir Condition="!Exists('$(SentryNativeNdkCache)')" Directories="$(SentryNativeNdkCache)" />
<MakeDir Condition="!Exists('$(SentryNativeNdkLocalAar)') And !Exists('$(SentryNativeNdkCache)')" Directories="$(SentryNativeNdkCache)" />

<Message Importance="High" Text="Downloading sentry-native-ndk version '$(NativeVersion)'." />
<Message Condition="!Exists('$(SentryNativeNdkLocalAar)')" Importance="High" Text="Downloading sentry-native-ndk version '$(NativeVersion)'." />

<!-- Download prebuilt sentry-native-ndk -->
<Exec
Condition="!Exists('$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion).zip')"
Condition="!Exists('$(SentryNativeNdkLocalAar)') And !Exists('$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion).zip')"
Command="curl -L https://github.com/getsentry/sentry-native/releases/download/$(NativeVersion)/sentry-native-ndk-$(NativeVersion).zip -o &quot;$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion).zip&quot;" />

<Exec
Condition="!Exists('$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion)')"
Condition="!Exists('$(SentryNativeNdkLocalAar)') And !Exists('$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion)')"
Command="pwsh -Command &quot;Expand-Archive -Path '$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion).zip' -DestinationPath '$(SentryNativeNdkCache)' -Force&quot;" />

<Copy SourceFiles="$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion)/sentry-native-ndk-release.aar" DestinationFiles="$(SentryAndroidArtifactsDestination)sentry-native-ndk-release.aar" />
<Copy Condition="!Exists('$(SentryNativeNdkLocalAar)')" SourceFiles="$(SentryNativeNdkCache)sentry-native-ndk-$(NativeVersion)/sentry-native-ndk-release.aar" DestinationFiles="$(SentryAndroidArtifactsDestination)sentry-native-ndk-release.aar" />

<Error Condition="!Exists('$(SentryAndroidArtifactsDestination)')" Text="Failed to build the Android SDK." />
</Target>

<!-- Build the Sentry Native SDK for Windows: dotnet msbuild /t:BuildWindowsSDK src/Sentry.Unity -->
<!--
Build sentry-native-ndk and publish it to the local Maven repo (~/.m2)
so sentry-java's :sentry-android-ndk build consumes the local artifact
instead of mavenCentral. Requires mavenLocal() to be listed before
mavenCentral() in modules/sentry-java/settings.gradle.kts (the script
verifies this and aborts otherwise).

dotnet msbuild /t:PublishNativeNdkLocal src/Sentry.Unity

Purge stale Gradle caches before publishing (use the first time you
switch from mavenCentral or when the consumed artifact looks stale):
dotnet msbuild /t:PublishNativeNdkLocal src/Sentry.Unity -p:PurgeNdkCache=true
-->
<Target Name="PublishNativeNdkLocal" Condition="'$(MSBuildProjectName)' == 'Sentry.Unity'">
<Error Condition="!Exists('$(SentryNativeRoot)ndk')" Text="Couldn't find sentry-native NDK module at $(SentryNativeRoot)ndk. Run: git submodule update --init modules/sentry-native" />

<PropertyGroup>
<PublishNativeNdkScriptArgs Condition="'$(PurgeNdkCache)' == 'true'">-PurgeCache</PublishNativeNdkScriptArgs>
</PropertyGroup>

<Message Importance="High" Text="Publishing sentry-native-ndk to mavenLocal." />

<Exec Command="pwsh &quot;$(RepoRoot)scripts/build-native-ndk-local.ps1&quot; $(PublishNativeNdkScriptArgs)" />
</Target>

<!--
Build the Sentry Native SDK for Windows:
dotnet msbuild /t:BuildWindowsSDK src/Sentry.Unity
Force a clean rebuild (bypass the "sentry.dll already exists" short-circuit):
dotnet msbuild /t:BuildWindowsSDK src/Sentry.Unity -p:RebuildNativeSdk=true
-->
<Target Name="BuildWindowsSDK" Condition="'$(MSBuildProjectName)' == 'Sentry.Unity'
And $([MSBuild]::IsOsPlatform('Windows'))
And !Exists('$(SentryWindowsArtifactsDestination)sentry.dll')" BeforeTargets="BeforeBuild">
And ('$(RebuildNativeSdk)' == 'true' Or !Exists('$(SentryWindowsArtifactsDestination)sentry.dll'))" BeforeTargets="BeforeBuild">
<Error Condition="!Exists('$(SentryNativeRoot)')" Text="Couldn't find the Native root at $(SentryNativeRoot)."></Error>

<Message Importance="High" Text="Building artifacts of Sentry Native SDK for Windows." />
Expand All @@ -222,10 +286,15 @@
<Copy SourceFiles="@(NativeSdkArtifacts)" DestinationFiles="@(NativeSdkArtifacts->'$(SentryWindowsArtifactsDestination)%(Filename)%(Extension)')" />
</Target>

<!-- Build the Sentry Native SDK for Linux: dotnet msbuild /t:BuildLinuxSDK src/Sentry.Unity -->
<!--
Build the Sentry Native SDK for Linux:
dotnet msbuild /t:BuildLinuxSDK src/Sentry.Unity
Force a clean rebuild (bypass the "libsentry.so already exists" short-circuit):
dotnet msbuild /t:BuildLinuxSDK src/Sentry.Unity -p:RebuildNativeSdk=true
-->
<Target Name="BuildLinuxSDK" Condition="'$(MSBuildProjectName)' == 'Sentry.Unity'
And $([MSBuild]::IsOsPlatform('Linux'))
And !Exists('$(SentryLinuxArtifactsDestination)libsentry.so')" BeforeTargets="BeforeBuild">
And ('$(RebuildNativeSdk)' == 'true' Or !Exists('$(SentryLinuxArtifactsDestination)libsentry.so'))" BeforeTargets="BeforeBuild">
<Error Condition="!Exists('$(SentryNativeRoot)')" Text="Couldn't find the Native root at $(SentryNativeRoot)."></Error>

<Message Importance="High" Text="Building artifacts of Sentry Native SDK for Linux." />
Expand Down
9 changes: 8 additions & 1 deletion scripts/build-cocoa-sdk.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ param(
[string]$iOSDestination,

[Parameter(Mandatory = $true)]
[string]$macOSDestination
[string]$macOSDestination,

[switch]$Clean
)

Set-StrictMode -Version latest
Expand All @@ -25,6 +27,11 @@ $buildPath = Join-Path $CocoaRoot "XCFrameworkBuildPath"
$iOSXcframeworkPath = Join-Path $buildPath "Sentry-Dynamic-iOS.xcframework"
$macOSXcframeworkPath = Join-Path $buildPath "Sentry-Dynamic-macOS.xcframework"

if ($Clean -and (Test-Path $buildPath)) {
Write-Host "Clean build requested — removing $buildPath" -ForegroundColor Yellow
Remove-Item -Path $buildPath -Recurse -Force
}

Write-Host "Building Cocoa SDK from source..." -ForegroundColor Yellow

Push-Location $CocoaRoot
Expand Down
124 changes: 124 additions & 0 deletions scripts/build-native-ndk-local.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<#
.SYNOPSIS
Builds modules/sentry-native (NDK) and publishes the artifact to the local
Maven repo so modules/sentry-java consumes it instead of mavenCentral.

.DESCRIPTION
Runs :sentry-native-ndk:publishToMavenLocal in modules/sentry-native/ndk,
producing io.sentry:sentry-native-ndk:<version> at ~/.m2.

Requires mavenLocal() to be listed before mavenCentral() in
modules/sentry-java/settings.gradle.kts. The script verifies this and
aborts otherwise.

Because both repos publish the same version coordinate, Gradle's module
and transform caches can hold a previously-resolved mavenCentral copy.
The first time you switch to local (or when the module cache holds a
stale build), pass -PurgeCache to wipe sentry-native-ndk caches and
stop the Gradle daemon so the next build re-resolves from mavenLocal.

.PARAMETER PurgeCache
Delete sentry-native-ndk from the Gradle module cache and the related
transform directories, then stop the Gradle daemon. Use when switching
from mavenCentral resolution or when the consumed artifact looks stale.

.PARAMETER BuildJava
After publishing, run :sentry-android-ndk:assembleRelease in
modules/sentry-java to consume the freshly published artifact.

.EXAMPLE
pwsh scripts/build-native-ndk-local.ps1
# Publish ndk to ~/.m2 (assumes caches are already clean).

.EXAMPLE
pwsh scripts/build-native-ndk-local.ps1 -PurgeCache -BuildJava
# Wipe stale caches, publish, then rebuild sentry-android-ndk against
# the local artifact.
#>

param(
[switch] $PurgeCache,
[switch] $BuildJava
)

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest

$repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..')
$ndkDir = Join-Path $repoRoot 'modules/sentry-native/ndk'
$javaDir = Join-Path $repoRoot 'modules/sentry-java'
$javaSettings = Join-Path $javaDir 'settings.gradle.kts'

if (-not (Test-Path $ndkDir)) {
throw "sentry-native NDK module not found at $ndkDir. Did you check out the submodule?"
}
if (-not (Test-Path $javaSettings)) {
throw "sentry-java settings.gradle.kts not found at $javaSettings."
}

$settingsContent = Get-Content $javaSettings -Raw
$drmMatch = [regex]::Match($settingsContent, 'dependencyResolutionManagement\s*\{[^}]*repositories\s*\{(?<repos>[^}]*)\}')
if (-not $drmMatch.Success) {
throw "Could not locate dependencyResolutionManagement.repositories block in $javaSettings."
}
$reposBlock = $drmMatch.Groups['repos'].Value
$localIdx = $reposBlock.IndexOf('mavenLocal()')
$centralIdx = $reposBlock.IndexOf('mavenCentral()')
if ($localIdx -lt 0 -or $centralIdx -lt 0 -or $localIdx -gt $centralIdx) {
throw @"
mavenLocal() must appear before mavenCentral() in
$javaSettings (dependencyResolutionManagement block) so sentry-java
resolves the locally-published sentry-native-ndk artifact. Reorder the
repositories and re-run this script.
"@
}

if ($PurgeCache) {
Write-Host '==> Purging Gradle caches for sentry-native-ndk'
$gradleCaches = Join-Path $HOME '.gradle/caches'
$moduleCache = Join-Path $gradleCaches 'modules-2/files-2.1/io.sentry/sentry-native-ndk'
if (Test-Path $moduleCache) {
Remove-Item -Recurse -Force $moduleCache
Write-Host " removed $moduleCache"
}

if (Test-Path $gradleCaches) {
$transformRoots = Get-ChildItem -Path $gradleCaches -Recurse -Force -ErrorAction SilentlyContinue `
| Where-Object { $_.FullName -like '*sentry-native-ndk*' } `
| ForEach-Object {
$idx = $_.FullName.IndexOf('/transformed/')
if ($idx -lt 0) { $idx = $_.FullName.IndexOf([IO.Path]::DirectorySeparatorChar + 'transformed' + [IO.Path]::DirectorySeparatorChar) }
if ($idx -ge 0) { $_.FullName.Substring(0, $idx) } else { $null }
} `
| Where-Object { $_ } `
| Sort-Object -Unique
foreach ($dir in $transformRoots) {
if (Test-Path $dir) {
Remove-Item -Recurse -Force $dir
Write-Host " removed $dir"
}
}
}

Write-Host '==> Stopping Gradle daemon to clear in-memory transform registry'
Push-Location $ndkDir
try { & ./gradlew --stop | Out-Null } finally { Pop-Location }
}

Write-Host '==> Publishing sentry-native-ndk to mavenLocal'
Push-Location $ndkDir
try {
& ./gradlew :sentry-native-ndk:publishToMavenLocal
if ($LASTEXITCODE -ne 0) { throw "publishToMavenLocal failed (exit $LASTEXITCODE)" }
} finally { Pop-Location }

if ($BuildJava) {
Write-Host '==> Building :sentry-android-ndk:assembleRelease against mavenLocal'
Push-Location $javaDir
try {
& ./gradlew :sentry-android-ndk:assembleRelease
if ($LASTEXITCODE -ne 0) { throw "sentry-android-ndk assembleRelease failed (exit $LASTEXITCODE)" }
} finally { Pop-Location }
}

Write-Host '==> Done.'
Loading