From a625f2c2a4f2598600ccdce7c4381d52f3ad83ba Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 11 May 2026 15:31:50 +0200 Subject: [PATCH 1/5] native anr samples --- .../Assets/Scenes/3_AdditionalSamples.unity | 275 +++++++++++++++--- .../Assets/Scripts/AdditionalSampleButtons.cs | 25 ++ .../Scripts/NativeSupport/KotlinPlugin.kt | 5 + .../Scripts/NativeSupport/ObjectiveCPlugin.m | 7 + 4 files changed, 275 insertions(+), 37 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Scenes/3_AdditionalSamples.unity b/samples/unity-of-bugs/Assets/Scenes/3_AdditionalSamples.unity index 85086bcb6..6d7db894a 100644 --- a/samples/unity-of-bugs/Assets/Scenes/3_AdditionalSamples.unity +++ b/samples/unity-of-bugs/Assets/Scenes/3_AdditionalSamples.unity @@ -13,7 +13,7 @@ OcclusionCullingSettings: --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 - serializedVersion: 9 + serializedVersion: 10 m_Fog: 0 m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} m_FogMode: 3 @@ -42,8 +42,8 @@ RenderSettings: --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 - serializedVersion: 12 - m_GIWorkflowMode: 1 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 m_GISettings: serializedVersion: 2 m_BounceScale: 1 @@ -66,9 +66,6 @@ LightmapSettings: m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 m_TextureCompression: 1 - m_FinalGather: 0 - m_FinalGatherFiltering: 1 - m_FinalGatherRayCount: 256 m_ReflectionCompression: 2 m_MixedBakeMode: 2 m_BakeBackend: 0 @@ -103,7 +100,7 @@ NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 2 + serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -116,12 +113,145 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &191506112 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 191506113} + - component: {fileID: 191506116} + - component: {fileID: 191506115} + - component: {fileID: 191506114} + m_Layer: 5 + m_Name: Application-Not-Responding (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &191506113 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191506112} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1603559786} + m_Father: {fileID: 253040315} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 200, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &191506114 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191506112} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 191506115} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 253040317} + m_TargetAssemblyTypeName: AdditionalSampleButtons, Assembly-CSharp + m_MethodName: ApplicationNotRespondingNative + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 1 +--- !u!114 &191506115 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191506112} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &191506116 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191506112} + m_CullTransparentMesh: 0 --- !u!1 &253040314 GameObject: m_ObjectHideFlags: 0 @@ -156,9 +286,9 @@ RectTransform: - {fileID: 1326160953} - {fileID: 1983589452} - {fileID: 978406552} + - {fileID: 191506113} - {fileID: 2066465601} m_Father: {fileID: 1665572489} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -234,9 +364,17 @@ Camera: m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 @@ -270,13 +408,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 519420028} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &582054325 GameObject: @@ -318,13 +456,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 582054325} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &582054329 MonoBehaviour: @@ -367,7 +505,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638424148094703} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -405,7 +542,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 978406552} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -445,7 +581,7 @@ MonoBehaviour: m_HorizontalOverflow: 1 m_VerticalOverflow: 0 m_LineSpacing: 1 - m_Text: Application-Not-Responding + m_Text: App Freeze in C# --- !u!222 &765642148 CanvasRenderer: m_ObjectHideFlags: 0 @@ -487,7 +623,6 @@ RectTransform: m_Children: - {fileID: 834374186} m_Father: {fileID: 253040315} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -619,7 +754,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 802360430} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -699,7 +833,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1983589452} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -781,7 +914,6 @@ RectTransform: m_Children: - {fileID: 765642146} m_Father: {fileID: 253040315} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -915,7 +1047,6 @@ RectTransform: m_Children: - {fileID: 1857152829} m_Father: {fileID: 253040315} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1047,7 +1178,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2066465601} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1096,6 +1226,85 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1583546086} m_CullTransparentMesh: 0 +--- !u!1 &1603559785 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1603559786} + - component: {fileID: 1603559788} + - component: {fileID: 1603559787} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1603559786 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1603559785} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 191506113} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1603559787 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1603559785} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: App Freeze in Native +--- !u!222 &1603559788 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1603559785} + m_CullTransparentMesh: 0 --- !u!1 &1665572488 GameObject: m_ObjectHideFlags: 0 @@ -1131,7 +1340,6 @@ RectTransform: - {fileID: 253040315} - {fileID: 5216638424148094703} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1197,6 +1405,7 @@ Canvas: m_SortingBucketNormalizedSize: 0 m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 @@ -1231,7 +1440,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1326160953} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1313,7 +1521,6 @@ RectTransform: m_Children: - {fileID: 908640125} m_Father: {fileID: 253040315} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1447,7 +1654,6 @@ RectTransform: m_Children: - {fileID: 1583546087} m_Father: {fileID: 253040315} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1623,7 +1829,6 @@ RectTransform: - {fileID: 5216638425317031111} - {fileID: 735359052} m_Father: {fileID: 1665572489} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0} m_AnchorMax: {x: 0.5, y: 0} @@ -1703,7 +1908,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638425317031111} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1765,7 +1969,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638425882040144} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1837,7 +2040,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638424148094703} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1997,7 +2199,6 @@ RectTransform: m_Children: - {fileID: 5216638424536828319} m_Father: {fileID: 5216638424148094703} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -2112,7 +2313,6 @@ RectTransform: m_Children: - {fileID: 5216638426142319615} m_Father: {fileID: 5216638424148094703} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -2152,7 +2352,6 @@ RectTransform: m_Children: - {fileID: 5216638425035397373} m_Father: {fileID: 5216638424148094703} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -2301,7 +2500,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638424148094703} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -2381,7 +2579,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638425805414493} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -2453,7 +2650,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7152012675849339084} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -2483,7 +2679,6 @@ RectTransform: - {fileID: 7152012675148913643} - {fileID: 7152012676968016302} m_Father: {fileID: 1665572489} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -2561,7 +2756,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7152012675849339084} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -2586,3 +2780,10 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1665572489} + - {fileID: 582054328} + - {fileID: 519420032} diff --git a/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs b/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs index 0bfac1503..1f621e2d5 100644 --- a/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs +++ b/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs @@ -1,5 +1,8 @@ ο»Ώusing System; using System.Threading; +#if UNITY_IOS +using System.Runtime.InteropServices; +#endif using Sentry; using Sentry.Unity; using Unity.Burst; @@ -52,6 +55,28 @@ public void ApplicationNotResponding() Debug.Log("Thread.Sleep() finished."); } + public void ApplicationNotRespondingNative() + { +#if UNITY_ANDROID && !UNITY_EDITOR + Debug.Log("Stalling the main thread via Kotlin to trigger a native ANR event."); + using (var jo = new AndroidJavaObject("unity.of.bugs.KotlinPlugin")) + { + jo.CallStatic("applicationNotResponding"); + } +#elif UNITY_IOS && !UNITY_EDITOR + Debug.Log("Stalling the main thread via Objective-C to trigger a native ANR event."); + applicationNotResponding(); +#else + Debug.LogWarning("Native ANR sample requires running on Android or iOS."); +#endif + } + +#if UNITY_IOS && !UNITY_EDITOR + // ObjectiveCPlugin.m + [DllImport("__Internal")] + private static extern void applicationNotResponding(); +#endif + public void Assert() => UnityEngine.Assertions.Assert.IsTrue(false); diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt index 4f684b432..ada8ebd38 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt @@ -18,4 +18,9 @@ object KotlinPlugin { throw Exception("Kotlin πŸ› from a background thread.") } } + @JvmStatic fun applicationNotResponding() { + Log.i("test", "Stalling the main thread from Kotlin to trigger a native ANR.") + Thread.sleep(6 * 1000) // ANR detection currently defaults to 5 seconds + Log.i("test", "Kotlin main thread stall finished.") + } } diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m index e092f42eb..b0f36280e 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m @@ -16,4 +16,11 @@ void throwObjectiveC() #endif } +void applicationNotResponding() +{ + NSLog(@"Stalling the main thread from Objective-C to trigger a native ANR."); + [NSThread sleepForTimeInterval:6.0]; // ANR detection currently defaults to 5 seconds + NSLog(@"Objective-C main thread stall finished."); +} + NS_ASSUME_NONNULL_END From a33b5141f96c81cd9cf220b95cb8c9f8d82f8afd Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 13 May 2026 15:55:38 +0200 Subject: [PATCH 2/5] . --- samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs | 2 +- .../unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt | 2 +- .../Assets/Scripts/NativeSupport/ObjectiveCPlugin.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs b/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs index 1f621e2d5..80e6ab199 100644 --- a/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs +++ b/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs @@ -51,7 +51,7 @@ public void CaptureMessageWithContext() public void ApplicationNotResponding() { Debug.Log("Running Thread.Sleep() on the UI thread to trigger an ANR event."); - Thread.Sleep(6 * 1000); // ANR detection currently defaults to 5 seconds + Thread.Sleep(10 * 1000); // ANR detection currently defaults to 5 seconds Debug.Log("Thread.Sleep() finished."); } diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt index ada8ebd38..b975abfa7 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt @@ -20,7 +20,7 @@ object KotlinPlugin { } @JvmStatic fun applicationNotResponding() { Log.i("test", "Stalling the main thread from Kotlin to trigger a native ANR.") - Thread.sleep(6 * 1000) // ANR detection currently defaults to 5 seconds + Thread.sleep(10 * 1000) // ANR detection currently defaults to 5 seconds Log.i("test", "Kotlin main thread stall finished.") } } diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m index b0f36280e..deaa19b51 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m @@ -19,7 +19,7 @@ void throwObjectiveC() void applicationNotResponding() { NSLog(@"Stalling the main thread from Objective-C to trigger a native ANR."); - [NSThread sleepForTimeInterval:6.0]; // ANR detection currently defaults to 5 seconds + [NSThread sleepForTimeInterval:10.0]; // ANR detection currently defaults to 5 seconds NSLog(@"Objective-C main thread stall finished."); } From 268c3d2e3e74ba666ebabde1c7b0e3068c133070 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 13 May 2026 16:50:07 +0200 Subject: [PATCH 3/5] allow force rebuild for mobile native sdks --- CLAUDE.md | 14 +++- Directory.Build.targets | 105 +++++++++++++++++++----- scripts/build-cocoa-sdk.ps1 | 9 ++- scripts/build-native-ndk-local.ps1 | 124 +++++++++++++++++++++++++++++ 4 files changed, 232 insertions(+), 20 deletions(-) create mode 100644 scripts/build-native-ndk-local.ps1 diff --git a/CLAUDE.md b/CLAUDE.md index f68c3912a..36d356257 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 | @@ -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/`):** diff --git a/Directory.Build.targets b/Directory.Build.targets index 8f6613858..49fe224c5 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -117,33 +117,57 @@ - - + - + + -Clean + + + - + + + + + + + + + + $([System.IO.File]::ReadAllText("$(RepoRoot)modules/sentry-java/gradle/libs.versions.toml")) $([System.Text.RegularExpressions.Regex]::Match($(PropertiesContent), 'sentry-native-ndk\s*=\s*\{[^}]*version\s*=\s*"([^"]+)"').Groups[1].Value) - + - + - + - + + + + + + -PurgeCache + + + + + + + + + And ('$(RebuildNativeSdk)' == 'true' Or !Exists('$(SentryWindowsArtifactsDestination)sentry.dll'))" BeforeTargets="BeforeBuild"> @@ -222,10 +286,15 @@ - + + And ('$(RebuildNativeSdk)' == 'true' Or !Exists('$(SentryLinuxArtifactsDestination)libsentry.so'))" BeforeTargets="BeforeBuild"> diff --git a/scripts/build-cocoa-sdk.ps1 b/scripts/build-cocoa-sdk.ps1 index deb8444b7..ef745627f 100644 --- a/scripts/build-cocoa-sdk.ps1 +++ b/scripts/build-cocoa-sdk.ps1 @@ -8,7 +8,9 @@ param( [string]$iOSDestination, [Parameter(Mandatory = $true)] - [string]$macOSDestination + [string]$macOSDestination, + + [switch]$Clean ) Set-StrictMode -Version latest @@ -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 diff --git a/scripts/build-native-ndk-local.ps1 b/scripts/build-native-ndk-local.ps1 new file mode 100644 index 000000000..05773f490 --- /dev/null +++ b/scripts/build-native-ndk-local.ps1 @@ -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: 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*\{(?[^}]*)\}') +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.' From fc7fd2943bfbcc6e7de774f943923e143b1147ab Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 18 May 2026 15:58:32 +0200 Subject: [PATCH 4/5] split build targets into different files --- .github/workflows/sdk.yml | 2 +- CLAUDE.md | 59 ++++-- Directory.Build.targets | 330 +---------------------------- build/local-dev.targets | 88 ++++++++ build/native-sdks.targets | 214 +++++++++++++++++++ scripts/build-native-ndk-local.ps1 | 44 +--- 6 files changed, 357 insertions(+), 380 deletions(-) create mode 100644 build/local-dev.targets create mode 100644 build/native-sdks.targets diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index d96c7eb66..d31ee50b3 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -62,7 +62,7 @@ jobs: # hash of package/package.json for cache busting on release builds (version bump) path: | package-dev/Plugins - key: sdk=${{ env.TARGET }}-${{ hashFiles('submodules-status', 'package/package.json', 'Directory.Build.targets', 'sdk-static/**', 'scripts/build-cocoa-sdk.ps1') }} + key: sdk=${{ env.TARGET }}-${{ hashFiles('submodules-status', 'package/package.json', 'Directory.Build.targets', 'build/native-sdks.targets', 'sdk-static/**', 'scripts/build-cocoa-sdk.ps1') }} - name: Installing Linux Dependencies if: ${{ env.TARGET == 'Linux' && steps.cache.outputs.cache-hit != 'true' }} diff --git a/CLAUDE.md b/CLAUDE.md index 36d356257..5d61bfa62 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -148,17 +148,19 @@ Builds run in Docker containers using `ghcr.io/unityci/editor` images: ### MSBuild Targets -Key targets defined in `Directory.Build.targets`: - -| Target | Purpose | -| -------------------- | --------------------------------------- | -| `DownloadNativeSDKs` | Downloads prebuilt native SDKs from CI | -| `BuildAndroidSDK` | Builds Android SDK via Gradle | -| `BuildLinuxSDK` | Builds Linux SDK via CMake | -| `BuildWindowsSDK` | Builds Windows SDK via CMake (Crashpad) | -| `BuildCocoaSDK` | Builds iOS/macOS SDKs via Xcode | -| `UnityEditModeTest` | Runs edit-mode unit tests | -| `UnityPlayModeTest` | Runs play-mode tests | +Key targets defined across `build/` target files: + +| Target | Purpose | +| ----------------------- | ----------------------------------------------------------------------- | +| `BuildCocoaSDK` | Builds iOS + macOS SDKs via Xcode (`build/native-sdks.targets`) | +| `BuildAndroidSDK` | Builds Android SDK via Gradle (`build/native-sdks.targets`) | +| `BuildLinuxSDK` | Builds Linux SDK via CMake (`build/native-sdks.targets`) | +| `BuildWindowsSDK` | Builds Windows SDK via CMake + Crashpad (`build/native-sdks.targets`) | +| `EnsureSDK` | Auto-bootstrap wrapper; fires from `dotnet build` when artifacts missing | +| `PublishNativeNdkLocal` | Builds and publishes `sentry-native-ndk` to `~/.m2` | +| `DownloadNativeSDKs` | Downloads prebuilt native SDKs from CI (`build/local-dev.targets`) | +| `UnityEditModeTest` | Runs edit-mode unit tests | +| `UnityPlayModeTest` | Runs play-mode tests | ### Artifact Caching @@ -190,7 +192,7 @@ Key targets defined in `Directory.Build.targets`: ### Build System -Central configuration in `Directory.Build.targets` (900+ lines) and `Directory.Build.props`: +Core build configuration in `Directory.Build.props`. Build targets are split across `Directory.Build.targets` (CI-shared: `FindUnity`, Unity test/configure targets), `build/native-sdks.targets` (native SDK builders), and `build/local-dev.targets` (developer convenience targets): ```xml @@ -322,16 +324,37 @@ modules/ ### 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: +When iterating on `modules/sentry-native/ndk` together with `modules/sentry-java`, +`BuildAndroidSDK` automatically publishes the locally-built NDK to `~/.m2` before +running Gradle (as long as the `modules/sentry-native` submodule is checked out). +sentry-java's `dependencyResolutionManagement` block already lists `mavenLocal()`, +so Gradle resolves whatever version is declared in +`modules/sentry-java/gradle/libs.versions.toml` β€” falling through to Maven Central +when the version isn't found locally. + +**The local build must publish a version that is NOT available on Maven Central.** +Otherwise Gradle uses the released artifact on Central (listed before `mavenLocal()` +in the repositories block) and your local changes are silently ignored. + +To iterate on NDK code: + +1. Bump the version in the sentry-native NDK source to something unique β€” e.g. + add a `-dev`, `-SNAPSHOT`, or hash suffix that doesn't exist on Maven Central. +2. Bump the matching `sentry-native-ndk` version in + `modules/sentry-java/gradle/libs.versions.toml` to the same value. +3. Run `dotnet msbuild /t:BuildAndroidSDK src/Sentry.Unity`. The target rebuilds + the NDK, publishes it to `~/.m2`, then builds sentry-java against the local + artifact. + +To refresh only `~/.m2` without rebuilding the Android SDK: ```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 +dotnet msbuild /t:PublishNativeNdkLocal src/Sentry.Unity +# Purge cached Gradle artifacts (use when iterating on NDK source without +# bumping the version, so Gradle re-resolves rather than reusing its cache): +dotnet msbuild /t:PublishNativeNdkLocal src/Sentry.Unity -p:PurgeNdkCache=true ``` -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/`):** diff --git a/Directory.Build.targets b/Directory.Build.targets index 49fe224c5..ba2b8f88d 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -10,21 +10,11 @@ ../../artifacts/test/playmode/results.xml ../../artifacts/test/editmode/results.xml - $(RepoRoot)package-dev/Plugins/ - - $(RepoRoot)modules/sentry-cocoa/ - $(SentryArtifactsDestination)iOS/Sentry.xcframework~/ - $(SentryArtifactsDestination)macOS/Sentry/ - - $(RepoRoot)modules/sentry-native-ndk/ - $(RepoRoot)modules/sentry-java/ - $(SentryArtifactsDestination)Android/Sentry~/ - - $(RepoRoot)modules/sentry-native/ - $(SentryArtifactsDestination)Linux/Sentry/ - $(SentryArtifactsDestination)Windows/Sentry/ + + + @@ -87,229 +77,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Clean - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(SentryNativeRoot)ndk/lib/build/outputs/aar/sentry-native-ndk-release.aar - - - - - - - - - - $([System.IO.File]::ReadAllText("$(RepoRoot)modules/sentry-java/gradle/libs.versions.toml")) - $([System.Text.RegularExpressions.Regex]::Match($(PropertiesContent), 'sentry-native-ndk\s*=\s*\{[^}]*version\s*=\s*"([^"]+)"').Groups[1].Value) - - - - - - - - - - - - - - - - - - - - - - - - - - -PurgeCache - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -432,54 +157,5 @@ Log.LogError("Failed to resolve 'com.unity.template.3d-*' for TestRunner at: " + - - - - - - - - - - - - !l.Contains($"\"{PackageToRemove}\"")).ToArray(); -File.WriteAllLines(PackageManifestFile, lines); -]]> - - - - - - - - - - - - - - - - - - - diff --git a/build/local-dev.targets b/build/local-dev.targets new file mode 100644 index 000000000..0e1e59340 --- /dev/null +++ b/build/local-dev.targets @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/native-sdks.targets b/build/native-sdks.targets new file mode 100644 index 000000000..76810933b --- /dev/null +++ b/build/native-sdks.targets @@ -0,0 +1,214 @@ + + + $(RepoRoot)package-dev/Plugins/ + + + $(RepoRoot)modules/sentry-cocoa/ + $(SentryArtifactsDestination)iOS/Sentry.xcframework~/ + $(SentryArtifactsDestination)macOS/Sentry/ + + + $(RepoRoot)modules/sentry-java/ + $(SentryArtifactsDestination)Android/Sentry~/ + + + $(RepoRoot)modules/sentry-native/ + $(SentryArtifactsDestination)Linux/Sentry/ + $(SentryArtifactsDestination)Windows/Sentry/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -PurgeCache + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/build-native-ndk-local.ps1 b/scripts/build-native-ndk-local.ps1 index 05773f490..a7b6bef7f 100644 --- a/scripts/build-native-ndk-local.ps1 +++ b/scripts/build-native-ndk-local.ps1 @@ -1,26 +1,23 @@ <# .SYNOPSIS Builds modules/sentry-native (NDK) and publishes the artifact to the local - Maven repo so modules/sentry-java consumes it instead of mavenCentral. + Maven repo so sentry-java can resolve it via mavenLocal(). .DESCRIPTION Runs :sentry-native-ndk:publishToMavenLocal in modules/sentry-native/ndk, producing io.sentry:sentry-native-ndk: 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. + sentry-java's settings.gradle.kts already lists mavenLocal() in + dependencyResolutionManagement. For Gradle to pick the locally-published + artifact over mavenCentral, the local build's version must be unique + (i.e., not on Maven Central) β€” bump the version in both the NDK source + and modules/sentry-java/gradle/libs.versions.toml before iterating. .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. + transform directories, then stop the Gradle daemon. Use when Gradle is + holding a stale cached copy (e.g., when iterating on NDK source without + bumping the version). .PARAMETER BuildJava After publishing, run :sentry-android-ndk:assembleRelease in @@ -28,7 +25,7 @@ .EXAMPLE pwsh scripts/build-native-ndk-local.ps1 - # Publish ndk to ~/.m2 (assumes caches are already clean). + # Publish ndk to ~/.m2. .EXAMPLE pwsh scripts/build-native-ndk-local.ps1 -PurgeCache -BuildJava @@ -47,31 +44,10 @@ 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*\{(?[^}]*)\}') -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' From 708f8c9d23e177d67f6d62a44acb70f9ea12a8a3 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 18 May 2026 16:33:50 +0200 Subject: [PATCH 5/5] fix ndk pickup regression --- .github/workflows/sdk.yml | 3 ++- CLAUDE.md | 8 +++++--- build/native-sdks.targets | 29 +++++++++++++++++++---------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index d31ee50b3..0618105e5 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -26,7 +26,8 @@ jobs: shell: bash run: | if [[ "${TARGET}" == "Android" ]]; then - submodules="modules/sentry-java" + # Android needs sentry-native too β€” BuildAndroidSDK builds the NDK from source. + submodules="modules/sentry-java modules/sentry-native" elif [[ "${TARGET}" == "Cocoa" ]]; then submodules="modules/sentry-cocoa" else diff --git a/CLAUDE.md b/CLAUDE.md index 5d61bfa62..0a23817f9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -324,9 +324,11 @@ modules/ ### Local Android NDK Development -When iterating on `modules/sentry-native/ndk` together with `modules/sentry-java`, -`BuildAndroidSDK` automatically publishes the locally-built NDK to `~/.m2` before -running Gradle (as long as the `modules/sentry-native` submodule is checked out). +`BuildAndroidSDK` builds the NDK from source on every run (via `PublishNativeNdkLocal`) +and ships the resulting AAR alongside the sentry-java artifacts. Both +`modules/sentry-java` AND `modules/sentry-native` must be checked out β€” the target +aborts otherwise and prints the `git submodule update --init` command. + sentry-java's `dependencyResolutionManagement` block already lists `mavenLocal()`, so Gradle resolves whatever version is declared in `modules/sentry-java/gradle/libs.versions.toml` β€” falling through to Maven Central diff --git a/build/native-sdks.targets b/build/native-sdks.targets index 76810933b..765c0ff38 100644 --- a/build/native-sdks.targets +++ b/build/native-sdks.targets @@ -160,14 +160,16 @@ Build the Android SDK: dotnet msbuild /t:BuildAndroidSDK src/Sentry.Unity - Always wipes prior outputs and rebuilds. When modules/sentry-native/ndk is - checked out, PublishNativeNdkLocal runs first so a locally-built NDK is - available in ~/.m2 before Gradle resolves. Gradle falls through to mavenLocal - only when the version declared in gradle/libs.versions.toml isn't on Maven - Central β€” see PublishNativeNdkLocal above for the iteration workflow. + Always wipes prior outputs and rebuilds the NDK from source via + PublishNativeNdkLocal, then runs Gradle to build sentry-java. Both + modules/sentry-java and modules/sentry-native must be checked out. + For Gradle to resolve the locally-built NDK over Maven Central, the + version in gradle/libs.versions.toml must not exist on Maven Central + (see PublishNativeNdkLocal above for the iteration workflow). --> + @@ -177,11 +179,9 @@ - - - + + + + $(SentryNativeRoot)ndk/lib/build/outputs/aar/sentry-native-ndk-release.aar + + + +