Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add build.zig API for linking against libstdc++ #3936

Open
dcao opened this issue Dec 18, 2019 · 4 comments
Open

add build.zig API for linking against libstdc++ #3936

dcao opened this issue Dec 18, 2019 · 4 comments
Labels
standard library This issue involves writing Zig code for the standard library. zig build system std.Build, the build runner, `zig build` subcommand, package management
Milestone

Comments

@dcao
Copy link
Contributor

dcao commented Dec 18, 2019

I'm attempting to build a project which links in the hyperscan regex library. The build.zig script links to the hyperscan library and I have a zig source file which includes the hyperscan header file in a @cImport block. However, when I attempt to build the project with zig build, I get this error message:

lld: error: undefined symbol: __gxx_personality_v0
>>> referenced by libunwind.cpp
>>>               /home/david/.local/share/zig/stage1/o/O52Tz8yjwKTfxA_7ZFqf7o46uv59sKmBhDgOE31UEeWVeMBV0cLPD4GnzhB-Hkqz/libunwind.o:(DW.ref.__gxx_personality_v0) in archive /home/david/.local/share/zig/stage1/o/5ImJwmxLqixH7RSFicg8PiRam4oQs9H1Xb3dmdM5J2Rni9aDl1cboZirbXRsFt5r/libunwind.a

The following command exited with error code 1:
/nix/store/v7gp1yjd5dz0gzadxckz18c9k0ygy9xc-zig-0.5.0/bin/zig build-exe /home/david/dev/wordsmith/src/main.zig --library c --library hs --cache-dir /home/david/dev/wordsmith/zig-cache --name ws -isystem /nix/store/66jnj8nqsk9ir44y4fi2kpxb7r3crmqn-hyperscan-5.2.1-dev/include/hs -L /nix/store/islpcsy87hf3jspb7mwlgzgwsfkkinpy-hyperscan-5.2.1/lib -isystem /nix/store/66jnj8nqsk9ir44y4fi2kpxb7r3crmqn-hyperscan-5.2.1-dev/include -isystem /nix/store/p52h688mi6yb62wfsw2ibj3xm6vqq98v-gdb-8.3.1/include -isystem /nix/store/i5wf0wfyd4n4lnvksbbgbwllpq1mq743-valgrind-3.15.0-dev/include -isystem /nix/store/95gcjjxrafz14lcm1qlr0c22cllq0z0g-compiler-rt-7.1.0-dev/include -isystem /nix/store/66jnj8nqsk9ir44y4fi2kpxb7r3crmqn-hyperscan-5.2.1-dev/include -isystem /nix/store/p52h688mi6yb62wfsw2ibj3xm6vqq98v-gdb-8.3.1/include -isystem /nix/store/i5wf0wfyd4n4lnvksbbgbwllpq1mq743-valgrind-3.15.0-dev/include -isystem /nix/store/95gcjjxrafz14lcm1qlr0c22cllq0z0g-compiler-rt-7.1.0-dev/include -rpath /nix/store/8yflb7qdsj3qb791jsiwg67zhcvbxirc-lorri-keep-env-hack-wordsmith/lib64 -rpath /nix/store/8yflb7qdsj3qb791jsiwg67zhcvbxirc-lorri-keep-env-hack-wordsmith/lib --library-path /nix/store/islpcsy87hf3jspb7mwlgzgwsfkkinpy-hyperscan-5.2.1/lib --library-path /nix/store/p52h688mi6yb62wfsw2ibj3xm6vqq98v-gdb-8.3.1/lib --library-path /nix/store/islpcsy87hf3jspb7mwlgzgwsfkkinpy-hyperscan-5.2.1/lib --library-path /nix/store/p52h688mi6yb62wfsw2ibj3xm6vqq98v-gdb-8.3.1/lib --cache on

