Not-Nik / raylib-zig

Manually tweaked, auto-generated raylib bindings for zig. https://github.com/raysan5/raylib
MIT License
459 stars 101 forks source link

modified build.zig can build it now #71

Closed Angluca closed 3 months ago

Angluca commented 4 months ago

I'm a newbie use new zig dev version 0.12.3xxx I don't know emscripten so all comment ... Update raylib-zig please, Like it T_T

Angluca commented 4 months ago

I modified build.zig can compiled successfully

// modified raylib-zig/build.zig
const std = @import("std");
const rl = @This();
const builtin = @import("builtin");

const Program = struct {
    name: []const u8,
    path: []const u8,
    desc: []const u8,
};

pub fn link(
    b: *std.Build,
    exe: *std.Build.Step.Compile,
    target: std.zig.CrossTarget,
    optimize: std.builtin.Mode,
) void {
    const lib = getRaylib(b, target, optimize);

    const target_os = exe.rootModuleTarget().os.tag;
    switch (target_os) {
        .windows => {
            exe.linkSystemLibrary("winmm");
            exe.linkSystemLibrary("gdi32");
            exe.linkSystemLibrary("opengl32");
        },
        .macos => {
            exe.linkFramework("OpenGL");
            exe.linkFramework("Cocoa");
            exe.linkFramework("IOKit");
            exe.linkFramework("CoreAudio");
            exe.linkFramework("CoreVideo");
        },
        .freebsd, .openbsd, .netbsd, .dragonfly => {
            exe.linkSystemLibrary("GL");
            exe.linkSystemLibrary("rt");
            exe.linkSystemLibrary("dl");
            exe.linkSystemLibrary("m");
            exe.linkSystemLibrary("X11");
            exe.linkSystemLibrary("Xrandr");
            exe.linkSystemLibrary("Xinerama");
            exe.linkSystemLibrary("Xi");
            exe.linkSystemLibrary("Xxf86vm");
            exe.linkSystemLibrary("Xcursor");
        },
        .emscripten, .wasi => {
            // When using emscripten, the libries don't need to be linked
            // because emscripten is going to do that later.
        },
        else => { // Linux and possibly others.
            exe.linkSystemLibrary("GL");
            exe.linkSystemLibrary("rt");
            exe.linkSystemLibrary("dl");
            exe.linkSystemLibrary("m");
            exe.linkSystemLibrary("X11");
        },
    }

    exe.linkLibrary(lib);
}

var _raylib_lib_cache: ?*std.Build.Step.Compile = null;
pub fn getRaylib(
    b: *std.Build,
    target: std.zig.CrossTarget,
    optimize: std.builtin.Mode,
) *std.Build.Step.Compile {
    if (_raylib_lib_cache) |lib| return lib else {
        const raylib = b.dependency("raylib", .{
            .target = target,
            .optimize = optimize,
        });

        const lib = raylib.artifact("raylib");
        b.installArtifact(lib);
        _raylib_lib_cache = lib;
        return lib;
    }
}

// TODO: Make these not comptime.
pub fn getModule(b: *std.Build, comptime rl_path: []const u8) *std.Build.Module {
    if (b.modules.contains("raylib")) {
        return b.modules.get("raylib").?;
    }
    return b.addModule("raylib", .{ .root_source_file = .{ .path = rl_path ++ "/lib/raylib-zig.zig" } });
}

fn getModuleInternal(b: *std.Build) *std.Build.Module {
    if (b.modules.contains("raylib")) {
        return b.modules.get("raylib").?;
    }
    return b.addModule("raylib", .{ .root_source_file = .{ .path = "lib/raylib-zig.zig" } });
}

pub const math = struct {
    pub fn getModule(b: *std.Build, comptime rl_path: []const u8) *std.Build.Module {
        const raylib = rl.getModule(b, rl_path);
        return b.addModule("raylib-math", .{
            .root_source_file = .{ .path = rl_path ++ "/lib/raylib-zig-math.zig" },
            .imports = &.{.{ .name = "raylib-zig", .module = raylib }},
        });
    }

    fn getModuleInternal(b: *std.Build) *std.Build.Module {
        const raylib = rl.getModuleInternal(b);
        return b.addModule("raylib-math", .{
            .root_source_file = .{ .path = "lib/raylib-zig-math.zig" },
            .imports = &.{.{ .name = "raylib-zig", .module = raylib }},
        });
    }
};

