ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
33.63k stars 2.46k forks source link

Multiple undefined libc symbols with GLFW3 on Windows #5735

Open ameyp opened 4 years ago

ameyp commented 4 years ago

I'm trying to build an application that uses GLFW3 on Windows. I've downloaded the "Windows pre-compiled binaries" from https://www.glfw.org/download.html and extracted the contents of the zip to a folder in my zig project.

Here's my build.zig:

const Builder = @import("std").build.Builder;
const builtin = @import("builtin");

pub fn build(b: *Builder) void {
    const mode = b.standardReleaseOptions();
    const exe = b.addExecutable("main", "src/main.zig");
    exe.setBuildMode(mode);

    exe.addIncludeDir("glfw-3.3.2.bin.WIN64/include");
    exe.addLibPath("glfw-3.3.2.bin.WIN64/lib-vc2019");

    exe.linkSystemLibrary("glfw3");
    exe.linkSystemLibrary("c");
    exe.linkSystemLibrary("opengl32");
    exe.linkSystemLibrary("user32");
    exe.linkSystemLibrary("gdi32");
    exe.linkSystemLibrary("shell32");

    exe.install();

    const run_cmd = exe.run();
    run_cmd.step.dependOn(b.getInstallStep());

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}

Here's my main.zig:

const c = @cImport({
    @cInclude("GLFW/glfw3.h");
});

const std = @import("std");
const warn = std.debug.warn;
const panic = std.debug.panic;

var window: *c.GLFWwindow = undefined;

export fn errorCallback(err: c_int, description: [*c]const u8) void {
    panic("Error: {}\n", .{description});
}

pub fn main() u8 {
    _ = c.glfwSetErrorCallback(errorCallback);

    if (c.glfwInit() == c.GL_FALSE) {
        warn("Failed to initialize GLFW\n", .{});
        return 1;
    }

    return 0;
}

And here's the build log:

