GLFW and OpenGL in Zig on Windows

Jun 27, 2020

I ran into some issues getting a sample OpenGL application with GLFW working with Zig on Windows, so I figured I’d write a short post outlining what’s needed to get it working.

Build GLFW

Download GLFW and build it from source. The pre-built windows static libraries available for download are compiled with a flag that is incompatible with the way Zig builds libraries. I discovered this by opening an issue on the Zig repo.

After you’ve cloned the source from https://github.com/glfw/glfw, build it using CMake. If you don’t know how to do that using Visual Studio, here’s what I did:

  1. Open a Visual Studio developer tools terminal
  2. cd to the directory where you cloned glfw
  3. mkdir build && cd build
  4. cmake .. -DUSE_MSVC_RUNTIME_LIBRARY_DLL=OFF
  5. Open the generated GLFW.sln in Visual Studio and build it. I set the Configuration to Release. The static library glfw3.lib was generated at build/src/Release/glfw3.lib.

Build Zig using GLFW

In your build.zig, add the GLFW includes and lib folder:

exe.addIncludeDir("/path/to/glfw/include");
exe.addLibPath("/path/to/glfw/build/src/Release");

Link against the following system libraries:

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

After all these steps, linking should succeed. Here’s a complete trivial sample that also includes the OpenGL lib:

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("/path/to/glfw/include");
    exe.addLibPath("/path/to/glfw/build/src/Release");

    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);
}

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;
}