Build failed. The following command failed:
/home/david/dev/wordsmith/zig-cache/o/z56kk2rvgVc7NHq34aIRdQ-vtWKZuuPxTQrscrdjfBnSZIyXKXsJ0VTog_KCdiXB/build /nix/store/v7gp1yjd5dz0gzadxckz18c9k0ygy9xc-zig-0.5.0/bin/zig /home/david/dev/wordsmith /home/david/dev/wordsmith/zig-cache

My hunch is that the remedy would involve using libstdc++ by passing in --stdlib=libc++ or something similar to clang; however, I'm not sure if this is correct, and even if it was, there's no way to pass arbitrary flags to clang via the zig cli or a build.zig file, as far as I can tell.

@andrewrk
Copy link
Member

Yeah you need libstdc++ to the linker. I think we should have a build.zig API to support this use case; trying to link libstdc++ is pretty common, and is relevant for #853.

In the meantime, your best bet is probably looking at zig's own build.zig script, because the self-hosted compiler must link libstdc++.

@andrewrk andrewrk added the zig build system std.Build, the build runner, `zig build` subcommand, package management label Dec 18, 2019
@andrewrk andrewrk added this to the 0.7.0 milestone Dec 18, 2019
@andrewrk andrewrk added the standard library This issue involves writing Zig code for the standard library. label Dec 18, 2019
@andrewrk andrewrk changed the title lld: error: undefined symbol: __gxx_personality_v0 add build.zig API for linking against libstdc++ Dec 18, 2019
@dcao
Copy link
Contributor Author

dcao commented Dec 18, 2019

Thanks for the pointers! I got a workaround working with the following code in my build script:

pub fn build(b: *Builder) !void {
    // snip
    try linkStdCpp(b, exe, ctx);
    // snip
}

const LibraryDep = struct {
    prefix: []const u8,
    libdirs: ArrayList([]const u8),
    libs: ArrayList([]const u8),
    system_libs: ArrayList([]const u8),
    includes: ArrayList([]const u8),
};

const Context = struct {
    cmake_binary_dir: []const u8,
    cxx_compiler: []const u8,
    llvm_config_exe: []const u8,
    lld_include_dir: []const u8,
    lld_libraries: []const u8,
    dia_guids_lib: []const u8,
    llvm: LibraryDep,
};

fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
    const shared_mode = try b.exec(&[_][]const u8{ llvm_config_exe, "--shared-mode" });
    const is_static = mem.startsWith(u8, shared_mode, "static");
    const libs_output = if (is_static)
        try b.exec(&[_][]const u8{
            llvm_config_exe,
            "--libfiles",
            "--system-libs",
        })
    else
        try b.exec(&[_][]const u8{
            llvm_config_exe,
            "--libs",
        });
    const includes_output = try b.exec(&[_][]const u8{ llvm_config_exe, "--includedir" });
    const libdir_output = try b.exec(&[_][]const u8{ llvm_config_exe, "--libdir" });
    const prefix_output = try b.exec(&[_][]const u8{ llvm_config_exe, "--prefix" });

    var result = LibraryDep{
        .prefix = mem.tokenize(prefix_output, " \r\n").next().?,
        .libs = ArrayList([]const u8).init(b.allocator),
        .system_libs = ArrayList([]const u8).init(b.allocator),
        .includes = ArrayList([]const u8).init(b.allocator),
        .libdirs = ArrayList([]const u8).init(b.allocator),
    };
    {
        var it = mem.tokenize(libs_output, " \r\n");
        while (it.next()) |lib_arg| {
            if (mem.startsWith(u8, lib_arg, "-l")) {
                try result.system_libs.append(lib_arg[2..]);
            } else {
                if (fs.path.isAbsolute(lib_arg)) {
                    try result.libs.append(lib_arg);
                } else {
                    if (mem.endsWith(u8, lib_arg, ".lib")) {
                        lib_arg = lib_arg[0 .. lib_arg.len - 4];
                    }
                    try result.system_libs.append(lib_arg);
                }
            }
        }
    }
    {
        var it = mem.tokenize(includes_output, " \r\n");
        while (it.next()) |include_arg| {
            if (mem.startsWith(u8, include_arg, "-I")) {
                try result.includes.append(include_arg[2..]);
            } else {
                try result.includes.append(include_arg);
            }
        }
    }
    {
        var it = mem.tokenize(libdir_output, " \r\n");
        while (it.next()) |libdir| {
            if (mem.startsWith(u8, libdir, "-L")) {
                try result.libdirs.append(libdir[2..]);
            } else {
                try result.libdirs.append(libdir);
            }
        }
    }
    return result;
}