pub fn build(b: *std.Build) !void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const examples = [_]Program{
        .{
            .name = "basic_window",
            .path = "examples/core/basic_window.zig",
            .desc = "Creates a basic window with text",
        },
        .{
            .name = "input_keys",
            .path = "examples/core/input_keys.zig",
            .desc = "Simple keyboard input",
        },
        .{
            .name = "input_mouse",
            .path = "examples/core/input_mouse.zig",
            .desc = "Simple mouse input",
        },
        .{
            .name = "input_mouse_wheel",
            .path = "examples/core/input_mouse_wheel.zig",
            .desc = "Mouse wheel input",
        },
        .{
            .name = "input_multitouch",
            .path = "examples/core/input_multitouch.zig",
            .desc = "Multitouch input",
        },
        .{
            .name = "2d_camera",
            .path = "examples/core/2d_camera.zig",
            .desc = "Shows the functionality of a 2D camera",
        },
        .{
            .name = "3d_camera_first_person",
            .path = "examples/core/3d_camera_first_person.zig",
            .desc = "Simple first person demo",
        },
        .{
            .name = "texture_outline",
            .path = "examples/shaders/texture_outline.zig",
            .desc = "Uses a shader to create an outline around a sprite",
        },
        .{
            .name = "logo_raylib",
            .path = "examples/shapes/logo_raylib.zig",
            .desc = "Renders the raylib-zig logo",
        },
        .{
            .name = "sprite_anim",
            .path = "examples/textures/sprite_anim.zig",
            .desc = "Animate a sprite",
        },
        // .{
        //     .name = "models_loading",
        //     .path = "examples/models/models_loading.zig",
        //     .desc = "Loads a model and renders it",
        // },
        // .{
        //     .name = "shaders_basic_lighting",
        //     .path = "examples/shaders/shaders_basic_lighting.zig",
        //     .desc = "Loads a model and renders it",
        // },
    };

    const examples_step = b.step("examples", "Builds all the examples");

    const raylib = rl.getModuleInternal(b);
    const raylib_math = rl.math.getModuleInternal(b);

    //_ = &raylib; _ = &raylib_math;
    for (examples) |ex| {
        if (target.result.os.tag == .emscripten) {
            //const exe_lib = compileForEmscripten(b, ex.name, ex.path, target.query, optimize);
            //exe_lib.addModule("raylib", raylib);
            //exe_lib.addModule("raylib-math", raylib_math);
            //const raylib_lib = getRaylib(b, target, optimize);

            //// Note that raylib itself isn't actually added to the exe_lib
            //// output file, so it also needs to be linked with emscripten.
            //exe_lib.linkLibrary(raylib_lib);
            //const link_step = try linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_lib });
            //link_step.addArg("--embed-file");
            //link_step.addArg("resources/");

            //const run_step = try emscriptenRunStep(b);
            //run_step.step.dependOn(&link_step.step);
            //const run_option = b.step(ex.name, ex.desc);
            //run_option.dependOn(&run_step.step);
        } else {
            const exe = b.addExecutable(.{
                .name = ex.name,
                .root_source_file = .{ .path = ex.path },
                .optimize = optimize,
                .target = target,
            });

            exe.root_module.addImport("raylib", raylib);
            exe.root_module.addImport("raylib-math", raylib_math);

            rl.link(b, exe, target.query, optimize);
            b.installArtifact(exe);
            const run_cmd = b.addRunArtifact(exe);
            const run_step = b.step(ex.name, ex.desc);
            run_step.dependOn(&run_cmd.step);
            examples_step.dependOn(&exe.step);
        }
    }
}

