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 {
265265 for (dependee.link_objects.items) |link_object| switch (link_object) {
266266 .other_step => |compile| {
267267 addStepDependencies(m, dependee, &compile.step);
268 for (compile.installed_headers.items) |install_step|
269 addStepDependenciesOnly(m, install_step);
268 for (compile.installed_headers.items) |header|
269 addLazyPathDependenciesOnly(m, header.source.path());
270270 },
271271
272272 .static_path,
......@@ -691,20 +691,19 @@ pub fn appendZigProcessFlags(
691691 },
692692 .other_step => |other| {
693693 if (other.generated_h) |header| {
694 try zig_args.append("-isystem");
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 }));
694 try zig_args.appendSlice(&.{ "-isystem", std.fs.path.dirname(header.getPath()).? });
702695 }
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 };
703704 },
704705 .config_header_step => |config_header| {
705 const full_file_path = config_header.output_file.path.?;
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 });
706 try zig_args.appendSlice(&.{ "-I", std.fs.path.dirname(config_header.output_file.getPath()).? });
708707 },
709708 }
710709 }
......@@ -752,9 +751,8 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {
752751 m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");
753752 m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");
754753
755 for (other.installed_headers.items) |install_step| {
756 addStepDependenciesOnly(m, install_step);
757 }
754 for (other.installed_headers.items) |header|
755 addLazyPathDependenciesOnly(m, header.source.path());
758756}
759757
760758fn requireKnownTarget(m: *Module) std.Target {
lib/std/Build/Step/Compile.zig+94-61
......@@ -59,7 +59,7 @@ test_runner: ?[]const u8,
5959test_server_mode: bool,
6060wasi_exec_model: ?std.builtin.WasiExecModel = null,
6161
62installed_headers: ArrayList(*Step),
62installed_headers: ArrayList(InstalledHeader),
6363
6464// keep in sync with src/Compilation.zig:RcIncludes
6565/// Behavior of automatic detection of include directories when compiling .rc files.
......@@ -249,6 +249,70 @@ pub const Kind = enum {
249249 @"test",
250250};
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
252316pub fn create(owner: *std.Build, options: Options) *Compile {
253317 const name = owner.dupe(options.name);
254318 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 {
308372 .out_lib_filename = undefined,
309373 .major_only_filename = null,
310374 .name_only_filename = null,
311 .installed_headers = ArrayList(*Step).init(owner.allocator),
375 .installed_headers = ArrayList(InstalledHeader).init(owner.allocator),
312376 .zig_lib_dir = null,
313377 .exec_cmd_args = null,
314378 .filters = options.filters,
......@@ -380,78 +444,47 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
380444 return self;
381445}
382446
383pub fn installHeader(cs: *Compile, src_path: []const u8, dest_rel_path: []const u8) void {
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(
447pub fn installHeader(
396448 cs: *Compile,
397 config_header: *Step.ConfigHeader,
398 options: InstallConfigHeaderOptions,
449 source: LazyPath,
450 dest_rel_path: []const u8,
399451) void {
400 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
401452 const b = cs.step.owner;
402 const install_file = b.addInstallFileWithDir(
403 .{ .generated = &config_header.output_file },
404 options.install_dir,
405 dest_rel_path,
406 );
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");
453 cs.installed_headers.append(.{
454 .source = .{ .file = source.dupe(b) },
455 .dest_rel_path = b.dupePath(dest_rel_path),
456 }) catch @panic("OOM");
457 source.addStepDependencies(&cs.step);
410458}
411459
412pub fn installHeadersDirectory(
413 a: *Compile,
414 src_dir_path: []const u8,
460pub fn installHeaders(
461 cs: *Compile,
462 source: LazyPath,
415463 dest_rel_path: []const u8,
464 options: InstalledHeader.Source.Directory.Options,
416465) void {
417 return installHeadersDirectoryOptions(a, .{
418 .source_dir = .{ .path = src_dir_path },
419 .install_dir = .header,
420 .install_subdir = dest_rel_path,
421 });
466 const b = cs.step.owner;
467 cs.installed_headers.append(.{
468 .source = .{ .directory = .{
469 .path = source.dupe(b),
470 .options = options.dupe(b),
471 } },
472 .dest_rel_path = b.dupePath(dest_rel_path),
473 }) catch @panic("OOM");
474 source.addStepDependencies(&cs.step);
422475}
423476
424pub fn installHeadersDirectoryOptions(
425 cs: *Compile,
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");
477pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void {
478 cs.installHeader(.{ .generated = &config_header.output_file }, config_header.include_path);
432479}
433480
434pub fn installLibraryHeaders(cs: *Compile, l: *Compile) void {
435 assert(l.kind == .lib);
481pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void {
482 assert(lib.kind == .lib);
436483 const b = cs.step.owner;
437 const install_step = b.getInstallStep();
438 // Copy each element from installed_headers, modifying the builder
439 // to be the new parent's builder.
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);
484 for (lib.installed_headers.items) |header| {
485 cs.installed_headers.append(header.dupe(b)) catch @panic("OOM");
486 header.source.path().addStepDependencies(&cs.step);
453487 }
454 cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
455488}
456489
457490pub 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
7777 },
7878 .h_dir = switch (options.h_dir) {
7979 .disabled => null,
80 // https://github.com/ziglang/zig/issues/9698
81 .default => null,
82 //.default => switch (artifact.kind) {
83 // .lib => .header,
84 // else => null,
85 //},
80 .default => switch (artifact.kind) {
81 .lib => .header,
82 else => null,
83 },
8684 .override => |o| o,
8785 },
8886 .implib_dir = switch (options.implib_dir) {
......@@ -113,7 +111,8 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins
113111
114112 if (self.dest_dir != null) self.emitted_bin = artifact.getEmittedBin();
115113 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();
117116 if (self.implib_dir != null) self.emitted_implib = artifact.getEmittedImplib();
118117
119118 return self;
......@@ -122,14 +121,14 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins
122121fn make(step: *Step, prog_node: *std.Progress.Node) !void {
123122 _ = prog_node;
124123 const self: *InstallArtifact = @fieldParentPtr("step", step);
125 const dest_builder = step.owner;
124 const b = step.owner;
126125 const cwd = fs.cwd();
127126
128127 var all_cached = true;
129128
130129 if (self.dest_dir) |dest_dir| {
131 const full_dest_path = dest_builder.getInstallPath(dest_dir, self.dest_sub_path);
132 const full_src_path = self.emitted_bin.?.getPath2(step.owner, step);
130 const full_dest_path = b.getInstallPath(dest_dir, self.dest_sub_path);
131 const full_src_path = self.emitted_bin.?.getPath2(b, step);
133132 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| {
134133 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
135134 full_src_path, full_dest_path, @errorName(err),
......@@ -145,8 +144,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
145144 }
146145
147146 if (self.implib_dir) |implib_dir| {
148 const full_src_path = self.emitted_implib.?.getPath2(step.owner, step);
149 const full_implib_path = dest_builder.getInstallPath(implib_dir, fs.path.basename(full_src_path));
147 const full_src_path = self.emitted_implib.?.getPath2(b, step);
148 const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(full_src_path));
150149 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| {
151150 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
152151 full_src_path, full_implib_path, @errorName(err),
......@@ -156,8 +155,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
156155 }
157156
158157 if (self.pdb_dir) |pdb_dir| {
159 const full_src_path = self.emitted_pdb.?.getPath2(step.owner, step);
160 const full_pdb_path = dest_builder.getInstallPath(pdb_dir, fs.path.basename(full_src_path));
158 const full_src_path = self.emitted_pdb.?.getPath2(b, step);
159 const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(full_src_path));
161160 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| {
162161 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
163162 full_src_path, full_pdb_path, @errorName(err),
......@@ -167,14 +166,68 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
167166 }
168167
169168 if (self.h_dir) |h_dir| {
170 const full_src_path = self.emitted_h.?.getPath2(step.owner, step);
171 const full_h_path = dest_builder.getInstallPath(h_dir, fs.path.basename(full_src_path));
172 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| {
173 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
174 full_src_path, full_h_path, @errorName(err),
175 });
169 if (self.emitted_h) |emitted_h| {
170 const full_src_path = emitted_h.getPath2(b, step);
171 const full_h_path = b.getInstallPath(h_dir, fs.path.basename(full_src_path));
172 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| {
173 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
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 },
176230 };
177 all_cached = all_cached and p == .fresh;
178231 }
179232
180233 step.result_cached = all_cached;