const std = @import("std");
const builtin = @import("builtin");

pub fn build(b: *std.Build) void {
    const test_step = b.step("test", "Test it");
    b.default_step = test_step;

    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    if (target.result.cpu.arch.isRISCV() and target.result.os.tag == .linux) {
        // https://github.com/ziglang/zig/issues/24310
        return;
    }

    // Unwinding with a frame pointer
    //
    // getcontext version: zig std
    //
    // Unwind info type:
    //   - ELF: DWARF .debug_frame
    //   - MachO: __unwind_info encodings:
    //     - x86_64: RBP_FRAME
    //     - aarch64: FRAME, DWARF
    {
        const exe = b.addExecutable(.{
            .name = "unwind_fp",
            .root_module = b.createModule(.{
                .root_source_file = b.path("unwind.zig"),
                .target = target,
                .optimize = optimize,
                .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null,
                .omit_frame_pointer = false,
            }),
        });

        const run_cmd = b.addRunArtifact(exe);
        test_step.dependOn(&run_cmd.step);
    }

    // Unwinding without a frame pointer
    //
    // getcontext version: zig std
    //
    // Unwind info type:
    //   - ELF: DWARF .eh_frame_hdr + .eh_frame
    //   - MachO: __unwind_info encodings:
    //     - x86_64: STACK_IMMD, STACK_IND
    //     - aarch64: FRAMELESS, DWARF
    {
        const exe = b.addExecutable(.{
            .name = "unwind_nofp",
            .root_module = b.createModule(.{
                .root_source_file = b.path("unwind.zig"),
                .target = target,
                .optimize = optimize,
                .unwind_tables = .async,
                .omit_frame_pointer = true,
            }),
            // self-hosted lacks omit_frame_pointer support
            .use_llvm = true,
        });

        const run_cmd = b.addRunArtifact(exe);
        test_step.dependOn(&run_cmd.step);
    }

    // https://github.com/ziglang/zig/issues/24522
    //// Unwinding through a C shared library without a frame pointer (libc)
    ////
    //// getcontext version: libc
    ////
    //// Unwind info type:
    ////   - ELF: DWARF .eh_frame + .debug_frame
    ////   - MachO: __unwind_info encodings:
    ////     - x86_64: STACK_IMMD, STACK_IND
    ////     - aarch64: FRAMELESS, DWARF
    //{
    //    const c_shared_lib = b.addLibrary(.{
    //        .linkage = .dynamic,
    //        .name = "c_shared_lib",
    //        .root_module = b.createModule(.{
    //            .root_source_file = null,
    //            .target = target,
    //            .optimize = optimize,
    //            .link_libc = true,
    //            .strip = false,
    //        }),
    //    });

    //    if (target.result.os.tag == .windows)
    //        c_shared_lib.root_module.addCMacro("LIB_API", "__declspec(dllexport)");

    //    c_shared_lib.root_module.addCSourceFile(.{
    //        .file = b.path("shared_lib.c"),
    //        .flags = &.{"-fomit-frame-pointer"},
    //    });

    //    const exe = b.addExecutable(.{
    //        .name = "shared_lib_unwind",
    //        .root_module = b.createModule(.{
    //            .root_source_file = b.path("shared_lib_unwind.zig"),
    //            .target = target,
    //            .optimize = optimize,
    //            .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null,
    //            .omit_frame_pointer = true,
    //        }),
    //        // zig objcopy doesn't support incremental binaries
    //        .use_llvm = true,
    //    });

    //    exe.root_module.linkLibrary(c_shared_lib);

    //    const run_cmd = b.addRunArtifact(exe);
    //    test_step.dependOn(&run_cmd.step);

    //    // Separate debug info ELF file
    //    if (target.result.ofmt == .elf) {
    //        const filename = b.fmt("{s}_stripped", .{exe.out_filename});
    //        const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{
    //            .basename = filename, // set the name for the debuglink
    //            .compress_debug = true,
    //            .strip = .debug,
    //            .extract_to_separate_file = true,
    //        });

    //        const run_stripped = std.Build.Step.Run.create(b, b.fmt("run {s}", .{filename}));
    //        run_stripped.addFileArg(stripped_exe.getOutput());
    //        test_step.dependOn(&run_stripped.step);
    //    }
    //}

    // Unwinding without libc/posix
    //
    // No "getcontext" or "ucontext_t"
    const no_os_targets = [_]std.Target.Os.Tag{ .freestanding, .other };
    inline for (no_os_targets) |os_tag| {
        const exe = b.addExecutable(.{
            .name = "unwind_freestanding",
            .root_module = b.createModule(.{
                .root_source_file = b.path("unwind_freestanding.zig"),
                .target = b.resolveTargetQuery(.{
                    .cpu_arch = .x86_64,
                    .os_tag = os_tag,
                }),
                .optimize = optimize,
                .unwind_tables = null,
                .omit_frame_pointer = false,
            }),
            // self-hosted lacks omit_frame_pointer support
            .use_llvm = true,
        });

        // This "freestanding" binary is runnable because it invokes the
        // Linux exit syscall directly.
        if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) {
            const run_cmd = b.addRunArtifact(exe);
            test_step.dependOn(&run_cmd.step);
        } else {
            test_step.dependOn(&exe.step);
        }
    }
}
