diff --git a/.gitignore b/.gitignore index 4ee4ae8..03af89e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -zig-out/ -.zig-cache/ - +**/zig-out/ +**/.zig-cache/ +**/zig-pkg/ +**/.sentry-native diff --git a/README.md b/README.md index 5fe69ce..b5fb639 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ First add the dependencies to your project > zig fetch --save git+https://github.com/olksdr/sentry-native.git ``` -And after you can import `sentry_native` in yout `build.zig`. _Note_: the dash ("-") is replaced with underscore ("_") in the name. +And after you can import `sentry_native` in your `build.zig`. _Note_: the dash ("-") is replaced with underscore ("_") in the name. ```zig const sentry_native = b.dependency("sentry_native", .{ .target = target, .optimize = optimize, - // Only if you run on Unix-like system you might want to link with libcurl (deafult is "false"). + // Only if you run on Unix-like system you might want to link with libcurl (default is "false"). .curl = true, // Only if you are on Windows (default "false"). .winhttp = true, @@ -34,6 +34,9 @@ exe.linkLibrary(sentry_native.artifact("sentry")); // const sentry = @import("sentry"); exe.root_module.addImport("sentry", sentry_native.module("sentry")); +// Install crashpad binaries +@import("sentry_native").installCrashpad(b, &exe.step, sentry, target); + ``` You might have to install additional dependencies and makes sure the your dev environment is configured and all paths and includes are in-place. @@ -52,5 +55,4 @@ Currently only `none` or `inproc` (enabled by default) backends are supported. As part of this build the following items still on the list: -- [ ] Support Crashpad backend. - [ ] Support Breakpad backend. diff --git a/build.zig b/build.zig index 9ea0ccb..502ca84 100644 --- a/build.zig +++ b/build.zig @@ -3,21 +3,27 @@ const std = @import("std"); // The version of the `sentry-native` SDK. const version: std.SemanticVersion = .{ .major = 0, .minor = 8, .patch = 1 }; +const Backend = enum { + none, + in_proc, + crashpad, +}; + // Helper function to run the system command and return the stdout with trimmed whitespaces and new lines. fn run_cmd(opts: struct { + b: *std.Build, allocator: std.mem.Allocator, argv: []const []const u8, cwd: ?[]const u8 = null, }) ![]const u8 { std.log.info("{s}", .{try std.mem.join(opts.allocator, " ", opts.argv)}); - const cmd = try std.process.Child.run(.{ - .allocator = opts.allocator, + const cmd = try std.process.run(opts.allocator, opts.b.graph.io, .{ .argv = opts.argv, - .cwd = opts.cwd, + .cwd = if (opts.cwd) |cwd| .{ .path = cwd } else .inherit, }); - if (cmd.term.Exited != 0) { + if (cmd.term.exited != 0) { std.log.err("Faild with error: {s}", .{cmd.stderr}); return error.NonZeroExit; } @@ -34,14 +40,24 @@ pub fn build(b: *std.Build) void { // Add options which could be provided by the uses of this library. const linkage = b.option(std.builtin.LinkMode, "linkage", "Link mode of the sentry_native library (default: static)") orelse .static; - const with_in_proc_backend = b.option(bool, "inproc", "Enable inproc backend (default: true)") orelse true; + const backend = b.option(Backend, "backend", "Backend to use (default: Crashpad)") orelse .crashpad; const with_zlib = b.option(bool, "zlib", "Enable transport compression (default: false)") orelse false; - // Get the SDK path. - const sdk_path = run_cmd(.{ - .allocator = b.allocator, - .argv = &.{ "xcrun", "--show-sdk-path" }, - }) catch @panic("Unable to run command to get the MacOS sdk path."); + var cflags_list: std.ArrayList([]const u8) = .empty; + cflags_list.appendSlice(b.allocator, &.{ + "-Wall", "-pedantic", "-O2", "-fpic", "-MTd", "-pthread", + }) catch @panic("OOM"); + + if (target.result.os.tag == .macos) { + // Get the SDK path. + const sdk_path = run_cmd(.{ + .b = b, + .allocator = b.allocator, + .argv = &.{ "xcrun", "--show-sdk-path" }, + }) catch @panic("Unable to run command to get the MacOS sdk path."); + + cflags_list.appendSlice(b.allocator, &.{ "-isysroot", sdk_path }) catch @panic("OOM"); + } // Prepare the build of the static library. const sentry_native = b.addLibrary(.{ @@ -60,28 +76,50 @@ pub fn build(b: *std.Build) void { } // Compiler flags. - const cflags: []const []const u8 = &.{ "-Wall", "-pedantic", "-O2", "-fpic", "-MTd", "-pthread", "-isysroot", sdk_path }; + const cflags = cflags_list.items; // Enable transport compression. if (with_zlib) { // Link the zlib. - sentry_native.linkSystemLibrary("z"); + sentry_native.root_module.linkSystemLibrary("z", .{}); sentry_native.root_module.addCMacro("SENTRY_TRANSPORT_COMPRESSION", "1"); } b.installArtifact(sentry_native); // Setup the library's inputs and outputs. - sentry_native.addIncludePath(upstream.path("include")); - sentry_native.addIncludePath(upstream.path("src")); - sentry_native.addIncludePath(upstream.path("vendor")); + sentry_native.root_module.addIncludePath(upstream.path("include")); + sentry_native.root_module.addIncludePath(upstream.path("src")); + sentry_native.root_module.addIncludePath(upstream.path("vendor")); sentry_native.installHeadersDirectory(upstream.path("include"), "", .{}); - sentry_native.addCSourceFiles(.{ .files = sentry_src, .root = upstream.path(""), .flags = cflags }); + sentry_native.root_module.addCSourceFiles(.{ .files = sentry_src, .root = upstream.path(""), .flags = cflags }); + + switch (backend) { + .none => { + sentry_native.root_module.addCSourceFile(.{ .file = upstream.path("src/backends/sentry_backend_none.c"), .flags = cflags }); + }, + .in_proc => { + sentry_native.root_module.addCSourceFile(.{ .file = upstream.path("src/backends/sentry_backend_inproc.c"), .flags = cflags }); + }, + .crashpad => { + const crashpad = b.lazyDependency("crashpad", .{ + .target = target, + .optimize = optimize, + }) orelse @panic("Failed to resolve crashpad"); + + sentry_native.root_module.addCSourceFile(.{ .file = upstream.path("src/backends/sentry_backend_crashpad.cpp"), .flags = cflags }); + sentry_native.is_linking_libcpp = true; + + sentry_native.root_module.linkLibrary(crashpad.artifact("crashpad_client")); - // Enable inproc backend. - if (with_in_proc_backend) { - sentry_native.addCSourceFile(.{ .file = upstream.path("src/backends/sentry_backend_inproc.c"), .flags = cflags }); - } else { - sentry_native.addCSourceFile(.{ .file = upstream.path("src/backends/sentry_backend_none.c"), .flags = cflags }); + // Install crashpad_handler + const crashpad_handler = crashpad.artifact("crashpad_handler"); + b.installArtifact(crashpad_handler); + + if (target.result.os.tag == .windows) { + const wer_module = crashpad.artifact("crashpad_wer"); + b.installArtifact(wer_module); + } + }, } switch (target.result.os.tag) { @@ -90,45 +128,58 @@ pub fn build(b: *std.Build) void { const with_curl = b.option(bool, "curl", "Enable curl support (Unix-like only) for backend transport (default: false)") orelse false; // Link libcurl only for unix-like systems. if (with_curl) { - sentry_native.linkSystemLibrary("curl"); - sentry_native.addCSourceFile(.{ .file = upstream.path("src/transports/sentry_transport_curl.c"), .flags = cflags }); + const curl_dependency = b.dependency("curl", .{ + .target = target, + .optimize = optimize, + .libpsl = false, + .libssh2 = false, + .libidn2 = false, + .nghttp2 = false, + .@"disable-ldap" = true, + .@"use-boringssl" = true, + }); + + const lib_curl = @import("curl").artifact(curl_dependency, .lib); + sentry_native.root_module.linkLibrary(lib_curl); + + sentry_native.root_module.addCSourceFile(.{ .file = upstream.path("src/transports/sentry_transport_curl.c"), .flags = cflags }); } else { - sentry_native.addCSourceFile(.{ .file = upstream.path("src/transports/sentry_transport_none.c"), .flags = cflags }); + sentry_native.root_module.addCSourceFile(.{ .file = upstream.path("src/transports/sentry_transport_none.c"), .flags = cflags }); } - sentry_native.linkSystemLibrary("pthread"); + sentry_native.root_module.linkSystemLibrary("pthread", .{}); - sentry_native.addCSourceFiles(.{ .files = sentry_unix_src, .root = upstream.path(""), .flags = cflags }); + sentry_native.root_module.addCSourceFiles(.{ .files = sentry_unix_src, .root = upstream.path(""), .flags = cflags }); // Linux specific. if (target.result.os.tag == .linux) { std.debug.print("Starting Linux Build.\n", .{}); // Link libc when on linux. - sentry_native.linkLibC(); - sentry_native.addCSourceFile(.{ .file = upstream.path("src/modulefinder/sentry_modulefinder_linux.c"), .flags = cflags }); + sentry_native.root_module.link_libc = true; + sentry_native.root_module.addCSourceFile(.{ .file = upstream.path("src/modulefinder/sentry_modulefinder_linux.c"), .flags = cflags }); } // Macos specific if (target.result.os.tag == .macos) { std.debug.print("Starting MacOs Build.\n", .{}); - sentry_native.addCSourceFile(.{ .file = upstream.path("src/modulefinder/sentry_modulefinder_apple.c"), .flags = cflags }); + sentry_native.root_module.addCSourceFile(.{ .file = upstream.path("src/modulefinder/sentry_modulefinder_apple.c"), .flags = cflags }); } }, .windows => { std.debug.print("Starting Windows Build.\n", .{}); - sentry_native.linkLibC(); - sentry_native.linkSystemLibrary("version"); - sentry_native.linkSystemLibrary("dbghelp"); + sentry_native.root_module.link_libc = true; + sentry_native.root_module.linkSystemLibrary("version", .{}); + sentry_native.root_module.linkSystemLibrary("dbghelp", .{}); // Add option to enable winhttp support. const with_winhttp = b.option(bool, "winhttp", "Enable winhttp support (Windows only) for backend transport (default: false)") orelse false; // Enable winhttp support if option provided otherwise there will be no transport. if (with_winhttp) { - sentry_native.addCSourceFile(.{ .file = upstream.path("src/transports/sentry_transport_winhttp.c"), .flags = cflags }); + sentry_native.root_module.addCSourceFile(.{ .file = upstream.path("src/transports/sentry_transport_winhttp.c"), .flags = cflags }); } else { - sentry_native.addCSourceFile(.{ .file = upstream.path("src/transports/sentry_transport_none.c"), .flags = cflags }); + sentry_native.root_module.addCSourceFile(.{ .file = upstream.path("src/transports/sentry_transport_none.c"), .flags = cflags }); } - sentry_native.addCSourceFiles(.{ .files = sentry_win_src, .root = upstream.path(""), .flags = cflags }); + sentry_native.root_module.addCSourceFiles(.{ .files = sentry_win_src, .root = upstream.path(""), .flags = cflags }); }, else => @panic("Unsupported build target."), } @@ -198,3 +249,15 @@ pub const sentry_src: []const []const u8 = &.{ "vendor/stb_sprintf.c", "vendor/mpack.c", }; + +// Install crashpad +// Makes the given step a dependency of the installation +pub fn installCrashpad(target_build: *std.Build, step: *std.Build.Step, sentry: *std.Build.Dependency, target: std.Build.ResolvedTarget) void { + const install_crashpad = target_build.addInstallArtifact(sentry.artifact("crashpad_handler"), .{}); + step.dependOn(&install_crashpad.step); + + if (target.result.os.tag == .windows) { + const install_wer = target_build.addInstallArtifact(sentry.artifact("crashpad_wer"), .{}); + step.dependOn(&install_wer.step); + } +} diff --git a/build.zig.zon b/build.zig.zon index 7e40d83..62e4d80 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,12 +2,21 @@ .name = .sentry_native, .version = "0.2.0", .fingerprint = 0x8c2691ae4ce6c4cf, - .minimum_zig_version = "0.15.0", + .minimum_zig_version = "0.16.0", .dependencies = .{ .@"sentry-native" = .{ .url = "git+https://github.com/getsentry/sentry-native.git#ccef7125b3783210f44ee6b100df7278e6ba3eff", .hash = "N-V-__8AAJmoHgAPxgAObxk1ekBBdb0LSAf71bFKKpgSq5aS", }, + .crashpad = .{ + .url = "git+https://github.com/LukasKastern/crashpad.git#84151ec7b7e04e690d81c2866d66da06a8935324", + .hash = "crashpad-0.1.0-tVD9TQeNAAAnmV4gLisVGynQo44zGj3vv21SjMykbV_k", + .lazy = true, + }, + .curl = .{ + .url = "git+https://github.com/lukaskastern/curl.git?ref=boringssl#79c7db96e951a5ddbb0180c1412b1d5a2022e1ad", + .hash = "curl-8.18.0-mFDAWCH5AADu_sgXOXIw6_ZDSmqLsybYPS3BkU6gDRZY", + }, }, .paths = .{ "build.zig", diff --git a/examples/crashpad/build.zig b/examples/crashpad/build.zig new file mode 100644 index 0000000..48dd426 --- /dev/null +++ b/examples/crashpad/build.zig @@ -0,0 +1,39 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const sentry = b.dependency("sentry_native", .{ + .target = target, + .optimize = optimize, + .backend = .crashpad, + }); + + const exe_mod = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + const exe = b.addExecutable(.{ + .name = "simple", + .root_module = exe_mod, + }); + + exe.root_module.linkLibrary(sentry.artifact("sentry")); + exe.root_module.addImport("sentry", sentry.module("sentry")); + + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + // Install crashpad binaries + @import("sentry_native").installCrashpad(b, &exe.step, sentry, target); +} diff --git a/examples/crashpad/build.zig.zon b/examples/crashpad/build.zig.zon new file mode 100644 index 0000000..ac0d8b2 --- /dev/null +++ b/examples/crashpad/build.zig.zon @@ -0,0 +1,16 @@ +.{ + .name = .simple, + .version = "0.1.0", + .fingerprint = 0xc17b3d028051780f, // Changing this has security and trust implications. + .minimum_zig_version = "0.16.0", + .dependencies = .{ + .sentry_native = .{ + .path = "../../", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/examples/crashpad/src/main.zig b/examples/crashpad/src/main.zig new file mode 100644 index 0000000..2723990 --- /dev/null +++ b/examples/crashpad/src/main.zig @@ -0,0 +1,21 @@ +const sentry = @import("sentry"); + +pub fn main() !void { + // Create a new options set. + const opts = sentry.sentry_options_new(); + // Make sure to set the correct DSN. + sentry.sentry_options_set_dsn(opts, "https://public@sentry.example.com/1"); + // Do not sample errors + sentry.sentry_options_set_sample_rate(opts, 1.0); + // Do not sample transactions. + sentry.sentry_options_set_traces_sample_rate(opts, 1.0); + // Enable DEBUG, 0 is to turn off the debug logging. + sentry.sentry_options_set_debug(opts, 1); + + // Initiate the SDK using options we set up before, and now we can use it to sent the events to Sentry. + _ = sentry.sentry_init(opts); + // Make sure we release all the resources before the program exits. + defer _ = sentry.sentry_close(); + + @panic("Crash to forward to sentry"); +} diff --git a/examples/simple/build.zig b/examples/simple/build.zig index f1050bc..ef91be8 100644 --- a/examples/simple/build.zig +++ b/examples/simple/build.zig @@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void { .root_module = exe_mod, }); - exe.linkLibrary(sentry.artifact("sentry")); + exe.root_module.linkLibrary(sentry.artifact("sentry")); exe.root_module.addImport("sentry", sentry.module("sentry")); b.installArtifact(exe); diff --git a/examples/simple/build.zig.zon b/examples/simple/build.zig.zon index 38284c5..ac0d8b2 100644 --- a/examples/simple/build.zig.zon +++ b/examples/simple/build.zig.zon @@ -2,11 +2,10 @@ .name = .simple, .version = "0.1.0", .fingerprint = 0xc17b3d028051780f, // Changing this has security and trust implications. - .minimum_zig_version = "0.15.0", + .minimum_zig_version = "0.16.0", .dependencies = .{ .sentry_native = .{ - .url = "../../", - .hash = "sentry_native-0.2.0-z8TmTOItAACFYGluvX09B8pu81K8F4cl5J5hruHNfK0S", + .path = "../../", }, }, .paths = .{