2024-05-29 20:27:02 +02:00
|
|
|
const builtin = @import("builtin");
|
|
|
|
|
2023-04-19 01:25:35 +02:00
|
|
|
const std = @import("std");
|
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
const ImportList = std.ArrayList(struct {
|
|
|
|
name: []const u8,
|
|
|
|
module: *std.Build.Module
|
|
|
|
});
|
|
|
|
|
|
|
|
const Project = struct {
|
|
|
|
target: std.Build.ResolvedTarget,
|
|
|
|
optimize: std.builtin.OptimizeMode,
|
|
|
|
imports: ImportList,
|
|
|
|
|
|
|
|
pub fn find_demos(self: Project, b: *std.Build) !void {
|
|
|
|
const demos = b.step("demos", "Build demos");
|
|
|
|
|
|
|
|
var dir = try std.fs.cwd().openDir("demos/", .{
|
|
|
|
.iterate = true,
|
|
|
|
});
|
|
|
|
|
|
|
|
defer {
|
|
|
|
dir.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
var entries = try dir.walk(b.allocator);
|
|
|
|
|
|
|
|
defer {
|
|
|
|
entries.deinit();
|
|
|
|
}
|
|
|
|
|
|
|
|
while (try entries.next()) |entry| {
|
|
|
|
if (entry.kind != .file or !std.mem.endsWith(u8, entry.path, ".zig")) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const source_path = try sub_path(.{"demos", entry.basename});
|
|
|
|
var path_buffer = [_:0]u8{0} ** 255;
|
|
|
|
|
|
|
|
const demo = b.addExecutable(.{
|
|
|
|
.name = try std.fmt.bufPrint(&path_buffer, "{s}.out", .{std.fs.path.stem(entry.basename)}),
|
|
|
|
.root_source_file = b.path(source_path.bytes()),
|
|
|
|
.target = self.target,
|
|
|
|
.optimize = self.optimize,
|
|
|
|
});
|
|
|
|
|
|
|
|
for (self.imports.items) |import| {
|
|
|
|
demo.root_module.addImport(import.name, import.module);
|
|
|
|
}
|
|
|
|
|
|
|
|
demos.dependOn(&b.addInstallArtifact(demo, .{
|
|
|
|
.dest_dir = .{
|
|
|
|
.override = .{
|
|
|
|
.custom = "../demos/",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}).step);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn find_tests(self: Project, b: *std.Build) !void {
|
2024-07-24 01:56:14 +02:00
|
|
|
const tests = b.step("tests", "Build and run tests");
|
2024-07-24 01:25:18 +02:00
|
|
|
|
|
|
|
for (self.imports.items) |import| {
|
|
|
|
tests.dependOn(&b.addRunArtifact(b.addTest(.{
|
|
|
|
.root_source_file = import.module.root_source_file.?,
|
|
|
|
.target = self.target,
|
|
|
|
.optimize = self.optimize,
|
|
|
|
})).step);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_module(self: *Project, b: *std.Build, comptime name: []const u8, options: std.Build.Module.CreateOptions) !*std.Build.Module {
|
|
|
|
const cwd = std.fs.cwd();
|
|
|
|
var corrected_options = options;
|
|
|
|
|
|
|
|
if (corrected_options.root_source_file == null) {
|
|
|
|
corrected_options.root_source_file = b.path("src/" ++ name ++ "/" ++ name ++ ".zig");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (corrected_options.target == null) {
|
|
|
|
corrected_options.target = self.target;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (corrected_options.optimize == null) {
|
|
|
|
corrected_options.optimize = self.optimize;
|
|
|
|
}
|
|
|
|
|
|
|
|
const module = b.addModule(name, corrected_options);
|
|
|
|
|
|
|
|
try self.imports.append(.{
|
|
|
|
.name = name,
|
|
|
|
.module = module
|
|
|
|
});
|
|
|
|
|
|
|
|
// TODO: Probably want to make it search the same path as any explicit root_source_path override for shaders.
|
|
|
|
const shaders_path = "src/" ++ name ++ "/shaders/";
|
|
|
|
|
|
|
|
var shaders_dir = cwd.openDir(shaders_path, .{.iterate = true}) catch |open_error| {
|
|
|
|
return switch (open_error) {
|
|
|
|
error.FileNotFound, error.NotDir => module,
|
|
|
|
else => open_error,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
defer {
|
|
|
|
shaders_dir.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
var shaders_entries = try shaders_dir.walk(b.allocator);
|
|
|
|
|
|
|
|
defer {
|
|
|
|
shaders_entries.deinit();
|
|
|
|
}
|
|
|
|
|
|
|
|
while (try shaders_entries.next()) |entry| {
|
|
|
|
if (entry.kind != .file) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const is_shader_file = std.mem.endsWith(u8, entry.path, ".frag") or std.mem.endsWith(u8, entry.path, ".vert");
|
|
|
|
|
|
|
|
if (!is_shader_file) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var binary_buffer = [_:0]u8{0} ** 255;
|
|
|
|
const binary_name = try std.fmt.bufPrint(&binary_buffer, "{s}.spv", .{entry.path});
|
|
|
|
const full_source_path = try sub_path(.{shaders_path, entry.path});
|
|
|
|
const full_binary_path = try sub_path(.{shaders_path, binary_name});
|
|
|
|
|
|
|
|
const glslang_validator_args = [_][]const u8{
|
|
|
|
"glslangValidator",
|
|
|
|
"-V",
|
|
|
|
full_source_path.bytes(),
|
|
|
|
"-o",
|
|
|
|
full_binary_path.bytes(),
|
|
|
|
};
|
|
|
|
|
|
|
|
shaders_dir.access(binary_name, .{.mode = .read_only}) catch {
|
|
|
|
const output = b.run(&glslang_validator_args);
|
|
|
|
|
|
|
|
std.log.info("{s}", .{output[0 .. output.len - 1]});
|
|
|
|
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
if ((try shaders_dir.statFile(entry.path)).mtime > (try shaders_dir.statFile(binary_name)).mtime) {
|
|
|
|
const output = b.run(&glslang_validator_args);
|
|
|
|
|
|
|
|
std.log.info("{s}", .{output[0 .. output.len - 1]});
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return module;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-07-06 18:42:59 +02:00
|
|
|
const SubPath = struct {
|
|
|
|
buffer: [max]u8 = [_]u8{0} ** max,
|
|
|
|
unused: u8 = max,
|
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
pub const max = 255;
|
2024-07-06 18:42:59 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
pub fn append(self: *SubPath, component: []const u8) !void {
|
|
|
|
const used = max - self.unused;
|
2024-07-06 18:42:59 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
if (used != 0 and self.buffer[used - 1] != '/') {
|
|
|
|
if (component.len > self.unused) {
|
|
|
|
return error.PathTooBig;
|
|
|
|
}
|
2023-07-10 02:10:56 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
@memcpy(self.buffer[used .. (used + component.len)], component);
|
2024-07-03 00:08:25 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
self.unused -= @intCast(component.len);
|
|
|
|
} else {
|
|
|
|
const required_len = component.len + 1;
|
2023-04-19 01:25:35 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
if (required_len > self.unused) {
|
|
|
|
return error.PathTooBig;
|
|
|
|
}
|
2024-07-03 00:08:25 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
@memcpy(self.buffer[used .. (used + component.len)], component);
|
|
|
|
|
|
|
|
self.buffer[component.len] = '/';
|
|
|
|
|
|
|
|
self.unused -= @intCast(required_len);
|
2024-07-03 00:08:25 +02:00
|
|
|
}
|
2024-07-24 01:25:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn bytes(self: *const SubPath) [:0]const u8 {
|
|
|
|
return @ptrCast(self.buffer[0 .. max - self.unused]);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn build(b: *std.Build) !void {
|
|
|
|
var project = Project{
|
|
|
|
.imports = ImportList.init(b.allocator),
|
|
|
|
.target = b.standardTargetOptions(.{}),
|
|
|
|
.optimize = b.standardOptimizeOption(.{}),
|
|
|
|
};
|
|
|
|
|
|
|
|
const sokol_dependency = b.dependency("sokol", .{
|
|
|
|
.target = project.target,
|
|
|
|
.optimize = project.optimize,
|
2024-07-03 00:08:25 +02:00
|
|
|
});
|
2023-04-19 01:25:35 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
const ona_module = try project.add_module(b, "ona", .{});
|
2024-07-25 02:11:58 +02:00
|
|
|
|
|
|
|
const hid_module = try project.add_module(b, "hid", .{
|
|
|
|
.imports = &.{
|
|
|
|
.{
|
|
|
|
.name = "ona",
|
|
|
|
.module = ona_module,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2023-04-19 01:25:35 +02:00
|
|
|
|
2024-07-24 23:09:27 +02:00
|
|
|
const gfx_module = try project.add_module(b, "gfx", .{
|
2024-07-03 00:08:25 +02:00
|
|
|
.imports = &.{
|
|
|
|
.{
|
|
|
|
.name = "sokol",
|
|
|
|
.module = sokol_dependency.module("sokol"),
|
|
|
|
},
|
2024-05-29 20:27:02 +02:00
|
|
|
|
2024-07-03 00:08:25 +02:00
|
|
|
.{
|
2024-07-24 01:25:18 +02:00
|
|
|
.name = "ona",
|
|
|
|
.module = ona_module,
|
2024-05-29 20:27:02 +02:00
|
|
|
},
|
|
|
|
|
2024-07-03 00:08:25 +02:00
|
|
|
.{
|
2024-07-25 02:11:58 +02:00
|
|
|
.name = "hid",
|
2024-07-24 23:09:27 +02:00
|
|
|
.module = hid_module,
|
2024-07-03 00:08:25 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2024-07-24 23:09:27 +02:00
|
|
|
gfx_module.addIncludePath(b.path("ext/"));
|
2023-07-11 00:44:27 +02:00
|
|
|
|
2024-07-24 23:09:27 +02:00
|
|
|
gfx_module.linkLibrary(spirv_cross: {
|
2024-07-06 18:42:59 +02:00
|
|
|
const dir = "ext/spirv-cross/";
|
|
|
|
|
|
|
|
const sources = [_][]const u8{
|
|
|
|
"spirv_cross.cpp",
|
|
|
|
"spirv_parser.cpp",
|
|
|
|
"spirv_cross_parsed_ir.cpp",
|
|
|
|
"spirv_cfg.cpp",
|
|
|
|
"spirv_glsl.cpp",
|
|
|
|
"spirv_msl.cpp",
|
|
|
|
"spirv_hlsl.cpp",
|
|
|
|
"spirv_reflect.cpp",
|
|
|
|
"spirv_cross_util.cpp",
|
|
|
|
"spirv_cross_c.cpp",
|
|
|
|
};
|
|
|
|
|
|
|
|
const lib = b.addStaticLibrary(.{
|
|
|
|
.name = "spirvcross",
|
2024-07-24 01:25:18 +02:00
|
|
|
.target = project.target,
|
|
|
|
.optimize = project.optimize,
|
2024-07-06 18:42:59 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
switch (lib.rootModuleTarget().abi) {
|
|
|
|
.msvc => lib.linkLibC(),
|
|
|
|
else => lib.linkLibCpp(),
|
|
|
|
}
|
|
|
|
|
|
|
|
inline for (sources) |src| {
|
|
|
|
lib.addCSourceFile(.{
|
|
|
|
.file = b.path(dir ++ src),
|
2024-07-24 01:25:18 +02:00
|
|
|
|
|
|
|
.flags = &.{
|
|
|
|
"-fstrict-aliasing",
|
|
|
|
"-DSPIRV_CROSS_C_API_GLSL",
|
|
|
|
"-DSPIRV_CROSS_C_API_HLSL",
|
|
|
|
"-DSPIRV_CROSS_C_API_MSL",
|
|
|
|
},
|
2024-07-06 18:42:59 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
break: spirv_cross lib;
|
|
|
|
});
|
|
|
|
|
2024-07-24 23:09:27 +02:00
|
|
|
gfx_module.linkSystemLibrary("SDL2", .{
|
2024-07-24 01:25:18 +02:00
|
|
|
.needed = true,
|
|
|
|
.preferred_link_mode = .dynamic,
|
2023-07-10 02:10:56 +02:00
|
|
|
});
|
2024-05-29 20:27:02 +02:00
|
|
|
|
2024-07-24 23:09:27 +02:00
|
|
|
gfx_module.link_libc = true;
|
2024-07-06 18:42:59 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
try project.find_tests(b);
|
|
|
|
try project.find_demos(b);
|
|
|
|
}
|
2024-07-06 18:42:59 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
fn sub_path(components: anytype) !SubPath {
|
|
|
|
var path = comptime try std.BoundedArray(u8, SubPath.max).init(0);
|
|
|
|
const Components = @TypeOf(components);
|
2024-07-06 18:42:59 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
switch (@typeInfo(Components)) {
|
|
|
|
.Struct => |@"struct"| {
|
|
|
|
if (!@"struct".is_tuple) {
|
|
|
|
@compileError("`components` must be a tuple");
|
2024-07-06 18:42:59 +02:00
|
|
|
}
|
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
const last_component_index = components.len - 1;
|
2024-07-06 18:42:59 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
inline for (components, 0 .. components.len) |component, i| {
|
|
|
|
path.appendSlice(component) catch {
|
|
|
|
return error.PathTooBig;
|
|
|
|
};
|
2024-07-06 18:42:59 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
if (i < last_component_index and !std.mem.endsWith(u8, component, "/")) {
|
|
|
|
path.append('/') catch {
|
|
|
|
return error.PathTooBig;
|
|
|
|
};
|
2024-07-06 18:42:59 +02:00
|
|
|
}
|
|
|
|
}
|
2024-07-24 01:25:18 +02:00
|
|
|
},
|
2024-07-06 18:42:59 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
else => {
|
|
|
|
@compileError("`components` cannot be a " ++ @typeName(Components));
|
2024-07-06 18:42:59 +02:00
|
|
|
}
|
2024-07-24 01:25:18 +02:00
|
|
|
}
|
2024-07-06 18:42:59 +02:00
|
|
|
|
2024-07-24 01:25:18 +02:00
|
|
|
return .{
|
|
|
|
.unused = SubPath.max - path.len,
|
|
|
|
.buffer = path.buffer,
|
|
|
|
};
|
2023-04-19 01:25:35 +02:00
|
|
|
}
|