diff --git a/build.zig b/build.zig index 99b565f..de6a0cb 100644 --- a/build.zig +++ b/build.zig @@ -38,7 +38,8 @@ pub fn build(b: *std.Build) void { shaders.add("triangle_frag", "examples/shaders/triangle.frag", .{}); triangle_exe.addModule("shaders", shaders.getModule()); - const triangle_run_cmd = triangle_exe.run(); + const triangle_run_cmd = b.addRunArtifact(triangle_exe); + triangle_run_cmd.condition = .always; triangle_run_cmd.step.dependOn(b.getInstallStep()); const triangle_run_step = b.step("run-triangle", "Run the triangle example"); triangle_run_step.dependOn(&triangle_run_cmd.step); diff --git a/generator/build_integration.zig b/generator/build_integration.zig index 3252a31..8a9c44c 100644 --- a/generator/build_integration.zig +++ b/generator/build_integration.zig @@ -28,10 +28,7 @@ pub const ShaderCompileStep = struct { hasher.update(arg); } for (self.watched_files) |file_path| { - const full_path = std.fs.path.join(b.allocator, &.{ - b.build_root, - file_path, - }) catch unreachable; + const full_path = b.build_root.join(b.allocator, &.{file_path}) catch unreachable; const source = std.fs.cwd().readFileAlloc( b.allocator, @@ -113,10 +110,7 @@ pub const ShaderCompileStep = struct { /// This path can then be used to include the binary into an executable, for example by passing it /// to @embedFile via an additional generated file. pub fn add(self: *ShaderCompileStep, name: []const u8, src: []const u8, options: ShaderOptions) void { - const full_source_path = std.fs.path.join(self.b.allocator, &.{ - self.b.build_root, - src, - }) catch unreachable; + const full_source_path = self.b.build_root.join(self.b.allocator, &.{src}) catch unreachable; self.shaders.append(.{ .name = name, .source_path = full_source_path, @@ -176,9 +170,12 @@ pub const ShaderCompileStep = struct { var shaders_file_contents = std.ArrayList(u8).init(self.b.allocator); const shaders_out = shaders_file_contents.writer(); - const shaders_dir = try std.fs.path.join( + const shaders_dir = try self.b.build_root.join( self.b.allocator, - &.{ self.b.build_root, self.b.cache_root, cache_dir }, + &.{try self.b.cache_root.join( + self.b.allocator, + &.{cache_dir}, + )}, ); try cwd.makePath(shaders_dir); diff --git a/generator/vulkan/build_integration.zig b/generator/vulkan/build_integration.zig index 6beab76..1a05e68 100644 --- a/generator/vulkan/build_integration.zig +++ b/generator/vulkan/build_integration.zig @@ -11,31 +11,25 @@ const Step = Build.Step; pub const GenerateStep = struct { step: Step, builder: *Build, - + generated_file: std.build.GeneratedFile, + /// Name of the resulting file + output_name: []const u8, /// The path to vk.xml spec_path: []const u8, - generated_file: std.build.GeneratedFile, - /// Initialize a Vulkan generation step, for `builder`. `spec_path` is the path to /// vk.xml, relative to the project root. The generated bindings will be placed at /// `out_path`, which is relative to the zig-cache directory. - pub fn create(builder: *Build, spec_path: []const u8, out_path: []const u8) *GenerateStep { + pub fn create(builder: *Build, spec_path: []const u8, output_name: []const u8) *GenerateStep { const self = builder.allocator.create(GenerateStep) catch unreachable; - const full_out_path = path.join(builder.allocator, &[_][]const u8{ - builder.build_root, - builder.cache_root, - out_path, - }) catch unreachable; - self.* = .{ .step = Step.init(.custom, "vulkan-generate", builder.allocator, make), .builder = builder, - .spec_path = spec_path, .generated_file = .{ .step = &self.step, - .path = full_out_path, }, + .output_name = output_name, + .spec_path = spec_path, }; return self; } @@ -44,13 +38,13 @@ pub const GenerateStep = struct { /// root. Typically, the location of the LunarG SDK root can be retrieved by querying for the VULKAN_SDK /// environment variable, set by activating the environment setup script located in the SDK root. /// `builder` and `out_path` are used in the same manner as `init`. - pub fn createFromSdk(builder: *Build, sdk_path: []const u8, out_path: []const u8) *GenerateStep { + pub fn createFromSdk(builder: *Build, sdk_path: []const u8, output_name: []const u8) *GenerateStep { const spec_path = std.fs.path.join( builder.allocator, &[_][]const u8{ sdk_path, "share/vulkan/registry/vk.xml" }, ) catch unreachable; - return create(builder, spec_path, out_path); + return create(builder, spec_path, output_name); } /// Returns the module with the generated budings, with name `module_name`. @@ -73,7 +67,29 @@ pub const GenerateStep = struct { const self = @fieldParentPtr(GenerateStep, "step", step); const cwd = std.fs.cwd(); + var man = self.builder.cache.obtain(); + defer man.deinit(); + const spec = try cwd.readFileAlloc(self.builder.allocator, self.spec_path, std.math.maxInt(usize)); + // TODO: Look into whether this is the right way to be doing + // this - maybe the file-level caching API has some benefits I + // don't understand. + man.hash.addBytes(self.spec_path); + man.hash.addBytes(spec); + man.hash.addBytes(self.output_name); + + const already_exists = man.hit() catch |err| @panic(switch (err) { + inline else => |e| "Cache error: " ++ @errorName(e), + }); + const digest = man.final(); + const output_file_path = try self.builder.cache_root.join( + self.builder.allocator, + &.{ "o", &digest, self.output_name }, + ); + if (already_exists) { + self.generated_file.path = output_file_path; + return; + } var out_buffer = std.ArrayList(u8).init(self.builder.allocator); generate(self.builder.allocator, spec, out_buffer.writer()) catch |err| switch (err) { @@ -101,10 +117,16 @@ pub const GenerateStep = struct { const tree = try std.zig.Ast.parse(self.builder.allocator, src, .zig); std.debug.assert(tree.errors.len == 0); // If this triggers, vulkan-zig produced invalid code. - var formatted = try tree.render(self.builder.allocator); + const formatted = try tree.render(self.builder.allocator); - const dir = path.dirname(self.generated_file.path.?).?; - try cwd.makePath(dir); - try cwd.writeFile(self.generated_file.path.?, formatted); + const output_dir_path = std.fs.path.dirname(output_file_path).?; + cwd.makePath(output_dir_path) catch |err| { + std.debug.print("unable to make path {s}: {s}\n", .{ output_dir_path, @errorName(err) }); + return err; + }; + + try cwd.writeFile(output_file_path, formatted); + self.generated_file.path = output_file_path; + try man.writeManifest(); } };