C:\Users\amey\Developer\zig-windows-x86_64-0.6.0+626b5ecca\zig.exe build run --verbose
C:\Users\amey\Developer\zig-windows-x86_64-0.6.0+626b5ecca\zig.exe build-exe C:\Users\amey\Developer\zig-opengl-sample\src\main.zig --library glfw3 --library c --library opengl32 --library user32 --library gdi32 --library shell32 --cache-dir C:\Users\amey\Developer\zig-opengl-sample\zig-cache --name main -I C:\Users\amey\Developer\zig-opengl-sample\glfw-3.3.2.bin.WIN64\include -L glfw-3.3.2.bin.WIN64/lib-vc2019 --cache on
Build Dependencies...lld: warning: glfw3.lib(init.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(init.obj): locally defined symbol imported: __stdio_common_vsprintf (defined in libucrtd.lib(output.obj)) [LNK4217]
lld: warning: glfw3.lib(window.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(input.obj): locally defined symbol imported: strncmp (defined in libucrtd.lib(strncmp.obj)) [LNK4217]
lld: warning: glfw3.lib(input.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(input.obj): locally defined symbol imported: strtoul (defined in libucrtd.lib(strtox.obj)) [LNK4217]
lld: warning: glfw3.lib(win32_init.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(win32_monitor.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(monitor.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(monitor.obj): locally defined symbol imported: qsort (defined in libucrtd.lib(qsort.obj)) [LNK4217]
lld: warning: glfw3.lib(vulkan.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(context.obj): locally defined symbol imported: strncmp (defined in libucrtd.lib(strncmp.obj)) [LNK4217]
lld: warning: glfw3.lib(win32_window.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(win32_joystick.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(win32_joystick.obj): locally defined symbol imported: qsort (defined in libucrtd.lib(qsort.obj)) [LNK4217]
lld: warning: glfw3.lib(win32_joystick.obj): locally defined symbol imported: __stdio_common_vsprintf (defined in libucrtd.lib(output.obj)) [LNK4217]
lld: warning: glfw3.lib(wgl_context.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(egl_context.obj): locally defined symbol imported: strncmp (defined in libucrtd.lib(strncmp.obj)) [LNK4217]
lld: warning: glfw3.lib(egl_context.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: warning: glfw3.lib(osmesa_context.obj): locally defined symbol imported: free (defined in libucrtd.lib(free.obj)) [LNK4217]
lld: error: undefined symbol: __declspec(dllimport) calloc
>>> referenced by glfw3.lib(init.obj):($LN40)
>>> referenced by glfw3.lib(init.obj):($LN9)
>>> referenced by glfw3.lib(window.obj):($LN20)
>>> referenced by glfw3.lib(input.obj):($LN7)
>>> referenced by glfw3.lib(input.obj):($LN9)
>>> referenced by glfw3.lib(input.obj):($LN84)
>>> referenced by glfw3.lib(input.obj):($LN84)
>>> referenced by glfw3.lib(input.obj):($LN84)
>>> referenced by glfw3.lib(win32_init.obj):($LN7)
>>> referenced by glfw3.lib(win32_init.obj):($LN7)
>>> referenced 17 more times

lld: error: undefined symbol: __declspec(dllimport) strncpy
>>> referenced by glfw3.lib(window.obj):(glfwWindowHintString)
>>> referenced by glfw3.lib(input.obj):($LN84)
>>> referenced by glfw3.lib(win32_joystick.obj):($LN5)

lld: error: undefined symbol: __declspec(dllimport) strcspn
>>> referenced by glfw3.lib(input.obj):($LN45)
>>> referenced by glfw3.lib(input.obj):($LN45)
>>> referenced by glfw3.lib(input.obj):(parseMapping)
>>> referenced by glfw3.lib(input.obj):(parseMapping)
>>> referenced by glfw3.lib(input.obj):(parseMapping)

lld: error: undefined symbol: __declspec(dllimport) strspn
>>> referenced by glfw3.lib(input.obj):($LN45)
>>> referenced by glfw3.lib(input.obj):(parseMapping)

lld: error: undefined symbol: __declspec(dllimport) realloc
>>> referenced by glfw3.lib(input.obj):($LN45)
>>> referenced by glfw3.lib(win32_monitor.obj):($LN33)
>>> referenced by glfw3.lib(monitor.obj):($LN36)

lld: error: undefined symbol: __declspec(dllimport) __stdio_common_vsscanf
>>> referenced by glfw3.lib(context.obj):($LN6)

main...The following command exited with error code 1:
C:\Users\amey\Developer\zig-windows-x86_64-0.6.0+626b5ecca\zig.exe build-exe C:\Users\amey\Developer\zig-opengl-sample\src\main.zig --library glfw3 --library c --library opengl32 --library user32 --library gdi32 --library shell32 --cache-dir C:\Users\amey\Developer\zig-opengl-sample\zig-cache --name main -I C:\Users\amey\Developer\zig-opengl-sample\glfw-3.3.2.bin.WIN64\include -L glfw-3.3.2.bin.WIN64/lib-vc2019 --cache on

Build failed. The following command failed:
C:\Users\amey\Developer\zig-opengl-sample\zig-cache\o\3xPLjFxXvwgUGceLqL2vPdtgnAPpb2tLyPm6M8rcgwVYuLBFOXZ_agwppOiaaXt8\build.exe C:\Users\amey\Developer\zig-windows-x86_64-0.6.0+626b5ecca\zig.exe C:\Users\amey\Developer\zig-opengl-sample C:\Users\amey\Developer\zig-opengl-sample\zig-cache run --verbose

Also, should I be worried about the warnings regarding locally defined symbols being imported?

SpexGuy commented 4 years ago

I've run into something this before. IIRC it's because the glfw lib is compiled with /MD but zig builds with the equivalent of /MT. (documentation) You need a version of glfw that was built for windows with /MT. They've noted that they don't want to host precompiled versions for multiple library types. If you want to get up and running quickly, I have a /MT version that I built for one of my zig projects hosted here: https://github.com/SpexGuy/Zig-ImGui/tree/master/examples/lib/win

ameyp commented 4 years ago

Yep, that did turn out to be the problem. I built my own GLFW locally with USE_MSVC_RUNTIME_LIBRARY_DLL set to OFF and linking succeeded without any errors or warnings. This issue can be closed now, but perhaps this should be documented somewhere?

CuriouserThing commented 4 years ago

Also worth noting (for posterity) that the static libraries built for vcpkg work as well.

andrewrk commented 3 years ago

Leaving this issue open for now to audit this use case and make sure zig is doing everything it should be doing here.

vassvik commented 3 years ago

It looks like USE_MSVC_RUNTIME_LIBRARY_DLL is deprecated or broken as of CMake 3.15, and replaced by CMAKE_MSVC_RUNTIME_LIBRARY. If you use a newer version of CMake than this you'll have problems as of https://github.com/glfw/glfw/commit/539535a367f78a393b48a910956ba9a4ee04e364.

So, to build GLFW so that it links to the CRT statically the correct build flag would be -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded. If this is omitted (and the old flag is used instead) it will append -MD to the CFLAGS in flags.cmake regardless of whether USE_MSVC_RUNTIME_LIBRARY_DLL=OFF adds /MT to the list or not. This seems like an oversight on CMake's end, but it is what it is.

See: https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html#variable:CMAKE_MSVC_RUNTIME_LIBRARY

crystalthoughts commented 3 years ago

I'm getting similar errors despite the flag suggested by @vassvik and @ameyp. vcpkg static libraries not working either!

lld-link: error: could not open 'libuuid.a': no such file or directory
lld-link: error: could not open 'libLIBCMT.a': no such file or directory
lld-link: error: could not open 'libOLDNAMES.a': no such file or directory

Anyone found a workflow that works on Win10 currently?

xtrnc commented 3 years ago

If you've built glfw3 with MSVC, set /MT(d) accordingly (check this in Visual Studio -> right click "glfw" project -> select Properties -> Configuration Properties -> C/C++ -> Code Generation -> Runtime Library -> should be /MT or /MTd) and linked with everything you needed in build.zig (libc, glfw3, gdi32, opengl32 etc.) try to change the target to:

zig build -Dtarget=x86_64-windows-msvc

You may need to setTarget for the exe in build.zig if the -Dtarget option doesn't work:

pub fn build(self: Target, b: *Builder) void {
    const exe = b.addExecutable(self.name, self.src);
    exe.setTarget(b.standardTargetOptions(.{}));
...

Alternatively, for -Dtarget=x86_64-windows-gnu, you can build glfw3 using mingw64 (not with MSVC like above):

  1. install msys2 following the instructions here: https://www.msys2.org/ (don't skip the steps because you won't have gcc with the base install);
  2. add msys64\mingw64\bin to the windows PATH (for cmake to find gcc there, verify with where gcc in cmd);
  3. cmake . -G "MinGW Makefiles" -B build_mingw (useful to set the build type -D CMAKE_BUILD_TYPE=Debug or Release) in the glfw repository (or select "MinGW Makefiles" from the GUI instead of "Visual Studio X 201Y");
  4. cd build_mingw && mingw32-make glfw should generate build_mingw\src\libglfw3.a;
  5. in build.zig replace addLibPath("path_to_glfw3.lib") and linkSystemLibrary("glfw3") with addObjectFile("path_to_build_mingw/src/libglfw3.a");
  6. zig build (or zig build -Dtarget=x86_64-windows-gnu) should work now.

Optional: If you also add -D CMAKE_C_FLAGS=-D_STDIO_DEFINED to the cmd at step 3 and rebuild the library, you'll be able to build with both -Dtarget=x86_64-windows-msvc and -Dtarget=x86_64-windows-gnu using libglfw3.a.

kcbanner commented 3 years ago

I was able to use the above steps to successfully build with both x86_64-windows-gnu and x86_64-windows-msvc using a libglfw3.a built with MinGW.

The only change is that for -gnu I needed to add exe.linkLibCpp();. Otherwise it failed to find __cxa_guard_acquire, __cxa_guard_release, __cxa_guard_release, _Unwind_Resume, __gxx_personality_seh0, __cxa_begin_catch, and std::terminate().

I wasn't able to link a MSVC-built glfw3 (with either /MD or /MT) using x86_64-windows-gnu, as it tried to link against the .a versions:

lld-link: error: could not open 'libuuid.a': no such file or directory
lld-link: error: could not open 'libMSVCRTD.a': no such file or directory
lld-link: error: could not open 'libOLDNAMES.a': no such file or directory
xtrnc commented 3 years ago

After using mingw to build I thought why not use zig to build, it's one of it's use cases after all and I had some issues using the ones built with mvsc/mingw. This is the build file:

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

pub fn build(b: *std.build.Builder) void {
    const lib = b.addStaticLibrary("glfw3", null);

    // modify the paths here with the ones from the https://github.com/glfw/glfw repository
    lib.addIncludeDir("F:/Development/glfw/include");
    const src_dir = "F:/Development/glfw/src";

    lib.setBuildMode(b.standardReleaseOptions());
    lib.setTarget(b.standardTargetOptions(.{}));
    lib.linkLibC();

    const c_flags = [_][]const u8{
        // when compiling this lib in debug mode, it seems to add -fstack-protector so if you want to link it
        // with an exe built with -Dtarget=x86_64-windows-msvc you need the line below or you'll get undefined symbols
        "-fno-stack-protector",

        // don't want to add some functions (__mingw_vsscanf etc.), also needed for building exe with msvc abi
        "-D_STDIO_DEFINED",

        // we're compiling for windows (https://github.com/glfw/glfw/blob/076bfd55be45e7ba5c887d4b32aa03d26881a1fb/src/glfw_config.h.in#L40) _GLFW_USE_CONFIG_H is used to get this define in cmake
        "-D_GLFW_WIN32",
        // added to windows builds (https://github.com/glfw/glfw/blob/076bfd55be45e7ba5c887d4b32aa03d26881a1fb/src/CMakeLists.txt#L144)
        "-D_UNICODE",
        "-DUNICODE",
    };

    if (lib.build_mode != .Debug) {
        lib.strip = true;
    }

    const src_files = [_][]const u8{
        // .c files for all targets (https://github.com/glfw/glfw/blob/076bfd55be45e7ba5c887d4b32aa03d26881a1fb/src/CMakeLists.txt#L4)
        "context.c",
        "init.c",
        "input.c",
        "monitor.c",
        "vulkan.c",
        "window.c",
        // .c files for windows build (https://github.com/glfw/glfw/blob/076bfd55be45e7ba5c887d4b32aa03d26881a1fb/src/CMakeLists.txt#L14)
        "win32_init.c",
        "win32_joystick.c",
        "win32_monitor.c",
        "win32_time.c",
        "win32_thread.c",
        "win32_window.c",
        "wgl_context.c",
        "egl_context.c",
        "osmesa_context.c",
    };

    inline for (src_files) |src| {
        lib.addCSourceFile(src_dir ++ "/" ++ src, &c_flags);
    }

    lib.install();
}

You only need zig and the GLFW repository. Runzig init-lib, copy this in build.zig and set your GLFW src and include paths. No other files are needed. It should also work with the MSVC abi, though I'd recommend leaving the default.

I was able to use the above steps to successfully build with both x86_64-windows-gnu and x86_64-windows-msvc using a libglfw3.a built with MinGW.

The only change is that for -gnu I needed to add exe.linkLibCpp();. Otherwise it failed to find __cxa_guard_acquire, __cxa_guard_release, __cxa_guard_release, _Unwind_Resume, __gxx_personality_seh0, __cxa_begin_catch, and std::terminate().

I wasn't able to link a MSVC-built glfw3 (with either /MD or /MT) using x86_64-windows-gnu, as it tried to link against the .a versions:

lld-link: error: could not open 'libuuid.a': no such file or directory
lld-link: error: could not open 'libMSVCRTD.a': no such file or directory
lld-link: error: could not open 'libOLDNAMES.a': no such file or directory

Yes, you also should link libc.

I'd recommend the lib built with zig (and to not waste time with issues like missing symbols avoid the msvc abi and use the default everywhere).

kcbanner commented 3 years ago

Ah, that sounds simpler! Just build with zig!

kcbanner commented 3 years ago

I'm going to leave a note here unless anyone else bumps into this using the above directions: I found linking with the Debug version glfw.a caused me to not have stack traces when errors were returned, it was like there were no debug symbols. Linking with Release instead solved the issue.