fn nextValue(index: *usize, build_info: []const u8) []const u8 {
    const start = index.*;
    while (true) : (index.* += 1) {
        switch (build_info[index.*]) {
            '\n' => {
                const result = build_info[start..index.*];
                index.* += 1;
                return result;
            },
            '\r' => {
                const result = build_info[start..index.*];
                index.* += 2;
                return result;
            },
            else => continue,
        }
    }
}

fn getContext(b: *Builder) !Context {
    // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library
    const build_info = try b.exec(&[_][]const u8{
        b.zig_exe,
        "BUILD_INFO",
    });
    var index: usize = 0;
    var ctx = Context{
        .cmake_binary_dir = nextValue(&index, build_info),
        .cxx_compiler = nextValue(&index, build_info),
        .llvm_config_exe = nextValue(&index, build_info),
        .lld_include_dir = nextValue(&index, build_info),
        .lld_libraries = nextValue(&index, build_info),
        .dia_guids_lib = nextValue(&index, build_info),
        .llvm = undefined,
    };
    ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);

    return ctx;
}

fn linkStdCpp(b: *Builder, exe: var, ctx: Context) !void {
    if (exe.target.getOs() == .linux) {
        try addCxxKnownPath(b, ctx, exe, "libstdc++.a",
            \\Unable to determine path to libstdc++.a
            \\On Fedora, install libstdc++-static and try again.
        );

        exe.linkSystemLibrary("pthread");
    } else if (exe.target.isFreeBSD()) {
        try addCxxKnownPath(b, ctx, exe, "libc++.a", null);
        exe.linkSystemLibrary("pthread");
    } else if (exe.target.isDarwin()) {
        if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) {
            // Compiler is GCC.
            try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null);
            exe.linkSystemLibrary("pthread");
            // TODO LLD cannot perform this link.
            // See https://github.com/ziglang/zig/issues/1535
            exe.enableSystemLinkerHack();
        } else |err| switch (err) {
            error.RequiredLibraryNotFound => {
                // System compiler, not gcc.
                exe.linkSystemLibrary("c++");
            },
            else => return err,
        }
    }

    if (ctx.dia_guids_lib.len != 0) {
        exe.addObjectFile(ctx.dia_guids_lib);
    }

    exe.linkSystemLibrary("c");
}

fn addCxxKnownPath(
    b: *Builder,
    ctx: Context,
    exe: var,
    objname: []const u8,
    errtxt: ?[]const u8,
) !void {
    const path_padded = try b.exec(&[_][]const u8{
        ctx.cxx_compiler,
        b.fmt("-print-file-name={}", objname),
    });
    const path_unpadded = mem.tokenize(path_padded, "\r\n").next().?;
    if (mem.eql(u8, path_unpadded, objname)) {
        if (errtxt) |msg| {
            warn("{}", msg);
        } else {
            warn("Unable to determine path to {}\n", objname);
        }
        return error.RequiredLibraryNotFound;
    }
    exe.addObjectFile(path_unpadded);
}

@andrewrk
Copy link
Member

Related: #4682

@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Oct 17, 2020
@andrewrk andrewrk modified the milestones: 0.8.0, 0.9.0 Nov 6, 2020
@andrewrk andrewrk modified the milestones: 0.9.0, 0.10.0 May 19, 2021
@ashpil
Copy link

ashpil commented Sep 8, 2023

I think we should have a build.zig API to support this use case

Is this still planned?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
standard library This issue involves writing Zig code for the standard library. zig build system std.Build, the build runner, `zig build` subcommand, package management
Projects
None yet
Development

No branches or pull requests

3 participants