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

Use Zig package manager #38

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

56 changes: 36 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,44 @@ For [raygui](https://github.com/raysan5/raygui) bindings see: https://github.com

## <a id="usage">usage</a>

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/kapricorn-media/raylib.zig/archive/7c882c229e8dc4fb6168e9a1cfed4ab1df31f40f.tar.gz",
.hash = "1220141e38f5d075c77a9d4fbd5c2b622ed1f0207ff2bdfc67af3488609e5067289b",
},
},
}
```

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

Expand All @@ -63,7 +81,7 @@ pub fn main() void {
while (!raylib.WindowShouldClose()) {
raylib.BeginDrawing();
defer raylib.EndDrawing();

raylib.ClearBackground(raylib.BLACK);
raylib.DrawFPS(10, 10);

Expand All @@ -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 <emscripten-sdk-path>/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`.
Expand Down
217 changes: 138 additions & 79 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,26 +1,111 @@
const std = @import("std");
const builtin = @import("builtin");

const generate = @import("generate.zig");

pub fn build(b: *std.Build) !void {
const raylibSrc = "raylib/src/";
const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str;
const emccOutputFile = "index.html";

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 this_builder = raylib_zig.builder;
const raylib = this_builder.dependency("raylib", .{
.target = options.target,
.optimize = options.optimize,
});

const module = app_builder.createModule(.{
.root_source_file = raylib_zig.path("raylib.zig"),
.target = options.target,
.optimize = options.optimize,
});
module.addIncludePath(.{ .path = try this_builder.build_root.join(app_builder.allocator, &.{"."}) });
module.addIncludePath(raylib.path("src"));
module.addCSourceFiles(.{ .files = &.{
raylib_zig.path("marshal.c").getPath(this_builder),
}, .flags = &.{} });
module.link_libc = true;

var compile: *std.Build.Step.Compile = undefined;
switch (options.target.result.os.tag) {
.emscripten => {
if (app_builder.sysroot == null) {
@panic("Pass '--sysroot \"$EMSDK/upstream/emscripten\"'");
}
const cache_include = try std.fs.path.join(app_builder.allocator, &.{ app_builder.sysroot.?, "cache", "sysroot", "include" });
module.addIncludePath(.{ .path = cache_include });

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", module);

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

//--- 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",
Expand All @@ -29,8 +114,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",
Expand All @@ -39,8 +125,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",
Expand All @@ -51,7 +138,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);
Expand All @@ -60,7 +147,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} });
Expand All @@ -73,77 +160,49 @@ 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 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 {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I snatched these emscripten utility functions from https://github.com/Not-Nik/raylib-zig/blob/devel/build.zig

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;
}
11 changes: 11 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -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",
},
},
}
Loading