const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str;
const emccOutputFile = "index.html";
pub fn emscriptenRunStep(b: *std.Build) !*std.Build.Step.Run {
    // Find emrun.
    if (b.sysroot == null) {
        @panic("Pass '--sysroot \"[path to emsdk installation]/upstream/emscripten\"'");
    }
    // If compiling on windows , use emrun.bat.
    const emrunExe = switch (builtin.os.tag) {
        .windows => "emrun.bat",
        else => "emrun",
    };
    const emrun_run_arg = try b.allocator.alloc(u8, b.sysroot.?.len + emrunExe.len + 1);
    defer b.allocator.free(emrun_run_arg);

    _ = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ b.sysroot.?, emrunExe });

    const run_cmd = b.addSystemCommand(&[_][]const u8{ emrun_run_arg, emccOutputDir ++ emccOutputFile });
    return run_cmd;
}

// Creates the static library to build a project for Emscripten.
//pub fn compileForEmscripten(
    //b: *std.Build,
    //name: []const u8,
    //root_source_file: []const u8,
    //target: std.zig.CrossTarget,
    //optimize: std.builtin.Mode,
//) *std.Build.Step.Compile {
    //// TODO: It might be a good idea to create a custom compile step, that does
    //// both the compile to static library and the link with emcc by overidding
    //// the make function of the step. However it might also be a bad idea since
    //// it messes with the build system itself.

    //const new_target = updateTargetForWeb(target);

    //// The project is built as a library and linked later.
    //const exe_lib = b.addStaticLibrary(.{
        //.name = name,
        //.root_source_file = .{ .path = root_source_file },
        //.target = new_target,
        //.optimize = optimize,
    //});

    //// There are some symbols that need to be defined in C.
    //const webhack_c_file_step = b.addWriteFiles();
    //const webhack_c_file = webhack_c_file_step.add("webhack.c", webhack_c);
    //exe_lib.addCSourceFile(.{ .file = webhack_c_file, .flags = &[_][]u8{} });
    //// Since it's creating a static library, the symbols raylib uses to webgl
    //// and glfw don't need to be linked by emscripten yet.
    //exe_lib.step.dependOn(&webhack_c_file_step.step);
    //return exe_lib;
//}

// Links a set of items together using emscripten.
//
// Will accept objects and static libraries as items to link. As for files to
// include, it is recomended to have a single resources directory and just pass
// the entire directory instead of passing every file individually. The entire
// path given will be the path to read the file within the program. So, if
// "resources/image.png" is passed, your program will use "resources/image.png"
// as the path to load the file.
//
// TODO: Test if shared libraries are accepted, I don't remember if emcc can
//       link a shared library with a project or not.
// TODO: Add a parameter that allows a custom output directory.
pub fn linkWithEmscripten(
    b: *std.Build,
    itemsToLink: []const *std.Build.Step.Compile,
) !*std.Build.Step.Run {
    // Raylib uses --sysroot in order to find emscripten, so do the same here.
    if (b.sysroot == null) {
        @panic("Pass '--sysroot \"[path to emsdk installation]/upstream/emscripten\"'");
    }
    const emccExe = switch (builtin.os.tag) {
        .windows => "emcc.bat",
        else => "emcc",
    };
    var emcc_run_arg = try b.allocator.alloc(u8, b.sysroot.?.len + emccExe.len + 1);
    defer b.allocator.free(emcc_run_arg);

    emcc_run_arg = try std.fmt.bufPrint(
        emcc_run_arg,
        "{s}" ++ std.fs.path.sep_str ++ "{s}",
        .{ b.sysroot.?, emccExe },
    );

    // Create the output directory because emcc can't do it.
    const mkdir_command = b.addSystemCommand(&[_][]const u8{ "mkdir", "-p", emccOutputDir });

    // Actually link everything together.
    const emcc_command = b.addSystemCommand(&[_][]const u8{emcc_run_arg});

    for (itemsToLink) |item| {
        emcc_command.addFileArg(item.getEmittedBin());
        emcc_command.step.dependOn(&item.step);
    }
    // This puts the file in zig-out/htmlout/index.html.
    emcc_command.step.dependOn(&mkdir_command.step);
    emcc_command.addArgs(&[_][]const u8{
        "-o",
        emccOutputDir ++ emccOutputFile,
        "-sFULL-ES3=1",
        "-sUSE_GLFW=3",
        "-sASYNCIFY",
        "-O3",
        "--emrun",
    });
    return emcc_command;
}

