From cd05af1d4a422d7d719aa50db9396feb5b2855da Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 19 Dec 2024 13:55:40 +0100 Subject: [PATCH 1/5] feat: add an option to configure native backend on windows & linux --- flutter/linux/CMakeLists.txt | 2 + flutter/sentry-native/sentry-native.cmake | 29 +- .../sentry_native/sentry_native_test_ffi.dart | 580 ++++++++++-------- flutter/windows/CMakeLists.txt | 8 + 4 files changed, 346 insertions(+), 273 deletions(-) diff --git a/flutter/linux/CMakeLists.txt b/flutter/linux/CMakeLists.txt index 2c886a675b..a2a17eaa1b 100644 --- a/flutter/linux/CMakeLists.txt +++ b/flutter/linux/CMakeLists.txt @@ -3,6 +3,8 @@ # the plugin to fail to compile for some customers of the plugin. cmake_minimum_required(VERSION 3.10) +set(SENTRY_BACKEND "crashpad" CACHE STRING "The sentry backend responsible for reporting crashes" FORCE) + include("${CMAKE_CURRENT_SOURCE_DIR}/../sentry-native/sentry-native.cmake") # Even though sentry_flutter doesn't actually provide a useful plugin, we need to accommodate the Flutter tooling. diff --git a/flutter/sentry-native/sentry-native.cmake b/flutter/sentry-native/sentry-native.cmake index 8e16cb9900..ce61d083e3 100644 --- a/flutter/sentry-native/sentry-native.cmake +++ b/flutter/sentry-native/sentry-native.cmake @@ -2,9 +2,16 @@ load_cache("${CMAKE_CURRENT_LIST_DIR}" READ_WITH_PREFIX SENTRY_NATIVE_ repo vers message(STATUS "Fetching Sentry native version: ${SENTRY_NATIVE_version} from ${SENTRY_NATIVE_repo}") set(SENTRY_SDK_NAME "sentry.native.flutter" CACHE STRING "The SDK name to report when sending events." FORCE) -set(SENTRY_BACKEND "crashpad" CACHE STRING "The sentry backend responsible for reporting crashes" FORCE) set(SENTRY_BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" FORCE) +# Note: the backend is also set in linux/CMakeLists.txt and windows/CMakeLists.txt. This overwrites those if user sets an env var. +if("$ENV{SENTRY_NATIVE_BACKEND}" STREQUAL "") + # Until sentry-dart v9, we disable native backend by default. + set(SENTRY_BACKEND "none" CACHE STRING "The sentry backend responsible for reporting crashes" FORCE) +else() + set(SENTRY_BACKEND $ENV{SENTRY_NATIVE_BACKEND} CACHE STRING "The sentry backend responsible for reporting crashes" FORCE) +endif() + include(FetchContent) FetchContent_Declare( sentry-native @@ -17,15 +24,19 @@ FetchContent_MakeAvailable(sentry-native) # List of absolute paths to libraries that should be bundled with the plugin. # This list could contain prebuilt libraries, or libraries created by an external build triggered from this build file. -if(WIN32) - set(sentry_flutter_bundled_libraries - $ - $ - PARENT_SCOPE) +if(SENTRY_BACKEND STREQUAL "crashpad") + if(WIN32) + set(sentry_flutter_bundled_libraries + $ + $ + PARENT_SCOPE) + else() + set(sentry_flutter_bundled_libraries + $ + PARENT_SCOPE) + endif() else() - set(sentry_flutter_bundled_libraries - $ - PARENT_SCOPE) + set(sentry_flutter_bundled_libraries "" PARENT_SCOPE) endif() # `*_plugin` is the name of the plugin library as expected by flutter. diff --git a/flutter/test/sentry_native/sentry_native_test_ffi.dart b/flutter/test/sentry_native/sentry_native_test_ffi.dart index 9a57e07325..a0b5a3aa2b 100644 --- a/flutter/test/sentry_native/sentry_native_test_ffi.dart +++ b/flutter/test/sentry_native/sentry_native_test_ffi.dart @@ -12,254 +12,305 @@ import 'package:sentry_flutter/src/native/factory.dart'; import '../mocks.dart'; import '../mocks.mocks.dart'; -late final String repoRootDir; -late final List expectedDistFiles; +enum NativeBackend { default_, crashpad, breakpad, inproc, none } + +extension on NativeBackend { + // TODO change default to crashpad in v9 + String get actualValue => this == NativeBackend.default_ ? 'none' : name; +} // NOTE: Don't run/debug this main(), it likely won't work. // You can use main() in `sentry_native_test.dart`. void main() { - repoRootDir = Directory.current.path.endsWith('/test') + final repoRootDir = Directory.current.path.endsWith('/test') ? Directory.current.parent.path : Directory.current.path; - expectedDistFiles = platform.instance.isWindows - ? ['sentry.dll', 'crashpad_handler.exe', 'crashpad_wer.dll'] - : ['libsentry.so', 'crashpad_handler']; - - setUpAll(() async { - Directory.current = - await _buildSentryNative('$repoRootDir/temp/native-test'); - SentryNative.dynamicLibraryDirectory = '${Directory.current.path}/'; - SentryNative.crashpadPath = - '${Directory.current.path}/${expectedDistFiles.firstWhere((f) => f.contains('crashpad_handler'))}'; - }); - - late SentryNative sut; - late SentryFlutterOptions options; - - setUp(() { - options = SentryFlutterOptions(dsn: fakeDsn) - // ignore: invalid_use_of_internal_member - ..automatedTestMode = true - ..debug = true; - sut = createBinding(options) as SentryNative; - }); - - test('expected output files', () { - for (var name in expectedDistFiles) { - if (!File(name).existsSync()) { - fail('Native distribution file $name does not exist'); - } - } - }); - - test('options', () { - options - ..debug = true - ..environment = 'foo' - ..release = 'foo@bar+1' - ..enableAutoSessionTracking = true - ..dist = 'distfoo' - ..maxBreadcrumbs = 42; - - final cOptions = sut.createOptions(options); - try { - expect( - SentryNative.native - .options_get_dsn(cOptions) - .cast() - .toDartString(), - fakeDsn); - expect( - SentryNative.native - .options_get_environment(cOptions) - .cast() - .toDartString(), - 'foo'); - expect( - SentryNative.native - .options_get_release(cOptions) - .cast() - .toDartString(), - 'foo@bar+1'); - expect( - SentryNative.native.options_get_auto_session_tracking(cOptions), 1); - expect(SentryNative.native.options_get_max_breadcrumbs(cOptions), 42); - } finally { - SentryNative.native.options_free(cOptions); - } - }); - - test('SDK version', () { - expect(_configuredSentryNativeVersion.length, greaterThanOrEqualTo(5)); - expect(SentryNative.native.sdk_version().cast().toDartString(), - _configuredSentryNativeVersion); - }); - - test('SDK name', () { - expect(SentryNative.native.sdk_name().cast().toDartString(), - 'sentry.native.flutter'); - }); - - test('init', () async { - addTearDown(sut.close); - await sut.init(MockHub()); - }); - - test('app start', () { - expect(sut.fetchNativeAppStart(), null); - }); - - test('frames tracking', () { - sut.beginNativeFrames(); - expect(sut.endNativeFrames(SentryId.newId()), null); - }); - - test('hang tracking', () { - sut.pauseAppHangTracking(); - sut.resumeAppHangTracking(); - }); - - test('setUser', () async { - final user = SentryUser( - id: "fixture-id", - username: 'username', - email: 'mail@domain.tld', - ipAddress: '1.2.3.4', - name: 'User Name', - data: { - 'str': 'foo-bar', - 'double': 1.0, - 'int': 1, - 'int64': 0x7FFFFFFF + 1, - 'boo': true, - 'inner-map': {'str': 'inner'}, - 'unsupported': Object() - }, - ); - - await sut.setUser(user); - }); - - test('addBreadcrumb', () async { - final breadcrumb = Breadcrumb( - type: 'type', - message: 'message', - category: 'category', - ); - await sut.addBreadcrumb(breadcrumb); - }); - - test('clearBreadcrumbs', () async { - await sut.clearBreadcrumbs(); - }); - - test('displayRefreshRate', () async { - expect(sut.displayRefreshRate(), isNull); - }); - - test('setContexts', () async { - final value = {'object': Object()}; - await sut.setContexts('fixture-key', value); - }); - - test('removeContexts', () async { - await sut.removeContexts('fixture-key'); - }); - - test('setExtra', () async { - final value = {'object': Object()}; - await sut.setExtra('fixture-key', value); - }); - - test('removeExtra', () async { - await sut.removeExtra('fixture-key'); - }); - - test('setTag', () async { - await sut.setTag('fixture-key', 'fixture-value'); - }); - - test('removeTag', () async { - await sut.removeTag('fixture-key'); - }); - - test('startProfiler', () { - expect(() => sut.startProfiler(SentryId.newId()), throwsUnsupportedError); - }); - - test('discardProfiler', () async { - expect(() => sut.discardProfiler(SentryId.newId()), throwsUnsupportedError); - }); - - test('collectProfile', () async { - final traceId = SentryId.newId(); - const startTime = 42; - const endTime = 50; - expect(() => sut.collectProfile(traceId, startTime, endTime), - throwsUnsupportedError); - }); - - test('captureEnvelope', () async { - final data = Uint8List.fromList([1, 2, 3]); - expect(() => sut.captureEnvelope(data, false), throwsUnsupportedError); - }); - - test('loadContexts', () async { - expect(await sut.loadContexts(), isNull); - }); - - test('loadDebugImages', () async { - final list = await sut.loadDebugImages(SentryStackTrace(frames: [])); - expect(list, isNotEmpty); - expect(list![0].type, platform.instance.isWindows ? 'pe' : 'elf'); - expect(list[0].debugId!.length, greaterThan(30)); - expect( - list[0].debugFile, platform.instance.isWindows ? isNotEmpty : isNull); - expect(list[0].imageSize, greaterThan(0)); - expect(list[0].imageAddr, startsWith('0x')); - expect(list[0].imageAddr?.length, greaterThan(2)); - expect(list[0].codeId!.length, greaterThan(10)); - expect(list[0].codeFile, isNotEmpty); - expect( - File(list[0].codeFile!), - (File file) => file.existsSync(), - ); - }); + // assert(NativeBackend.values.length == 4); + for (final backend in NativeBackend.values) { + group(backend.name, () { + late final NativeTestHelper helper; + setUpAll(() async { + late final List expectedDistFiles; + if (backend.actualValue == 'crashpad') { + expectedDistFiles = platform.instance.isWindows + ? ['sentry.dll', 'crashpad_handler.exe', 'crashpad_wer.dll'] + : ['libsentry.so', 'crashpad_handler']; + } else { + expectedDistFiles = + platform.instance.isWindows ? ['sentry.dll'] : ['libsentry.so']; + } + + helper = NativeTestHelper( + repoRootDir, + backend, + expectedDistFiles, + '$repoRootDir/temp/native-test-${backend.name}', + ); + + Directory.current = await helper._buildSentryNative(); + SentryNative.dynamicLibraryDirectory = '${Directory.current.path}/'; + if (backend.actualValue == 'crashpad') { + SentryNative.crashpadPath = + '${Directory.current.path}/${expectedDistFiles.firstWhere((f) => f.contains('crashpad_handler'))}'; + } + }); + + late SentryNative sut; + late SentryFlutterOptions options; + + setUp(() { + options = SentryFlutterOptions(dsn: fakeDsn) + // ignore: invalid_use_of_internal_member + ..automatedTestMode = true + ..debug = true; + sut = createBinding(options) as SentryNative; + }); + + test('native CMake was configured with configured backend', () async { + final cmakeCacheTxt = + await File('${helper.cmakeBuildDir}/CMakeCache.txt').readAsLines(); + expect(cmakeCacheTxt, + contains('SENTRY_BACKEND:STRING=${backend.actualValue}')); + }); + + test('expected output files', () { + for (var name in helper.expectedDistFiles) { + if (!File(name).existsSync()) { + fail('Native distribution file $name does not exist'); + } + } + }); + + test('options', () { + options + ..debug = true + ..environment = 'foo' + ..release = 'foo@bar+1' + ..enableAutoSessionTracking = true + ..dist = 'distfoo' + ..maxBreadcrumbs = 42; + + final cOptions = sut.createOptions(options); + try { + expect( + SentryNative.native + .options_get_dsn(cOptions) + .cast() + .toDartString(), + fakeDsn); + expect( + SentryNative.native + .options_get_environment(cOptions) + .cast() + .toDartString(), + 'foo'); + expect( + SentryNative.native + .options_get_release(cOptions) + .cast() + .toDartString(), + 'foo@bar+1'); + expect( + SentryNative.native.options_get_auto_session_tracking(cOptions), + 1); + expect(SentryNative.native.options_get_max_breadcrumbs(cOptions), 42); + } finally { + SentryNative.native.options_free(cOptions); + } + }); + + test('SDK version', () { + expect(helper.configuredSentryNativeVersion.length, + greaterThanOrEqualTo(5)); + expect(SentryNative.native.sdk_version().cast().toDartString(), + helper.configuredSentryNativeVersion); + }); + + test('SDK name', () { + expect(SentryNative.native.sdk_name().cast().toDartString(), + 'sentry.native.flutter'); + }); + + test('init', () async { + addTearDown(sut.close); + await sut.init(MockHub()); + }); + + test('app start', () { + expect(sut.fetchNativeAppStart(), null); + }); + + test('frames tracking', () { + sut.beginNativeFrames(); + expect(sut.endNativeFrames(SentryId.newId()), null); + }); + + test('hang tracking', () { + sut.pauseAppHangTracking(); + sut.resumeAppHangTracking(); + }); + + test('setUser', () async { + final user = SentryUser( + id: "fixture-id", + username: 'username', + email: 'mail@domain.tld', + ipAddress: '1.2.3.4', + name: 'User Name', + data: { + 'str': 'foo-bar', + 'double': 1.0, + 'int': 1, + 'int64': 0x7FFFFFFF + 1, + 'boo': true, + 'inner-map': {'str': 'inner'}, + 'unsupported': Object() + }, + ); + + await sut.setUser(user); + }); + + test('addBreadcrumb', () async { + final breadcrumb = Breadcrumb( + type: 'type', + message: 'message', + category: 'category', + ); + await sut.addBreadcrumb(breadcrumb); + }); + + test('clearBreadcrumbs', () async { + await sut.clearBreadcrumbs(); + }); + + test('displayRefreshRate', () async { + expect(sut.displayRefreshRate(), isNull); + }); + + test('setContexts', () async { + final value = {'object': Object()}; + await sut.setContexts('fixture-key', value); + }); + + test('removeContexts', () async { + await sut.removeContexts('fixture-key'); + }); + + test('setExtra', () async { + final value = {'object': Object()}; + await sut.setExtra('fixture-key', value); + }); + + test('removeExtra', () async { + await sut.removeExtra('fixture-key'); + }); + + test('setTag', () async { + await sut.setTag('fixture-key', 'fixture-value'); + }); + + test('removeTag', () async { + await sut.removeTag('fixture-key'); + }); + + test('startProfiler', () { + expect( + () => sut.startProfiler(SentryId.newId()), throwsUnsupportedError); + }); + + test('discardProfiler', () async { + expect(() => sut.discardProfiler(SentryId.newId()), + throwsUnsupportedError); + }); + + test('collectProfile', () async { + final traceId = SentryId.newId(); + const startTime = 42; + const endTime = 50; + expect(() => sut.collectProfile(traceId, startTime, endTime), + throwsUnsupportedError); + }); + + test('captureEnvelope', () async { + final data = Uint8List.fromList([1, 2, 3]); + expect(() => sut.captureEnvelope(data, false), throwsUnsupportedError); + }); + + test('loadContexts', () async { + expect(await sut.loadContexts(), isNull); + }); + + test('loadDebugImages', () async { + final list = await sut.loadDebugImages(SentryStackTrace(frames: [])); + expect(list, isNotEmpty); + expect(list![0].type, platform.instance.isWindows ? 'pe' : 'elf'); + expect(list[0].debugId!.length, greaterThan(30)); + expect(list[0].debugFile, + platform.instance.isWindows ? isNotEmpty : isNull); + expect(list[0].imageSize, greaterThan(0)); + expect(list[0].imageAddr, startsWith('0x')); + expect(list[0].imageAddr?.length, greaterThan(2)); + expect(list[0].codeId!.length, greaterThan(10)); + expect(list[0].codeFile, isNotEmpty); + expect( + File(list[0].codeFile!), + (File file) => file.existsSync(), + ); + }); + }); + } } -/// Runs [command] with command's stdout and stderr being forwrarded to -/// test runner's respective streams. It buffers stdout and returns it. -/// -/// Returns [_CommandResult] with exitCode and stdout as a single sting -Future _exec(String executable, List arguments) async { - final process = await Process.start(executable, arguments); - - // forward standard streams - unawaited(stderr.addStream(process.stderr)); - unawaited(stdout.addStream(process.stdout)); - - int exitCode = await process.exitCode; - if (exitCode != 0) { - throw Exception( - "$executable ${arguments.join(' ')} failed with exit code $exitCode"); +class NativeTestHelper { + final String repoRootDir; + final NativeBackend nativeBackend; + final List expectedDistFiles; + final String nativeTestRoot; + late final cmakeBuildDir = '$nativeTestRoot/build'; + late final cmakeConfDir = '$nativeTestRoot/conf'; + late final buildOutputDir = '$nativeTestRoot/dist/'; + + NativeTestHelper(this.repoRootDir, this.nativeBackend, this.expectedDistFiles, + this.nativeTestRoot); + + /// Runs [command] with command's stdout and stderr being forwrarded to + /// test runner's respective streams. It buffers stdout and returns it. + /// + /// Returns [_CommandResult] with exitCode and stdout as a single sting + Future _exec(String executable, List arguments) async { + final env = Map.of(Platform.environment); + if (nativeBackend != NativeBackend.default_) { + env['SENTRY_NATIVE_BACKEND'] = nativeBackend.name; + } else { + env.remove('SENTRY_NATIVE_BACKEND'); + } + + final process = await Process.start(executable, arguments, + environment: env, includeParentEnvironment: false); + + // forward standard streams + unawaited(stderr.addStream(process.stderr)); + unawaited(stdout.addStream(process.stdout)); + + int exitCode = await process.exitCode; + if (exitCode != 0) { + throw Exception( + "$executable ${arguments.join(' ')} failed with exit code $exitCode"); + } } -} -/// Compile sentry-native using CMake, as if it was part of a Flutter app. -/// Returns the directory containing built libraries -Future _buildSentryNative(String nativeTestRoot) async { - final cmakeBuildDir = '$nativeTestRoot/build'; - final cmakeConfDir = '$nativeTestRoot/conf'; - final buildOutputDir = '$nativeTestRoot/dist/'; - - if (!_builtVersionIsExpected(cmakeBuildDir, buildOutputDir)) { - Directory(cmakeConfDir).createSync(recursive: true); - Directory(buildOutputDir).createSync(recursive: true); - File('$cmakeConfDir/main.c').writeAsStringSync(''' + /// Compile sentry-native using CMake, as if it was part of a Flutter app. + /// Returns the directory containing built libraries + Future _buildSentryNative() async { + if (!_builtVersionIsExpected()) { + Directory(cmakeConfDir).createSync(recursive: true); + Directory(buildOutputDir).createSync(recursive: true); + File('$cmakeConfDir/main.c').writeAsStringSync(''' int main(int argc, char *argv[]) { return 0; } '''); - File('$cmakeConfDir/CMakeLists.txt').writeAsStringSync(''' + File('$cmakeConfDir/CMakeLists.txt').writeAsStringSync(''' cmake_minimum_required(VERSION 3.14) project(sentry-native-flutter-test) add_subdirectory(../../../${platform.instance.operatingSystem} plugin) @@ -272,39 +323,40 @@ list(APPEND PLUGIN_BUNDLED_LIBRARIES \${sentry_flutter_bundled_libraries}) install(FILES "\${PLUGIN_BUNDLED_LIBRARIES}" DESTINATION "${buildOutputDir.replaceAll('\\', '/')}" COMPONENT Runtime) set(CMAKE_INSTALL_PREFIX "${buildOutputDir.replaceAll('\\', '/')}") '''); - await _exec('cmake', ['-B', cmakeBuildDir, cmakeConfDir]); - await _exec('cmake', - ['--build', cmakeBuildDir, '--config', 'Release', '--parallel']); - await _exec('cmake', [ - '--install', - cmakeBuildDir, - '--config', - 'Release', - ]); - if (platform.instance.isLinux) { - await _exec('chmod', ['+x', '$buildOutputDir/crashpad_handler']); + await _exec('cmake', ['-B', cmakeBuildDir, cmakeConfDir]); + await _exec('cmake', + ['--build', cmakeBuildDir, '--config', 'Release', '--parallel']); + await _exec('cmake', [ + '--install', + cmakeBuildDir, + '--config', + 'Release', + ]); + if (platform.instance.isLinux) { + await _exec('chmod', ['+x', '$buildOutputDir/crashpad_handler']); + } } + return buildOutputDir; } - return buildOutputDir; -} -bool _builtVersionIsExpected(String cmakeBuildDir, String buildOutputDir) { - final buildCmake = File( - '$cmakeBuildDir/_deps/sentry-native-build/sentry-config-version.cmake'); - if (!buildCmake.existsSync()) return false; + bool _builtVersionIsExpected() { + final buildCmake = File( + '$cmakeBuildDir/_deps/sentry-native-build/sentry-config-version.cmake'); + if (!buildCmake.existsSync()) return false; + + if (!buildCmake + .readAsStringSync() + .contains('set(PACKAGE_VERSION "$configuredSentryNativeVersion")')) { + return false; + } - if (!buildCmake - .readAsStringSync() - .contains('set(PACKAGE_VERSION "$_configuredSentryNativeVersion")')) { - return false; + return !expectedDistFiles + .any((name) => !File('$buildOutputDir/$name').existsSync()); } - return !expectedDistFiles - .any((name) => !File('$buildOutputDir/$name').existsSync()); + late final configuredSentryNativeVersion = + File('$repoRootDir/sentry-native/CMakeCache.txt') + .readAsLinesSync() + .map((line) => line.startsWith('version=') ? line.substring(8) : null) + .firstWhere((line) => line != null)!; } - -final _configuredSentryNativeVersion = - File('$repoRootDir/sentry-native/CMakeCache.txt') - .readAsLinesSync() - .map((line) => line.startsWith('version=') ? line.substring(8) : null) - .firstWhere((line) => line != null)!; diff --git a/flutter/windows/CMakeLists.txt b/flutter/windows/CMakeLists.txt index 7926cf0571..9b1fcbcc5e 100644 --- a/flutter/windows/CMakeLists.txt +++ b/flutter/windows/CMakeLists.txt @@ -4,6 +4,14 @@ # customers of the plugin. cmake_minimum_required(VERSION 3.14) +if(FLUTTER_TARGET_PLATFORM EQUAL "windows-arm64") + set(native_backend "breakpad") +else() + set(native_backend "crashpad") +endif() + +set(SENTRY_BACKEND ${native_backend} CACHE STRING "The sentry backend responsible for reporting crashes" FORCE) + include("${CMAKE_CURRENT_SOURCE_DIR}/../sentry-native/sentry-native.cmake") # Even though sentry_flutter doesn't actually provide a useful plugin, we need to accommodate the Flutter tooling. From 8058a972b63a92159c6b687a153665bcf61e049a Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 19 Dec 2024 14:54:42 +0100 Subject: [PATCH 2/5] minor fix --- .../test/sentry_native/sentry_native_test_ffi.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/flutter/test/sentry_native/sentry_native_test_ffi.dart b/flutter/test/sentry_native/sentry_native_test_ffi.dart index a0b5a3aa2b..0cdd67ca5f 100644 --- a/flutter/test/sentry_native/sentry_native_test_ffi.dart +++ b/flutter/test/sentry_native/sentry_native_test_ffi.dart @@ -16,7 +16,8 @@ enum NativeBackend { default_, crashpad, breakpad, inproc, none } extension on NativeBackend { // TODO change default to crashpad in v9 - String get actualValue => this == NativeBackend.default_ ? 'none' : name; + NativeBackend get actualValue => + this == NativeBackend.default_ ? NativeBackend.none : this; } // NOTE: Don't run/debug this main(), it likely won't work. @@ -32,7 +33,7 @@ void main() { late final NativeTestHelper helper; setUpAll(() async { late final List expectedDistFiles; - if (backend.actualValue == 'crashpad') { + if (backend.actualValue == NativeBackend.crashpad) { expectedDistFiles = platform.instance.isWindows ? ['sentry.dll', 'crashpad_handler.exe', 'crashpad_wer.dll'] : ['libsentry.so', 'crashpad_handler']; @@ -50,7 +51,7 @@ void main() { Directory.current = await helper._buildSentryNative(); SentryNative.dynamicLibraryDirectory = '${Directory.current.path}/'; - if (backend.actualValue == 'crashpad') { + if (backend.actualValue == NativeBackend.crashpad) { SentryNative.crashpadPath = '${Directory.current.path}/${expectedDistFiles.firstWhere((f) => f.contains('crashpad_handler'))}'; } @@ -71,7 +72,7 @@ void main() { final cmakeCacheTxt = await File('${helper.cmakeBuildDir}/CMakeCache.txt').readAsLines(); expect(cmakeCacheTxt, - contains('SENTRY_BACKEND:STRING=${backend.actualValue}')); + contains('SENTRY_BACKEND:STRING=${backend.actualValue.name}')); }); test('expected output files', () { @@ -332,7 +333,8 @@ set(CMAKE_INSTALL_PREFIX "${buildOutputDir.replaceAll('\\', '/')}") '--config', 'Release', ]); - if (platform.instance.isLinux) { + if (platform.instance.isLinux && + nativeBackend.actualValue == NativeBackend.crashpad) { await _exec('chmod', ['+x', '$buildOutputDir/crashpad_handler']); } } From a42d4a23c51c5b4f78f5d0eb4918070fafcddf98 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 19 Dec 2024 17:21:44 +0100 Subject: [PATCH 3/5] chore: changelog --- CHANGELOG.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a6d64a8bb..5fabd19e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,27 @@ # Changelog +## Unreleased + +### Changes + +- Windows & Linux native crash handlers: add `SENTRY_NATIVE_BACKEND` env var with default setting of `none`. ([#2522](https://github.com/getsentry/sentry-dart/pull/2522)) + Native crash reporting support with `sentry-native`'s `crashpad` was added in v8.11.0 and has caused build-time issues + for some users, because it required newer build tools (newer versions of MSVC/Clang/GCC) than base Flutter SDK. + This broke the ability to build the app for some users compiling Windows and Linux apps with older toolchains. + + To avoid this issue, we're disabling the native crash handling by default for Linux and Windows for now. + You can enable manually by setting `SENTRY_NATIVE_BACKEND=crashpad` environment variable app build. + You can read more about available backends that fit your use-case in [sentry-native docs](https://docs.sentry.io/platforms/native/configuration/backends/). + + We plan to change the default to back to `crashpad` in the next major SDK release. + ## 8.11.1 ### Improvements - Check for type before casting in TTID ([#2497](https://github.com/getsentry/sentry-dart/pull/2497)) -### Fixes +### Fixes - SentryWidgetsFlutterBinding initializing even if a binding already exists ([#2494](https://github.com/getsentry/sentry-dart/pull/2494)) From 67d7d6a0a7fd9db40bf5ebd3b29712e3c92487a3 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 19 Dec 2024 17:23:19 +0100 Subject: [PATCH 4/5] chore: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fabd19e0a..a60ce90e40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ This broke the ability to build the app for some users compiling Windows and Linux apps with older toolchains. To avoid this issue, we're disabling the native crash handling by default for Linux and Windows for now. - You can enable manually by setting `SENTRY_NATIVE_BACKEND=crashpad` environment variable app build. + You can enable manually by setting `SENTRY_NATIVE_BACKEND=crashpad` environment variable before running `flutter build`. You can read more about available backends that fit your use-case in [sentry-native docs](https://docs.sentry.io/platforms/native/configuration/backends/). We plan to change the default to back to `crashpad` in the next major SDK release. From fc5cc91143ca67ac6349bb3ffe961864e409e9ec Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:37:50 +0100 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Giancarlo Buenaflor --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a60ce90e40..8234c3d697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,10 @@ This broke the ability to build the app for some users compiling Windows and Linux apps with older toolchains. To avoid this issue, we're disabling the native crash handling by default for Linux and Windows for now. - You can enable manually by setting `SENTRY_NATIVE_BACKEND=crashpad` environment variable before running `flutter build`. + You can enable it manually by setting the `SENTRY_NATIVE_BACKEND=crashpad` environment variable before running `flutter build`. You can read more about available backends that fit your use-case in [sentry-native docs](https://docs.sentry.io/platforms/native/configuration/backends/). - We plan to change the default to back to `crashpad` in the next major SDK release. + We plan to change the default back to `crashpad` in the next major SDK release. ## 8.11.1