authorgravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2024-03-02 22:59:00+01:00
committergravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2024-04-07 15:32:44+02:00
log0b7123f41d66bdda4da29d59623299d47b29aefb
treee909110b2a031944f20a41a0e44c17ccd37bb1c9
parent129de47a71b954b1118ce188f7032ad726491f53

std.Build: correct behavior of `Step.Compile.installHeader`

Previously, `Step.Compile.installHeader` and friends would incorrectly modify the default `install` top-level step, when the intent was for headers to get bundled with and installed alongside an artifact. This change set implements the intended behavior. This carries with it some breaking changes; `installHeader` and `installConfigHeader` both have new signatures, and `installHeadersDirectory` and `installHeadersDirectoryOptions` have been merged into `installHeaders`.

3 files changed, 182 insertions(+), 98 deletions(-)

lib/std/Build/Module.zig+14-16
...@@ -265,8 +265,8 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void {...@@ -265,8 +265,8 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void {
265 for (dependee.link_objects.items) |link_object| switch (link_object) {265 for (dependee.link_objects.items) |link_object| switch (link_object) {
266 .other_step => |compile| {266 .other_step => |compile| {
267 addStepDependencies(m, dependee, &compile.step);267 addStepDependencies(m, dependee, &compile.step);
268 for (compile.installed_headers.items) |install_step|268 for (compile.installed_headers.items) |header|
269 addStepDependenciesOnly(m, install_step);269 addLazyPathDependenciesOnly(m, header.source.path());
270 },270 },
271271
272 .static_path,272 .static_path,
...@@ -691,20 +691,19 @@ pub fn appendZigProcessFlags(...@@ -691,20 +691,19 @@ pub fn appendZigProcessFlags(
691 },691 },
692 .other_step => |other| {692 .other_step => |other| {
693 if (other.generated_h) |header| {693 if (other.generated_h) |header| {
694 try zig_args.append("-isystem");694 try zig_args.appendSlice(&.{ "-isystem", std.fs.path.dirname(header.getPath()).? });
695 try zig_args.append(std.fs.path.dirname(header.path.?).?);
696 }
697 if (other.installed_headers.items.len > 0) {
698 try zig_args.append("-I");
699 try zig_args.append(b.pathJoin(&.{
700 other.step.owner.install_prefix, "include",
701 }));
702 }695 }
696 for (other.installed_headers.items) |header| switch (header.source) {
697 .file => |lp| {
698 try zig_args.appendSlice(&.{ "-I", std.fs.path.dirname(lp.getPath2(b, asking_step)).? });
699 },
700 .directory => |dir| {
701 try zig_args.appendSlice(&.{ "-I", dir.path.getPath2(b, asking_step) });
702 },
703 };
703 },704 },
704 .config_header_step => |config_header| {705 .config_header_step => |config_header| {
705 const full_file_path = config_header.output_file.path.?;706 try zig_args.appendSlice(&.{ "-I", std.fs.path.dirname(config_header.output_file.getPath()).? });
706 const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len];
707 try zig_args.appendSlice(&.{ "-I", header_dir_path });
708 },707 },
709 }708 }
710 }709 }
...@@ -752,9 +751,8 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {...@@ -752,9 +751,8 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {
752 m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");751 m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");
753 m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");752 m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");
754753
755 for (other.installed_headers.items) |install_step| {754 for (other.installed_headers.items) |header|
756 addStepDependenciesOnly(m, install_step);755 addLazyPathDependenciesOnly(m, header.source.path());
757 }
758}756}
759757
760fn requireKnownTarget(m: *Module) std.Target {758fn requireKnownTarget(m: *Module) std.Target {
lib/std/Build/Step/Compile.zig+94-61
...@@ -59,7 +59,7 @@ test_runner: ?[]const u8,...@@ -59,7 +59,7 @@ test_runner: ?[]const u8,
59test_server_mode: bool,59test_server_mode: bool,
60wasi_exec_model: ?std.builtin.WasiExecModel = null,60wasi_exec_model: ?std.builtin.WasiExecModel = null,
6161
62installed_headers: ArrayList(*Step),62installed_headers: ArrayList(InstalledHeader),
6363
64// keep in sync with src/Compilation.zig:RcIncludes64// keep in sync with src/Compilation.zig:RcIncludes
65/// Behavior of automatic detection of include directories when compiling .rc files.65/// Behavior of automatic detection of include directories when compiling .rc files.
...@@ -249,6 +249,70 @@ pub const Kind = enum {...@@ -249,6 +249,70 @@ pub const Kind = enum {
249 @"test",249 @"test",
250};250};
251251
252pub const InstalledHeader = struct {
253 source: Source,
254 dest_rel_path: []const u8,
255
256 pub const Source = union(enum) {
257 file: LazyPath,
258 directory: Directory,
259
260 pub const Directory = struct {
261 path: LazyPath,
262 options: Directory.Options,
263
264 pub const Options = struct {
265 /// File paths which end in any of these suffixes will be excluded
266 /// from installation.
267 exclude_extensions: []const []const u8 = &.{},
268 /// Only file paths which end in any of these suffixes will be included
269 /// in installation.
270 /// `null` means all suffixes will be included.
271 /// `exclude_extensions` takes precedence over `include_extensions`
272 include_extensions: ?[]const []const u8 = &.{".h"},
273
274 pub fn dupe(self: Directory.Options, b: *std.Build) Directory.Options {
275 return .{
276 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
277 .include_extensions = if (self.include_extensions) |incs|
278 b.dupeStrings(incs)
279 else
280 null,
281 };
282 }
283 };
284
285 pub fn dupe(self: Directory, b: *std.Build) Directory {
286 return .{
287 .path = self.path.dupe(b),
288 .options = self.options.dupe(b),
289 };
290 }
291 };
292
293 pub fn path(self: Source) LazyPath {
294 return switch (self) {
295 .file => |lp| lp,
296 .directory => |dir| dir.path,
297 };
298 }
299
300 pub fn dupe(self: Source, b: *std.Build) Source {
301 return switch (self) {
302 .file => |lp| .{ .file = lp.dupe(b) },
303 .directory => |dir| .{ .directory = dir.dupe(b) },
304 };
305 }
306 };
307
308 pub fn dupe(self: InstalledHeader, b: *std.Build) InstalledHeader {
309 return .{
310 .source = self.source.dupe(b),
311 .dest_rel_path = b.dupePath(self.dest_rel_path),
312 };
313 }
314};
315
252pub fn create(owner: *std.Build, options: Options) *Compile {316pub fn create(owner: *std.Build, options: Options) *Compile {
253 const name = owner.dupe(options.name);317 const name = owner.dupe(options.name);
254 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {318 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
...@@ -308,7 +372,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -308,7 +372,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
308 .out_lib_filename = undefined,372 .out_lib_filename = undefined,
309 .major_only_filename = null,373 .major_only_filename = null,
310 .name_only_filename = null,374 .name_only_filename = null,
311 .installed_headers = ArrayList(*Step).init(owner.allocator),375 .installed_headers = ArrayList(InstalledHeader).init(owner.allocator),
312 .zig_lib_dir = null,376 .zig_lib_dir = null,
313 .exec_cmd_args = null,377 .exec_cmd_args = null,
314 .filters = options.filters,378 .filters = options.filters,
...@@ -380,78 +444,47 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -380,78 +444,47 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
380 return self;444 return self;
381}445}
382446
383pub fn installHeader(cs: *Compile, src_path: []const u8, dest_rel_path: []const u8) void {447pub fn installHeader(
384 const b = cs.step.owner;
385 const install_file = b.addInstallHeaderFile(src_path, dest_rel_path);
386 b.getInstallStep().dependOn(&install_file.step);
387 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
388}
389
390pub const InstallConfigHeaderOptions = struct {
391 install_dir: InstallDir = .header,
392 dest_rel_path: ?[]const u8 = null,
393};
394
395pub fn installConfigHeader(
396 cs: *Compile,448 cs: *Compile,
397 config_header: *Step.ConfigHeader,449 source: LazyPath,
398 options: InstallConfigHeaderOptions,450 dest_rel_path: []const u8,
399) void {451) void {
400 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
401 const b = cs.step.owner;452 const b = cs.step.owner;
402 const install_file = b.addInstallFileWithDir(453 cs.installed_headers.append(.{
403 .{ .generated = &config_header.output_file },454 .source = .{ .file = source.dupe(b) },
404 options.install_dir,455 .dest_rel_path = b.dupePath(dest_rel_path),
405 dest_rel_path,456 }) catch @panic("OOM");
406 );457 source.addStepDependencies(&cs.step);
407 install_file.step.dependOn(&config_header.step);
408 b.getInstallStep().dependOn(&install_file.step);
409 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
410}458}
411459
412pub fn installHeadersDirectory(460pub fn installHeaders(
413 a: *Compile,461 cs: *Compile,
414 src_dir_path: []const u8,462 source: LazyPath,
415 dest_rel_path: []const u8,463 dest_rel_path: []const u8,
464 options: InstalledHeader.Source.Directory.Options,
416) void {465) void {
417 return installHeadersDirectoryOptions(a, .{466 const b = cs.step.owner;
418 .source_dir = .{ .path = src_dir_path },467 cs.installed_headers.append(.{
419 .install_dir = .header,468 .source = .{ .directory = .{
420 .install_subdir = dest_rel_path,469 .path = source.dupe(b),
421 });470 .options = options.dupe(b),
471 } },
472 .dest_rel_path = b.dupePath(dest_rel_path),
473 }) catch @panic("OOM");
474 source.addStepDependencies(&cs.step);
422}475}
423476
424pub fn installHeadersDirectoryOptions(477pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void {
425 cs: *Compile,478 cs.installHeader(.{ .generated = &config_header.output_file }, config_header.include_path);
426 options: std.Build.Step.InstallDir.Options,
427) void {
428 const b = cs.step.owner;
429 const install_dir = b.addInstallDirectory(options);
430 b.getInstallStep().dependOn(&install_dir.step);
431 cs.installed_headers.append(&install_dir.step) catch @panic("OOM");
432}479}
433480
434pub fn installLibraryHeaders(cs: *Compile, l: *Compile) void {481pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void {
435 assert(l.kind == .lib);482 assert(lib.kind == .lib);
436 const b = cs.step.owner;483 const b = cs.step.owner;
437 const install_step = b.getInstallStep();484 for (lib.installed_headers.items) |header| {
438 // Copy each element from installed_headers, modifying the builder485 cs.installed_headers.append(header.dupe(b)) catch @panic("OOM");
439 // to be the new parent's builder.486 header.source.path().addStepDependencies(&cs.step);
440 for (l.installed_headers.items) |step| {
441 const step_copy = switch (step.id) {
442 inline .install_file, .install_dir => |id| blk: {
443 const T = id.Type();
444 const ptr = b.allocator.create(T) catch @panic("OOM");
445 ptr.* = step.cast(T).?.*;
446 ptr.dest_builder = b;
447 break :blk &ptr.step;
448 },
449 else => unreachable,
450 };
451 cs.installed_headers.append(step_copy) catch @panic("OOM");
452 install_step.dependOn(step_copy);
453 }487 }
454 cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
455}488}
456489
457pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy {490pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy {
lib/std/Build/Step/InstallArtifact.zig+74-21
...@@ -77,12 +77,10 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins...@@ -77,12 +77,10 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins
77 },77 },
78 .h_dir = switch (options.h_dir) {78 .h_dir = switch (options.h_dir) {
79 .disabled => null,79 .disabled => null,
80 // https://github.com/ziglang/zig/issues/969880 .default => switch (artifact.kind) {
81 .default => null,81 .lib => .header,
82 //.default => switch (artifact.kind) {82 else => null,
83 // .lib => .header,83 },
84 // else => null,
85 //},
86 .override => |o| o,84 .override => |o| o,
87 },85 },
88 .implib_dir = switch (options.implib_dir) {86 .implib_dir = switch (options.implib_dir) {
...@@ -113,7 +111,8 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins...@@ -113,7 +111,8 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins
113111
114 if (self.dest_dir != null) self.emitted_bin = artifact.getEmittedBin();112 if (self.dest_dir != null) self.emitted_bin = artifact.getEmittedBin();
115 if (self.pdb_dir != null) self.emitted_pdb = artifact.getEmittedPdb();113 if (self.pdb_dir != null) self.emitted_pdb = artifact.getEmittedPdb();
116 if (self.h_dir != null) self.emitted_h = artifact.getEmittedH();114 // https://github.com/ziglang/zig/issues/9698
115 //if (self.h_dir != null) self.emitted_h = artifact.getEmittedH();
117 if (self.implib_dir != null) self.emitted_implib = artifact.getEmittedImplib();116 if (self.implib_dir != null) self.emitted_implib = artifact.getEmittedImplib();
118117
119 return self;118 return self;
...@@ -122,14 +121,14 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins...@@ -122,14 +121,14 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins
122fn make(step: *Step, prog_node: *std.Progress.Node) !void {121fn make(step: *Step, prog_node: *std.Progress.Node) !void {
123 _ = prog_node;122 _ = prog_node;
124 const self: *InstallArtifact = @fieldParentPtr("step", step);123 const self: *InstallArtifact = @fieldParentPtr("step", step);
125 const dest_builder = step.owner;124 const b = step.owner;
126 const cwd = fs.cwd();125 const cwd = fs.cwd();
127126
128 var all_cached = true;127 var all_cached = true;
129128
130 if (self.dest_dir) |dest_dir| {129 if (self.dest_dir) |dest_dir| {
131 const full_dest_path = dest_builder.getInstallPath(dest_dir, self.dest_sub_path);130 const full_dest_path = b.getInstallPath(dest_dir, self.dest_sub_path);
132 const full_src_path = self.emitted_bin.?.getPath2(step.owner, step);131 const full_src_path = self.emitted_bin.?.getPath2(b, step);
133 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| {132 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| {
134 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{133 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
135 full_src_path, full_dest_path, @errorName(err),134 full_src_path, full_dest_path, @errorName(err),
...@@ -145,8 +144,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -145,8 +144,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
145 }144 }
146145
147 if (self.implib_dir) |implib_dir| {146 if (self.implib_dir) |implib_dir| {
148 const full_src_path = self.emitted_implib.?.getPath2(step.owner, step);147 const full_src_path = self.emitted_implib.?.getPath2(b, step);
149 const full_implib_path = dest_builder.getInstallPath(implib_dir, fs.path.basename(full_src_path));148 const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(full_src_path));
150 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| {149 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| {
151 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{150 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
152 full_src_path, full_implib_path, @errorName(err),151 full_src_path, full_implib_path, @errorName(err),
...@@ -156,8 +155,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -156,8 +155,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
156 }155 }
157156
158 if (self.pdb_dir) |pdb_dir| {157 if (self.pdb_dir) |pdb_dir| {
159 const full_src_path = self.emitted_pdb.?.getPath2(step.owner, step);158 const full_src_path = self.emitted_pdb.?.getPath2(b, step);
160 const full_pdb_path = dest_builder.getInstallPath(pdb_dir, fs.path.basename(full_src_path));159 const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(full_src_path));
161 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| {160 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| {
162 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{161 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
163 full_src_path, full_pdb_path, @errorName(err),162 full_src_path, full_pdb_path, @errorName(err),
...@@ -167,14 +166,68 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -167,14 +166,68 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
167 }166 }
168167
169 if (self.h_dir) |h_dir| {168 if (self.h_dir) |h_dir| {
170 const full_src_path = self.emitted_h.?.getPath2(step.owner, step);169 if (self.emitted_h) |emitted_h| {
171 const full_h_path = dest_builder.getInstallPath(h_dir, fs.path.basename(full_src_path));170 const full_src_path = emitted_h.getPath2(b, step);
172 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| {171 const full_h_path = b.getInstallPath(h_dir, fs.path.basename(full_src_path));
173 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{172 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| {
174 full_src_path, full_h_path, @errorName(err),173 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
175 });174 full_src_path, full_h_path, @errorName(err),
175 });
176 };
177 all_cached = all_cached and p == .fresh;
178 }
179
180 for (self.artifact.installed_headers.items) |header| switch (header.source) {
181 .file => |lp| {
182 const full_src_path = lp.getPath2(b, step);
183 const full_h_path = b.getInstallPath(h_dir, header.dest_rel_path);
184 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| {
185 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
186 full_src_path, full_h_path, @errorName(err),
187 });
188 };
189 all_cached = all_cached and p == .fresh;
190 },
191 .directory => |dir| {
192 const full_src_dir_path = dir.path.getPath2(b, step);
193 const full_h_prefix = b.getInstallPath(h_dir, header.dest_rel_path);
194
195 var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| {
196 return step.fail("unable to open source directory '{s}': {s}", .{
197 full_src_dir_path, @errorName(err),
198 });
199 };
200 defer src_dir.close();
201
202 var it = try src_dir.walk(b.allocator);
203 next_entry: while (try it.next()) |entry| {
204 for (dir.options.exclude_extensions) |ext| {
205 if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry;
206 }
207 if (dir.options.include_extensions) |incs| {
208 for (incs) |inc| {
209 if (std.mem.endsWith(u8, entry.path, inc)) break;
210 } else {
211 continue :next_entry;
212 }
213 }
214 const full_src_entry_path = b.pathJoin(&.{ full_src_dir_path, entry.path });
215 const full_dest_path = b.pathJoin(&.{ full_h_prefix, entry.path });
216 switch (entry.kind) {
217 .directory => try cwd.makePath(full_dest_path),
218 .file => {
219 const p = fs.Dir.updateFile(cwd, full_src_entry_path, cwd, full_dest_path, .{}) catch |err| {
220 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
221 full_src_entry_path, full_dest_path, @errorName(err),
222 });
223 };
224 all_cached = all_cached and p == .fresh;
225 },
226 else => continue,
227 }
228 }
229 },
176 };230 };
177 all_cached = all_cached and p == .fresh;
178 }231 }
179232
180 step.result_cached = all_cached;233 step.result_cached = all_cached;