// TODO: See if zig's standard library already has somehing like this.
fn lastIndexOf(string: []const u8, character: u8) usize {
    // Interestingly, Zig has no nice way of iterating a slice backwards.
    for (0..string.len) |i| {
        const index = string.len - i - 1;
        if (string[index] == character) return index;
    }
    return string.len - 1;
}
// TODO: each zig update, remove this and see if everything still works.
// https://github.com/ziglang/zig/issues/16776 is where the issue is submitted.
fn updateTargetForWeb(target: std.zig.CrossTarget) std.zig.CrossTarget {
    // Zig building to emscripten doesn't work, because the Zig standard library
    // is missing some things in the C system. "std/c.zig" is missing fd_t,
    // which causes compilation to fail. So build to wasi instead, until it gets
    // fixed.
    return std.zig.CrossTarget{
        .cpu_arch = target.cpu_arch,
        .cpu_model = target.cpu_model,
        .cpu_features_add = target.cpu_features_add,
        .cpu_features_sub = target.cpu_features_sub,
        .os_tag = .wasi,
        .os_version_min = target.os_version_min,
        .os_version_max = target.os_version_max,
        .glibc_version = target.glibc_version,
        .abi = target.abi,
        .dynamic_linker = target.dynamic_linker,
        .ofmt = target.ofmt,
    };
}
const webhack_c =
    \\// Zig adds '__stack_chk_guard', '__stack_chk_fail', and 'errno',
    \\// which emscripten doesn't actually support.
    \\// Seems that zig ignores disabling stack checking,
    \\// and I honestly don't know why emscripten doesn't have errno.
    \\// TODO: when the updateTargetForWeb workaround gets removed, see if those are nessesary anymore
    \\#include <stdint.h>
    \\uintptr_t __stack_chk_guard;
    \\//I'm not certain if this means buffer overflows won't be detected,
    \\// However, zig is pretty safe from those, so don't worry about it too much.
    \\void __stack_chk_fail(void){}
    \\int errno;
;
Angluca commented 4 months ago

My project can use it now!

// user project build.zig
const std = @import("std");
const rl = @import("raylib-zig/build.zig");

pub fn build(b: *std.Build) !void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    //const raylib = rl.getModule(b, "raylib-zig");
    //var raylib_math = rl.math.getModule(b, "raylib-zig");
    //web exports are completely separate
    //if (target.getOsTag() == .emscripten) {
        //const exe_lib = rl.compileForEmscripten(b, "'$PROJECT_NAME'", "src/main.zig", target, optimize);
        //exe_lib.addModule("raylib", raylib);
        //exe_lib.addModule("raylib-math", raylib_math);
        //const raylib_artifact = rl.getRaylib(b, target, optimize);
        //// Note that raylib itself is not actually added to the exe_lib output file, so it also needs to be linked with emscripten.
        //exe_lib.linkLibrary(raylib_artifact);
        //const link_step = try rl.linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact });
        //b.getInstallStep().dependOn(&link_step.step);
        //const run_step = try rl.emscriptenRunStep(b);
        //run_step.step.dependOn(&link_step.step);
        //const run_option = b.step("run", "Run '$PROJECT_NAME'");
        //run_option.dependOn(&run_step.step);
        //return;
    //}

    const exe = b.addExecutable(.{ .name = "myapp", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target });

    const raylib = rl.getModule(b, "./raylib-zig");
    const raylib_math = rl.math.getModule(b, "./raylib-zig");

    exe.root_module.addImport("raylib", raylib);
    exe.root_module.addImport("raylib-math", raylib_math);

    rl.link(b, exe, target.query, optimize);

    const run_cmd = b.addRunArtifact(exe);
    const run_step = b.step("run", "Run '$PROJECT_NAME'");
    run_step.dependOn(&run_cmd.step);

    b.installArtifact(exe);
}
Ev1lT3rm1nal commented 3 months ago

@Angluca close it