diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 3ba065a..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "raylib"] - path = raylib - url = https://github.com/raysan5/raylib diff --git a/README.md b/README.md index c0c8870..9c50e48 100644 --- a/README.md +++ b/README.md @@ -26,26 +26,44 @@ For [raygui](https://github.com/raysan5/raygui) bindings see: https://github.com ## usage -The easy way would be adding this as submodule directly in your source folder. -Thats what I do until there is an official package manager for Zig. - -```sh -cd $YOUR_SRC_FOLDER -git submodule add https://github.com/ryupold/raylib.zig raylib -git submodule update --init --recursive +Add this as a dependency to your `build.zig.zon` +```zig +.{ + .name = "example", + .version = "1.0.0", + .paths = ..., + .dependencies = .{ + .raylib_zig = .{ + .url = "https://github.com/ryupold/raylib.zig/archive/.tar.gz", + .hash = "", + }, + }, +} ``` -The bindings have been prebuilt so you just need to add raylib as module - -build.zig: +Then add the following setup to your `build.zig`: ```zig -const raylib = @import("path/to/raylib.zig/build.zig"); +const std = @import("std"); + +const raylib_zig_build = @import("raylib_zig"); -pub fn build(b: *std.Build) !void { +pub fn build(b: *std.Build) !void +{ const target = b.standardTargetOptions(.{}); - const mode = b.standardOptimizeOption(.{}); - const exe = ...; - raylib.addTo(b, exe, target, mode, .{}); + const optimize = b.standardOptimizeOption(.{}); + + const raylib_zig = b.dependency("raylib_zig", .{ + .target = target, + .optimize = optimize, + }); + const compile = try raylib_zig_build.setup(b, raylib_zig, .{ + .name = "example", + .src = "src/main.zig", + .target = target, + .optimize = optimize, + .createRunStep = true, + }); + b.installArtifact(compile); } ``` @@ -63,7 +81,7 @@ pub fn main() void { while (!raylib.WindowShouldClose()) { raylib.BeginDrawing(); defer raylib.EndDrawing(); - + raylib.ClearBackground(raylib.BLACK); raylib.DrawFPS(10, 10); @@ -72,11 +90,9 @@ pub fn main() void { } ``` -### WebGL (emscripten) builds - -For Webassembly builds see [examples-raylib.zig/build.zig](https://github.com/ryupold/examples-raylib.zig/blob/main/build.zig) +### WASM / emscripten builds -This weird workaround with `marshal.h/marshal.c` I actually had to make for Webassembly builds to work, because passing structs as function parameters or returning them cannot be done on the Zig side somehow. If I try it, I get a runtime error "index out of bounds". This happens only in WebAssembly builds. So `marshal.c` must be compiled with `emcc`. See [build.zig](https://github.com/ryupold/examples-raylib.zig/blob/main/build.zig) in the examples. +Download and install the [emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html), and then add the flags `-Dtarget=wasm32-emscripten` and `--sysroot /upstream/emscripten` to the zig build. ## custom definitions An easy way to fix binding mistakes is to edit them in `bindings.json` and setting the custom flag to true. This way the binding will not be overriden when calling `zig build intermediate`. diff --git a/build.zig b/build.zig index 8eb0cca..a0d431d 100644 --- a/build.zig +++ b/build.zig @@ -1,26 +1,109 @@ const std = @import("std"); +const builtin = @import("builtin"); + const generate = @import("generate.zig"); -pub fn build(b: *std.Build) !void { - const raylibSrc = "raylib/src/"; +pub fn setup(app_builder: *std.Build, raylib_zig: *std.Build.Dependency, options: struct { + name: []const u8, + src: []const u8, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + createRunStep: bool, +}) !*std.Build.Step.Compile { + const raylib = raylib_zig.builder.dependency("raylib", .{ + .target = options.target, + .optimize = options.optimize, + }); + + var compile: *std.Build.Step.Compile = undefined; + switch (options.target.result.os.tag) { + .emscripten => { + compile = app_builder.addStaticLibrary(.{ + .name = options.name, + .root_source_file = .{ .path = options.src }, + .target = options.target, + .optimize = options.optimize, + }); + const link_step = try linkWithEmscripten(app_builder, &.{ compile, raylib.artifact("raylib") }); + app_builder.getInstallStep().dependOn(&link_step.step); + + if (options.createRunStep) { + const run_step = try emscriptenRunStep(app_builder); + run_step.step.dependOn(&link_step.step); + const run_option = app_builder.step("run", "Run"); + run_option.dependOn(&run_step.step); + } + }, + else => { + compile = app_builder.addExecutable(.{ + .name = options.name, + .root_source_file = .{ .path = options.src }, + .target = options.target, + .optimize = options.optimize, + }); + compile.linkLibrary(raylib.artifact("raylib")); + + if (options.createRunStep) { + const run_cmd = app_builder.addRunArtifact(compile); + run_cmd.step.dependOn(app_builder.getInstallStep()); + if (app_builder.args) |args| { + run_cmd.addArgs(args); + } + const run_option = app_builder.step("run", "Run"); + run_option.dependOn(&run_cmd.step); + } + }, + } + compile.root_module.addImport("raylib", raylib_zig.module("raylib")); + return compile; +} + +pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const raylib = b.dependency("raylib", .{ + .target = target, + .optimize = optimize, + }); + + // Add raylib module for use in dependencies. + const module = b.addModule("raylib", .{ + .root_source_file = .{ .path = "raylib.zig" }, + .target = target, + .optimize = optimize, + }); + module.addIncludePath(.{ .path = "." }); + module.addIncludePath(raylib.path("src")); + // TODO: relative path doesn't work here when used as a dependency, not sure why... + const marshal_c_path = try b.build_root.join(b.allocator, &.{"marshal.c"}); + module.addCSourceFile(.{ .file = .{.path = marshal_c_path}, .flags = &.{} }); + module.link_libc = true; + if (target.result.os.tag == .emscripten) { + if (b.sysroot == null) { + @panic("Pass '--sysroot \"$EMSDK/upstream/emscripten\"'"); + } + const emscripten_include_path = try std.fs.path.join(b.allocator, &.{ b.sysroot.?, "cache", "sysroot", "include" }); + module.addIncludePath(.{ .path = emscripten_include_path }); + } //--- parse raylib and generate JSONs for all signatures -------------------------------------- const jsons = b.step("parse", "parse raylib headers and generate raylib jsons"); const raylib_parser_build = b.addExecutable(.{ .name = "raylib_parser", - .root_source_file = std.build.FileSource.relative("raylib_parser.zig"), + .root_source_file = .{ .path = "raylib_parser.zig" }, .target = target, - .optimize = .ReleaseFast, + .optimize = optimize, }); - raylib_parser_build.addCSourceFile(.{ .file = .{ .path = "raylib/parser/raylib_parser.c" }, .flags = &.{} }); + raylib_parser_build.addCSourceFile(.{ .file = raylib.path("parser/raylib_parser.c"), .flags = &.{} }); raylib_parser_build.linkLibC(); //raylib const raylib_H = b.addRunArtifact(raylib_parser_build); + const path_raylib_H = try b.allocator.dupe(u8, raylib.path("src/raylib.h").getPath(b)); raylib_H.addArgs(&.{ - "-i", raylibSrc ++ "raylib.h", + "-i", path_raylib_H, "-o", "raylib.json", "-f", "JSON", "-d", "RLAPI", @@ -29,8 +112,9 @@ pub fn build(b: *std.Build) !void { //raymath const raymath_H = b.addRunArtifact(raylib_parser_build); + const path_raymath_H = try b.allocator.dupe(u8, raylib.path("src/raymath.h").getPath(b)); raymath_H.addArgs(&.{ - "-i", raylibSrc ++ "raymath.h", + "-i", path_raymath_H, "-o", "raymath.json", "-f", "JSON", "-d", "RMAPI", @@ -39,8 +123,9 @@ pub fn build(b: *std.Build) !void { //rlgl const rlgl_H = b.addRunArtifact(raylib_parser_build); + const path_rlgl_H = try b.allocator.dupe(u8, raylib.path("src/rlgl.h").getPath(b)); rlgl_H.addArgs(&.{ - "-i", raylibSrc ++ "rlgl.h", + "-i", path_rlgl_H, "-o", "rlgl.json", "-f", "JSON", "-d", "RLAPI", @@ -51,7 +136,7 @@ pub fn build(b: *std.Build) !void { const intermediate = b.step("intermediate", "generate intermediate representation of the results from 'zig build parse' (keep custom=true)"); var intermediateZigStep = b.addRunArtifact(b.addExecutable(.{ .name = "intermediate", - .root_source_file = std.build.FileSource.relative("intermediate.zig"), + .root_source_file = .{ .path = "intermediate.zig" }, .target = target, })); intermediate.dependOn(&intermediateZigStep.step); @@ -60,7 +145,7 @@ pub fn build(b: *std.Build) !void { const bindings = b.step("bindings", "generate bindings in from bindings.json"); var generateZigStep = b.addRunArtifact(b.addExecutable(.{ .name = "generate", - .root_source_file = std.build.FileSource.relative("generate.zig"), + .root_source_file = .{ .path = "generate.zig" }, .target = target, })); const fmt = b.addFmt(.{ .paths = &.{generate.outputFile} }); @@ -73,77 +158,52 @@ pub fn build(b: *std.Build) !void { raylib_parser_install.dependOn(&generateBindings_install.step); } -// above: generate library -// below: linking (use as dependency) - -fn current_file() []const u8 { - return @src().file; -} +const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str; +const emccOutputFile = "index.html"; -const sep = std.fs.path.sep_str; -const cwd = std.fs.path.dirname(current_file()).?; -const dir_raylib = cwd ++ sep ++ "raylib" ++ sep ++ "src"; - -const raylib_build = @import("raylib/src/build.zig"); - -fn linkThisLibrary(b: *std.Build, target: std.Target.Query, optimize: std.builtin.Mode) *std.Build.Step.Compile { - const lib = b.addStaticLibrary(.{ .name = "raylib.zig", .target = b.resolveTargetQuery(target), .optimize = optimize }); - lib.addIncludePath(.{ .path = dir_raylib }); - lib.addIncludePath(.{ .path = cwd }); - lib.linkLibC(); - lib.addCSourceFile(.{ .file = .{ .path = cwd ++ sep ++ "marshal.c" }, .flags = &.{} }); - std.log.info("include '{s}' to {s}", .{ dir_raylib, lib.name }); - std.log.info("include '{s}' to {s}", .{ cwd, lib.name }); - return lib; -} +fn emscriptenRunStep(b: *std.Build) !*std.Build.Step.Run { + const emrun_exe = switch (builtin.os.tag) { + .windows => "emrun.bat", + else => "emrun", + }; + const emrun_exe_path = try std.fmt.allocPrint(b.allocator, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ b.sysroot.?, emrun_exe }); -/// add this package to exe -pub fn addTo(b: *std.Build, exe: *std.Build.Step.Compile, target: std.Target.Query, optimize: std.builtin.Mode, raylibOptions: raylib_build.Options) void { - exe.root_module.addAnonymousImport("raylib", .{ .root_source_file = .{ .path = cwd ++ sep ++ "raylib.zig" } }); - std.log.info("include '{s}' to {s}", .{ dir_raylib, exe.name }); - std.log.info("include '{s}' to {s}", .{ cwd, exe.name }); - exe.addIncludePath(.{ .path = dir_raylib }); - exe.addIncludePath(.{ .path = cwd }); - const lib = linkThisLibrary(b, target, optimize); - const lib_raylib = raylib_build.addRaylib(b, b.resolveTargetQuery(target), optimize, raylibOptions) catch |err| std.debug.panic("addRaylib: {any}", .{err}); - exe.linkLibrary(lib_raylib); - exe.linkLibrary(lib); - std.log.info("linked raylib.zig", .{}); + const run_cmd = b.addSystemCommand(&[_][]const u8{ emrun_exe_path, emccOutputDir ++ emccOutputFile }); + return run_cmd; } -pub fn linkSystemDependencies(exe: *std.build.Step.Compile) void { - switch (exe.target.getOsTag()) { - .macos => { - exe.linkFramework("Foundation"); - exe.linkFramework("Cocoa"); - exe.linkFramework("OpenGL"); - exe.linkFramework("CoreAudio"); - exe.linkFramework("CoreVideo"); - exe.linkFramework("IOKit"); - }, - .linux => { - exe.addLibraryPath(.{ .path = "/usr/lib" }); - exe.addIncludePath(.{ .path = "/usr/include" }); - exe.linkSystemLibrary("GL"); - exe.linkSystemLibrary("rt"); - exe.linkSystemLibrary("dl"); - exe.linkSystemLibrary("m"); - exe.linkSystemLibrary("X11"); - }, - .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"); - }, - else => {}, +fn linkWithEmscripten( + b: *std.Build, + itemsToLink: []const *std.Build.Step.Compile, +) !*std.Build.Step.Run { + const emcc_exe = switch (builtin.os.tag) { + .windows => "emcc.bat", + else => "emcc", + }; + const emcc_exe_path = try std.fmt.allocPrint(b.allocator, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ b.sysroot.?, emcc_exe }); + + // Create the output directory. + try b.build_root.handle.makePath(emccOutputDir); + + // Link everything together with emcc. + // TODO: The build doesn't work on Windows if emc_exe_path and any of the item + // emitted bin paths have spaces. + const emcc_command = switch (builtin.os.tag) { + .windows => b.addSystemCommand(&.{ "cmd", "/C", emcc_exe_path }), + else => b.addSystemCommand(&.{emcc_exe_path}), + }; + for (itemsToLink) |item| { + emcc_command.addFileArg(item.getEmittedBin()); + emcc_command.step.dependOn(&item.step); } - - exe.linkLibC(); + emcc_command.addArgs(&[_][]const u8{ + "-o", + emccOutputDir ++ emccOutputFile, + "-sFULL-ES3=1", + "-sUSE_GLFW=3", + "-sASYNCIFY", + "-O3", + "--emrun", + }); + return emcc_command; } diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..54664b9 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,11 @@ +.{ + .name = "raylib.zig", + .version = "0.0.0", + .paths = .{""}, + .dependencies = .{ + .raylib = .{ + .url = "https://github.com/raysan5/raylib/archive/40f3df5b865eee0cd87a9e4e1347cb04c87841f8.tar.gz", + .hash = "12209bb07b3926d027de330c05d9425eaa52cc1a40eed291628370ba759653d5f961", + }, + }, +} diff --git a/emscripten/entry.c b/emscripten/entry.c deleted file mode 100644 index 245e9e5..0000000 --- a/emscripten/entry.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include - -#include "emscripten/emscripten.h" -#include "raylib.h" - -// Zig compiles C code with -fstack-protector-strong which requires the following two symbols -// which don't seem to be provided by the emscripten toolchain(?) -void *__stack_chk_guard = (void *)0xdeadbeef; -void __stack_chk_fail(void) -{ - exit(1); -} - - -// emsc_main() is the Zig entry function in main.zig -extern int emsc_main(void); - -extern void emsc_set_window_size(int width, int height); - -int main() -{ - return emsc_main(); -} diff --git a/emscripten/minshell.html b/emscripten/minshell.html deleted file mode 100644 index 5095ad8..0000000 --- a/emscripten/minshell.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - demo - - - - - - - - - - - - - {{{ SCRIPT }}} - - - \ No newline at end of file diff --git a/emscripten/shell.html b/emscripten/shell.html deleted file mode 100644 index 3ab8c3a..0000000 --- a/emscripten/shell.html +++ /dev/null @@ -1,328 +0,0 @@ - - - - - - - raylib web game - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - - - - - {{{ SCRIPT }}} - - diff --git a/raylib b/raylib deleted file mode 160000 index 40f3df5..0000000 --- a/raylib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 40f3df5b865eee0cd87a9e4e1347cb04c87841f8