Backlog/build.zig

235 lines
8.5 KiB
Zig

// MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 -- use this if you're getting that odd crash on mac
b: *std.Build,
bEngine: *std.Build, // used to resolve executable paths from within the engine's root directory
opts: bh.Options,
nw_mod: *std.Build.Module,
gltf2ozz: ozz.GltfToOzz,
options: *std.Build.Step.Options,
cookShaders: bool,
backlogRoot: []const u8,
// list of all shaders discovered under
// content/_shaders/def
reflectShaderPathList: [][]u8 = undefined,
nwdep: *std.Build.Dependency,
// apigen: *std.Build.Step.Compile,
shaderEmbedGen: *std.Build.Step.Compile,
loadDynamicsGen: *std.Build.Step.Compile,
rcGen: *std.Build.Step.Compile,
specGen: *std.Build.Step.Compile,
generatedApis: std.StringHashMapUnmanaged(*std.Build.Module) = .{},
pub const BuildSystem = @This();
const std = @import("std");
const Build = std.Build;
const LazyPath = Build.LazyPath;
const ozz = @import("ozz");
pub const InitOptions = struct {
import_name: []const u8 = "Backlog",
backlogRoot: []const u8 = "./BacklogEngine",
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
};
pub fn init(b: *std.Build, initOptions: InitOptions) BuildSystem {
const buildOpts = declareBuildOptions(b);
const opts = bh.Options{
.target = initOptions.target,
.optimize = initOptions.optimize,
.static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false,
.tracy = b.option(bool, "tracy", "builds with tracy support") orelse false,
};
if (opts.target.result.os.tag == .linux) {
// using a static build for linux... object loading hell is not fun...
// i have skill issues in that realm right now
// std.debug.print("Important! only supporting static builds for linux", .{});
// opts.static_build = true;
}
const nwdep = b.dependency(initOptions.import_name, .{
.target = opts.target,
.optimize = opts.optimize,
.static_build = opts.static_build,
});
var self = BuildSystem{
.b = b,
.bEngine = nwdep.builder,
.opts = opts,
.nw_mod = nwdep.module("Backlog"),
.backlogRoot = initOptions.backlogRoot,
.options = createGameOptions(b, buildOpts, opts),
.gltf2ozz = ozz.GltfToOzz.init(nwdep.builder, .{}),
.cookShaders = b.option(bool, "cookShaders", "generates shaders and updates .json files before running the build. (needs to be done whenever shaders are updated, this just runs tools/scripts/cook-shaders.py)") orelse false,
.nwdep = nwdep,
// .apigen = nwdep.artifact("backlog-apigen"),
.loadDynamicsGen = nwdep.artifact("backlog-generate-loadDynamics"),
.specGen = nwdep.artifact("backlog-generate-spec"),
.rcGen = nwdep.artifact("backlog-generate-rc"),
.shaderEmbedGen = nwdep.artifact("backlog-shaderEmbedGen"),
};
const exeList = [1]*std.Build.Step.Compile{self.gltf2ozz.exe};
const install_tools = b.step("tools", "installs tools needed to generate outputs for the engine");
for (exeList) |exe| {
const toolsInstall = b.addInstallArtifact(exe, .{
.dest_dir = .{ .override = .{ .custom = "tools" } },
});
install_tools.dependOn(&toolsInstall.step);
}
{
const runArtifact = b.addRunArtifact(self.gltf2ozz.exe);
if (b.args) |args| {
runArtifact.addArgs(args);
}
const run_exe = b.step("gltf2ozz", "runs the gltf animation converter.");
run_exe.dependOn(&runArtifact.step);
}
self.addDependencyInstalls(self.b, .ReleaseFast);
return self;
}
// Build Options for the engine,
// should generally be written as false = default
// true = enabling something
const BuildOptions = struct {
mutex_job_queue: bool,
zero_logging: bool,
slow_logging: bool,
force_mailbox: bool,
};
fn declareBuildOptions(b: *std.Build) BuildOptions {
return .{
.mutex_job_queue = b.option(bool, "mutex_job_queue", "temporary test, reverts to old mutex based queue behaviour in jobs.zig:JobManager") orelse false,
.zero_logging = b.option(bool, "zero_logging", "disables all logging, only intended for use on job dispatch testing") orelse false,
.slow_logging = b.option(bool, "slow_logging", "Disables buffered logging, takes a hit to performance but gain timing information on logging") orelse false,
.force_mailbox = b.option(bool, "force_mailbox", "forces mailbox mode for present mode. unlocks framerate to irresponsible levels") orelse false,
};
}
fn createGameOptions(b: *std.Build, options: BuildOptions, bhopts: bh.Options) *std.Build.Step.Options {
const opts = b.addOptions();
inline for (@typeInfo(BuildOptions).@"struct".fields) |field| {
opts.addOption(bool, field.name, @field(options, field.name));
}
// forward the options to the build options
opts.addOption(bool, "tracy", bhopts.tracy);
opts.addOption(bool, "static_build", bhopts.static_build);
return opts;
}
pub fn addExtraModule(self: *BuildSystem, mod: *std.Build.Module, moduleName: []const u8) void {
const dep = self.b.dependency(moduleName, .{ .target = self.opts.target, .optimize = self.opts.optimize, .static_build = self.opts.static_build });
mod.addImport(moduleName, dep.module(moduleName));
}
fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
if (self.opts.static_build) {
return;
}
inline for (DynamicDepList) |d| {
b.installArtifact(b.dependency(
d.dep,
.{ .target = self.opts.target, .optimize = optimize, .static_build = self.opts.static_build },
).artifact(d.artifact));
}
}
const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } = &.{
.{ .dep = "sdl3", .artifact = "SDL3" },
.{ .dep = "spng", .artifact = "spng_c" },
.{ .dep = "lua", .artifact = "luac" },
.{ .dep = "miniaudio", .artifact = "miniaudio_c" },
.{ .dep = "zphysics", .artifact = "joltc" },
// .{ .dep = "enet", .artifact = "enet_c" },
.{ .dep = "ozz", .artifact = "ozz_cpp" },
};
pub fn addProgram(
self: *@This(),
name: []const u8,
) *Program {
const p = self.b.allocator.create(Program) catch unreachable;
p.* = .{
.name = name,
.opts = self.opts,
.buildSystem = self,
.allocator = self.b.allocator,
};
return p;
}
pub fn generateRc(self: *@This(), programName: []const u8, iconPath: []const u8) !std.Build.LazyPath {
const run = self.b.addRunArtifact(self.rcGen);
const pfile = self.b.fmt("{s}.rc", .{programName});
const output = run.addOutputFileArg(pfile);
run.addArg(iconPath);
return output;
}
pub fn generateInstallStaticResources(self: *@This(), programName: []const u8, shadersPath: []const u8) !*std.Build.Module {
const run = self.b.addRunArtifact(self.shaderEmbedGen);
const pfile = self.b.fmt("{s}ShaderGen.zig", .{programName});
const output = run.addOutputFileArg(pfile);
run.addArg(shadersPath);
const b = self.b;
const mod = self.b.addModule(pfile, .{
.target = self.opts.target,
.optimize = self.opts.optimize,
.root_source_file = output,
});
mod.addImport("core", self.nwdep.module("core"));
var dir = try std.fs.cwd().openDir("content/_shaders", .{ .iterate = true });
defer dir.close();
var walker = dir.iterate();
while (try walker.next()) |shaderType| {
if (shaderType.kind == .directory) {
var d2 = try dir.openDir(shaderType.name, .{ .iterate = true });
defer d2.close();
var w2 = d2.iterate();
while (try w2.next()) |shaderName| {
const shaderPath = b.fmt("content/_shaders/{s}/{s}", .{ shaderType.name, shaderName.name });
mod.addAnonymousImport(shaderName.name, .{
.root_source_file = b.path(shaderPath),
});
}
}
}
return mod;
}
// ========= standalone build instance =======
// maybe the default should be like an engine launcher/project launcher or something
pub fn build(b: *std.Build) void {
const opts = bh.declareOptions(b);
engineDeps.buildGenerators(b, opts);
}
const bh = @import("bh");
const engineDeps = @import("build/engineDeps.zig");
const program = @import("build/program.zig");
const Program = program.Program;