authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-07 15:07:00-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-04-07 15:07:00-07:00
logfdd6c31e8b25f9eed81c1e78fa71eca17fd29f68
tree61686eb9bc1399946d702e2fe024b6bf259f5b44
parentc78f996ff986b8843f328e1f083547c538ac865b
parenteee5400b7dc37845ea5f42e0841320953e7852b2
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19167 from castholm/installHeader

std.Build: fix `Compile.installHeader` behavior, add `WriteFile.addCopyDirectory`

14 files changed, 542 insertions(+), 140 deletions(-)

lib/std/Build.zig+13-12
...@@ -1568,23 +1568,24 @@ pub fn addObjCopy(b: *Build, source: LazyPath, options: Step.ObjCopy.Options) *S...@@ -1568,23 +1568,24 @@ pub fn addObjCopy(b: *Build, source: LazyPath, options: Step.ObjCopy.Options) *S
1568 return Step.ObjCopy.create(b, source, options);1568 return Step.ObjCopy.create(b, source, options);
1569}1569}
15701570
1571///`dest_rel_path` is relative to install prefix path1571/// `dest_rel_path` is relative to install prefix path
1572pub fn addInstallFile(self: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile {1572pub fn addInstallFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile {
1573 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);1573 return b.addInstallFileWithDir(source, .prefix, dest_rel_path);
1574}1574}
15751575
1576///`dest_rel_path` is relative to bin path1576/// `dest_rel_path` is relative to bin path
1577pub fn addInstallBinFile(self: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile {1577pub fn addInstallBinFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile {
1578 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);1578 return b.addInstallFileWithDir(source, .bin, dest_rel_path);
1579}1579}
15801580
1581///`dest_rel_path` is relative to lib path1581/// `dest_rel_path` is relative to lib path
1582pub fn addInstallLibFile(self: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile {1582pub fn addInstallLibFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile {
1583 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);1583 return b.addInstallFileWithDir(source, .lib, dest_rel_path);
1584}1584}
15851585
1586pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *Step.InstallFile {1586/// `dest_rel_path` is relative to header path
1587 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);1587pub fn addInstallHeaderFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile {
1588 return b.addInstallFileWithDir(source, .header, dest_rel_path);
1588}1589}
15891590
1590pub fn addInstallFileWithDir(1591pub fn addInstallFileWithDir(
...@@ -1593,7 +1594,7 @@ pub fn addInstallFileWithDir(...@@ -1593,7 +1594,7 @@ pub fn addInstallFileWithDir(
1593 install_dir: InstallDir,1594 install_dir: InstallDir,
1594 dest_rel_path: []const u8,1595 dest_rel_path: []const u8,
1595) *Step.InstallFile {1596) *Step.InstallFile {
1596 return Step.InstallFile.create(self, source.dupe(self), install_dir, dest_rel_path);1597 return Step.InstallFile.create(self, source, install_dir, dest_rel_path);
1597}1598}
15981599
1599pub fn addInstallDirectory(self: *Build, options: Step.InstallDir.Options) *Step.InstallDir {1600pub fn addInstallDirectory(self: *Build, options: Step.InstallDir.Options) *Step.InstallDir {
lib/std/Build/Module.zig+6-15
...@@ -265,8 +265,7 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void {...@@ -265,8 +265,7 @@ 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 addLazyPathDependenciesOnly(m, compile.getEmittedIncludeTree());
269 addStepDependenciesOnly(m, install_step);
270 },269 },
271270
272 .static_path,271 .static_path,
...@@ -691,20 +690,14 @@ pub fn appendZigProcessFlags(...@@ -691,20 +690,14 @@ pub fn appendZigProcessFlags(
691 },690 },
692 .other_step => |other| {691 .other_step => |other| {
693 if (other.generated_h) |header| {692 if (other.generated_h) |header| {
694 try zig_args.append("-isystem");693 try zig_args.appendSlice(&.{ "-isystem", std.fs.path.dirname(header.getPath()).? });
695 try zig_args.append(std.fs.path.dirname(header.path.?).?);
696 }694 }
697 if (other.installed_headers.items.len > 0) {695 if (other.installed_headers_include_tree) |include_tree| {
698 try zig_args.append("-I");696 try zig_args.appendSlice(&.{ "-I", include_tree.generated_directory.getPath() });
699 try zig_args.append(b.pathJoin(&.{
700 other.step.owner.install_prefix, "include",
701 }));
702 }697 }
703 },698 },
704 .config_header_step => |config_header| {699 .config_header_step => |config_header| {
705 const full_file_path = config_header.output_file.path.?;700 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 },701 },
709 }702 }
710 }703 }
...@@ -752,9 +745,7 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {...@@ -752,9 +745,7 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {
752 m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");745 m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");
753 m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");746 m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");
754747
755 for (other.installed_headers.items) |install_step| {748 addLazyPathDependenciesOnly(m, other.getEmittedIncludeTree());
756 addStepDependenciesOnly(m, install_step);
757 }
758}749}
759750
760fn requireKnownTarget(m: *Module) std.Target {751fn requireKnownTarget(m: *Module) std.Target {
lib/std/Build/Step/Compile.zig+163-66
...@@ -59,7 +59,13 @@ test_runner: ?[]const u8,...@@ -59,7 +59,13 @@ 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(HeaderInstallation),
63
64/// This step is used to create an include tree that dependent modules can add to their include
65/// search paths. Installed headers are copied to this step.
66/// This step is created the first time a module links with this artifact and is not
67/// created otherwise.
68installed_headers_include_tree: ?*Step.WriteFile = null,
6369
64// keep in sync with src/Compilation.zig:RcIncludes70// keep in sync with src/Compilation.zig:RcIncludes
65/// Behavior of automatic detection of include directories when compiling .rc files.71/// Behavior of automatic detection of include directories when compiling .rc files.
...@@ -249,6 +255,90 @@ pub const Kind = enum {...@@ -249,6 +255,90 @@ pub const Kind = enum {
249 @"test",255 @"test",
250};256};
251257
258pub const HeaderInstallation = union(enum) {
259 file: File,
260 directory: Directory,
261
262 pub const File = struct {
263 source: LazyPath,
264 dest_rel_path: []const u8,
265
266 pub fn dupe(self: File, b: *std.Build) File {
267 // 'path' lazy paths are relative to the build root of some step, inferred from the step
268 // in which they are used. This means that we can't dupe such paths, because they may
269 // come from dependencies with their own build roots and duping the paths as is might
270 // cause the build script to search for the file relative to the wrong root.
271 // As a temporary workaround, we convert build root-relative paths to absolute paths.
272 // If/when the build-root relative paths are updated to encode which build root they are
273 // relative to, this workaround should be removed.
274 const duped_source: LazyPath = switch (self.source) {
275 .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) },
276 else => self.source.dupe(b),
277 };
278
279 return .{
280 .source = duped_source,
281 .dest_rel_path = b.dupePath(self.dest_rel_path),
282 };
283 }
284 };
285
286 pub const Directory = struct {
287 source: LazyPath,
288 dest_rel_path: []const u8,
289 options: Directory.Options,
290
291 pub const Options = struct {
292 /// File paths that end in any of these suffixes will be excluded from installation.
293 exclude_extensions: []const []const u8 = &.{},
294 /// Only file paths that end in any of these suffixes will be included in installation.
295 /// `null` means that all suffixes will be included.
296 /// `exclude_extensions` takes precedence over `include_extensions`.
297 include_extensions: ?[]const []const u8 = &.{".h"},
298
299 pub fn dupe(self: Directory.Options, b: *std.Build) Directory.Options {
300 return .{
301 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
302 .include_extensions = if (self.include_extensions) |incs| b.dupeStrings(incs) else null,
303 };
304 }
305 };
306
307 pub fn dupe(self: Directory, b: *std.Build) Directory {
308 // 'path' lazy paths are relative to the build root of some step, inferred from the step
309 // in which they are used. This means that we can't dupe such paths, because they may
310 // come from dependencies with their own build roots and duping the paths as is might
311 // cause the build script to search for the file relative to the wrong root.
312 // As a temporary workaround, we convert build root-relative paths to absolute paths.
313 // If/when the build-root relative paths are updated to encode which build root they are
314 // relative to, this workaround should be removed.
315 const duped_source: LazyPath = switch (self.source) {
316 .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) },
317 else => self.source.dupe(b),
318 };
319
320 return .{
321 .source = duped_source,
322 .dest_rel_path = b.dupePath(self.dest_rel_path),
323 .options = self.options.dupe(b),
324 };
325 }
326 };
327
328 pub fn getSource(self: HeaderInstallation) LazyPath {
329 return switch (self) {
330 inline .file, .directory => |x| x.source,
331 };
332 }
333
334 pub fn dupe(self: HeaderInstallation, b: *std.Build) HeaderInstallation {
335 return switch (self) {
336 .file => |f| .{ .file = f.dupe(b) },
337 .directory => |d| .{ .directory = d.dupe(b) },
338 };
339 }
340};
341
252pub fn create(owner: *std.Build, options: Options) *Compile {342pub fn create(owner: *std.Build, options: Options) *Compile {
253 const name = owner.dupe(options.name);343 const name = owner.dupe(options.name);
254 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {344 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
...@@ -308,7 +398,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -308,7 +398,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
308 .out_lib_filename = undefined,398 .out_lib_filename = undefined,
309 .major_only_filename = null,399 .major_only_filename = null,
310 .name_only_filename = null,400 .name_only_filename = null,
311 .installed_headers = ArrayList(*Step).init(owner.allocator),401 .installed_headers = ArrayList(HeaderInstallation).init(owner.allocator),
312 .zig_lib_dir = null,402 .zig_lib_dir = null,
313 .exec_cmd_args = null,403 .exec_cmd_args = null,
314 .filters = options.filters,404 .filters = options.filters,
...@@ -380,78 +470,85 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -380,78 +470,85 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
380 return self;470 return self;
381}471}
382472
383pub fn installHeader(cs: *Compile, src_path: []const u8, dest_rel_path: []const u8) void {473/// Marks the specified header for installation alongside this artifact.
474/// When a module links with this artifact, all headers marked for installation are added to that
475/// module's include search path.
476pub fn installHeader(cs: *Compile, source: LazyPath, dest_rel_path: []const u8) void {
384 const b = cs.step.owner;477 const b = cs.step.owner;
385 const install_file = b.addInstallHeaderFile(src_path, dest_rel_path);478 const installation: HeaderInstallation = .{ .file = .{
386 b.getInstallStep().dependOn(&install_file.step);479 .source = source.dupe(b),
387 cs.installed_headers.append(&install_file.step) catch @panic("OOM");480 .dest_rel_path = b.dupePath(dest_rel_path),
388}481 } };
389482 cs.installed_headers.append(installation) catch @panic("OOM");
390pub const InstallConfigHeaderOptions = struct {483 cs.addHeaderInstallationToIncludeTree(installation);
391 install_dir: InstallDir = .header,484 installation.getSource().addStepDependencies(&cs.step);
392 dest_rel_path: ?[]const u8 = null,485}
393};486
394487/// Marks headers from the specified directory for installation alongside this artifact.
395pub fn installConfigHeader(488/// When a module links with this artifact, all headers marked for installation are added to that
396 cs: *Compile,489/// module's include search path.
397 config_header: *Step.ConfigHeader,
398 options: InstallConfigHeaderOptions,
399) void {
400 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
401 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");
410}
411
412pub fn installHeadersDirectory(490pub fn installHeadersDirectory(
413 a: *Compile,
414 src_dir_path: []const u8,
415 dest_rel_path: []const u8,
416) void {
417 return installHeadersDirectoryOptions(a, .{
418 .source_dir = .{ .path = src_dir_path },
419 .install_dir = .header,
420 .install_subdir = dest_rel_path,
421 });
422}
423
424pub fn installHeadersDirectoryOptions(
425 cs: *Compile,491 cs: *Compile,
426 options: std.Build.Step.InstallDir.Options,492 source: LazyPath,
493 dest_rel_path: []const u8,
494 options: HeaderInstallation.Directory.Options,
427) void {495) void {
428 const b = cs.step.owner;496 const b = cs.step.owner;
429 const install_dir = b.addInstallDirectory(options);497 const installation: HeaderInstallation = .{ .directory = .{
430 b.getInstallStep().dependOn(&install_dir.step);498 .source = source.dupe(b),
431 cs.installed_headers.append(&install_dir.step) catch @panic("OOM");499 .dest_rel_path = b.dupePath(dest_rel_path),
500 .options = options.dupe(b),
501 } };
502 cs.installed_headers.append(installation) catch @panic("OOM");
503 cs.addHeaderInstallationToIncludeTree(installation);
504 installation.getSource().addStepDependencies(&cs.step);
505}
506
507/// Marks the specified config header for installation alongside this artifact.
508/// When a module links with this artifact, all headers marked for installation are added to that
509/// module's include search path.
510pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void {
511 cs.installHeader(config_header.getOutput(), config_header.include_path);
512}
513
514/// Forwards all headers marked for installation from `lib` to this artifact.
515/// When a module links with this artifact, all headers marked for installation are added to that
516/// module's include search path.
517pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void {
518 assert(lib.kind == .lib);
519 for (lib.installed_headers.items) |installation| {
520 const installation_copy = installation.dupe(lib.step.owner);
521 cs.installed_headers.append(installation_copy) catch @panic("OOM");
522 cs.addHeaderInstallationToIncludeTree(installation_copy);
523 installation_copy.getSource().addStepDependencies(&cs.step);
524 }
525}
526
527fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstallation) void {
528 if (cs.installed_headers_include_tree) |wf| switch (installation) {
529 .file => |file| {
530 _ = wf.addCopyFile(file.source, file.dest_rel_path);
531 },
532 .directory => |dir| {
533 _ = wf.addCopyDirectory(dir.source, dir.dest_rel_path, .{
534 .exclude_extensions = dir.options.exclude_extensions,
535 .include_extensions = dir.options.include_extensions,
536 });
537 },
538 };
432}539}
433540
434pub fn installLibraryHeaders(cs: *Compile, l: *Compile) void {541pub fn getEmittedIncludeTree(cs: *Compile) LazyPath {
435 assert(l.kind == .lib);542 if (cs.installed_headers_include_tree) |wf| return wf.getDirectory();
436 const b = cs.step.owner;543 const b = cs.step.owner;
437 const install_step = b.getInstallStep();544 const wf = b.addWriteFiles();
438 // Copy each element from installed_headers, modifying the builder545 cs.installed_headers_include_tree = wf;
439 // to be the new parent's builder.546 for (cs.installed_headers.items) |installation| {
440 for (l.installed_headers.items) |step| {547 cs.addHeaderInstallationToIncludeTree(installation);
441 const step_copy = switch (step.id) {548 }
442 inline .install_file, .install_dir => |id| blk: {549 // The compile step itself does not need to depend on the write files step,
443 const T = id.Type();550 // only dependent modules do.
444 const ptr = b.allocator.create(T) catch @panic("OOM");551 return wf.getDirectory();
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 }
454 cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
455}552}
456553
457pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy {554pub 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) |installation| switch (installation) {
181 .file => |file| {
182 const full_src_path = file.source.getPath2(b, step);
183 const full_h_path = b.getInstallPath(h_dir, file.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.source.getPath2(b, step);
193 const full_h_prefix = b.getInstallPath(h_dir, dir.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;
lib/std/Build/Step/InstallDir.zig+9-14
...@@ -8,9 +8,6 @@ const InstallDirStep = @This();...@@ -8,9 +8,6 @@ const InstallDirStep = @This();
88
9step: Step,9step: Step,
10options: Options,10options: Options,
11/// This is used by the build system when a file being installed comes from one
12/// package but is being installed by another.
13dest_builder: *std.Build,
1411
15pub const base_id = .install_dir;12pub const base_id = .install_dir;
1613
...@@ -55,7 +52,6 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep {...@@ -55,7 +52,6 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep {
55 .makeFn = make,52 .makeFn = make,
56 }),53 }),
57 .options = options.dupe(owner),54 .options = options.dupe(owner),
58 .dest_builder = owner,
59 };55 };
60 options.source_dir.addStepDependencies(&self.step);56 options.source_dir.addStepDependencies(&self.step);
61 return self;57 return self;
...@@ -63,15 +59,14 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep {...@@ -63,15 +59,14 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep {
6359
64fn make(step: *Step, prog_node: *std.Progress.Node) !void {60fn make(step: *Step, prog_node: *std.Progress.Node) !void {
65 _ = prog_node;61 _ = prog_node;
62 const b = step.owner;
66 const self: *InstallDirStep = @fieldParentPtr("step", step);63 const self: *InstallDirStep = @fieldParentPtr("step", step);
67 const dest_builder = self.dest_builder;64 const arena = b.allocator;
68 const arena = dest_builder.allocator;65 const dest_prefix = b.getInstallPath(self.options.install_dir, self.options.install_subdir);
69 const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir);66 const src_dir_path = self.options.source_dir.getPath2(b, step);
70 const src_builder = self.step.owner;67 var src_dir = b.build_root.handle.openDir(src_dir_path, .{ .iterate = true }) catch |err| {
71 const src_dir_path = self.options.source_dir.getPath2(src_builder, step);
72 var src_dir = src_builder.build_root.handle.openDir(src_dir_path, .{ .iterate = true }) catch |err| {
73 return step.fail("unable to open source directory '{}{s}': {s}", .{68 return step.fail("unable to open source directory '{}{s}': {s}", .{
74 src_builder.build_root, src_dir_path, @errorName(err),69 b.build_root, src_dir_path, @errorName(err),
75 });70 });
76 };71 };
77 defer src_dir.close();72 defer src_dir.close();
...@@ -104,20 +99,20 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -104,20 +99,20 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
104 .file => {99 .file => {
105 for (self.options.blank_extensions) |ext| {100 for (self.options.blank_extensions) |ext| {
106 if (mem.endsWith(u8, entry.path, ext)) {101 if (mem.endsWith(u8, entry.path, ext)) {
107 try dest_builder.truncateFile(dest_path);102 try b.truncateFile(dest_path);
108 continue :next_entry;103 continue :next_entry;
109 }104 }
110 }105 }
111106
112 const prev_status = fs.Dir.updateFile(107 const prev_status = fs.Dir.updateFile(
113 src_builder.build_root.handle,108 b.build_root.handle,
114 src_sub_path,109 src_sub_path,
115 cwd,110 cwd,
116 dest_path,111 dest_path,
117 .{},112 .{},
118 ) catch |err| {113 ) catch |err| {
119 return step.fail("unable to update file from '{}{s}' to '{s}': {s}", .{114 return step.fail("unable to update file from '{}{s}' to '{s}': {s}", .{
120 src_builder.build_root, src_sub_path, dest_path, @errorName(err),115 b.build_root, src_sub_path, dest_path, @errorName(err),
121 });116 });
122 };117 };
123 all_cached = all_cached and prev_status == .fresh;118 all_cached = all_cached and prev_status == .fresh;
lib/std/Build/Step/InstallFile.zig+3-8
...@@ -11,9 +11,6 @@ step: Step,...@@ -11,9 +11,6 @@ step: Step,
11source: LazyPath,11source: LazyPath,
12dir: InstallDir,12dir: InstallDir,
13dest_rel_path: []const u8,13dest_rel_path: []const u8,
14/// This is used by the build system when a file being installed comes from one
15/// package but is being installed by another.
16dest_builder: *std.Build,
1714
18pub fn create(15pub fn create(
19 owner: *std.Build,16 owner: *std.Build,
...@@ -34,7 +31,6 @@ pub fn create(...@@ -34,7 +31,6 @@ pub fn create(
34 .source = source.dupe(owner),31 .source = source.dupe(owner),
35 .dir = dir.dupe(owner),32 .dir = dir.dupe(owner),
36 .dest_rel_path = owner.dupePath(dest_rel_path),33 .dest_rel_path = owner.dupePath(dest_rel_path),
37 .dest_builder = owner,
38 };34 };
39 source.addStepDependencies(&self.step);35 source.addStepDependencies(&self.step);
40 return self;36 return self;
...@@ -42,11 +38,10 @@ pub fn create(...@@ -42,11 +38,10 @@ pub fn create(
4238
43fn make(step: *Step, prog_node: *std.Progress.Node) !void {39fn make(step: *Step, prog_node: *std.Progress.Node) !void {
44 _ = prog_node;40 _ = prog_node;
45 const src_builder = step.owner;41 const b = step.owner;
46 const self: *InstallFile = @fieldParentPtr("step", step);42 const self: *InstallFile = @fieldParentPtr("step", step);
47 const dest_builder = self.dest_builder;43 const full_src_path = self.source.getPath2(b, step);
48 const full_src_path = self.source.getPath2(src_builder, step);44 const full_dest_path = b.getInstallPath(self.dir, self.dest_rel_path);
49 const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path);
50 const cwd = std.fs.cwd();45 const cwd = std.fs.cwd();
51 const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| {46 const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| {
52 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{47 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
lib/std/Build/Step/WriteFile.zig+130-4
...@@ -15,9 +15,11 @@ const ArrayList = std.ArrayList;...@@ -15,9 +15,11 @@ const ArrayList = std.ArrayList;
15const WriteFile = @This();15const WriteFile = @This();
1616
17step: Step,17step: Step,
18/// The elements here are pointers because we need stable pointers for the18
19/// GeneratedFile field.19// The elements here are pointers because we need stable pointers for the GeneratedFile field.
20files: std.ArrayListUnmanaged(*File),20files: std.ArrayListUnmanaged(*File),
21directories: std.ArrayListUnmanaged(*Directory),
22
21output_source_files: std.ArrayListUnmanaged(OutputSourceFile),23output_source_files: std.ArrayListUnmanaged(OutputSourceFile),
22generated_directory: std.Build.GeneratedFile,24generated_directory: std.Build.GeneratedFile,
2325
...@@ -33,6 +35,33 @@ pub const File = struct {...@@ -33,6 +35,33 @@ pub const File = struct {
33 }35 }
34};36};
3537
38pub const Directory = struct {
39 source: std.Build.LazyPath,
40 sub_path: []const u8,
41 options: Options,
42 generated_dir: std.Build.GeneratedFile,
43
44 pub const Options = struct {
45 /// File paths that end in any of these suffixes will be excluded from copying.
46 exclude_extensions: []const []const u8 = &.{},
47 /// Only file paths that end in any of these suffixes will be included in copying.
48 /// `null` means that all suffixes will be included.
49 /// `exclude_extensions` takes precedence over `include_extensions`.
50 include_extensions: ?[]const []const u8 = null,
51
52 pub fn dupe(self: Options, b: *std.Build) Options {
53 return .{
54 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
55 .include_extensions = if (self.include_extensions) |incs| b.dupeStrings(incs) else null,
56 };
57 }
58 };
59
60 pub fn getPath(self: *Directory) std.Build.LazyPath {
61 return .{ .generated = &self.generated_dir };
62 }
63};
64
36pub const OutputSourceFile = struct {65pub const OutputSourceFile = struct {
37 contents: Contents,66 contents: Contents,
38 sub_path: []const u8,67 sub_path: []const u8,
...@@ -53,6 +82,7 @@ pub fn create(owner: *std.Build) *WriteFile {...@@ -53,6 +82,7 @@ pub fn create(owner: *std.Build) *WriteFile {
53 .makeFn = make,82 .makeFn = make,
54 }),83 }),
55 .files = .{},84 .files = .{},
85 .directories = .{},
56 .output_source_files = .{},86 .output_source_files = .{},
57 .generated_directory = .{ .step = &wf.step },87 .generated_directory = .{ .step = &wf.step },
58 };88 };
...@@ -96,6 +126,31 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const...@@ -96,6 +126,31 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const
96 return file.getPath();126 return file.getPath();
97}127}
98128
129/// Copy files matching the specified exclude/include patterns to the specified subdirectory
130/// relative to this step's generated directory.
131/// The returned value is a lazy path to the generated subdirectory.
132pub fn addCopyDirectory(
133 wf: *WriteFile,
134 source: std.Build.LazyPath,
135 sub_path: []const u8,
136 options: Directory.Options,
137) std.Build.LazyPath {
138 const b = wf.step.owner;
139 const gpa = b.allocator;
140 const dir = gpa.create(Directory) catch @panic("OOM");
141 dir.* = .{
142 .source = source.dupe(b),
143 .sub_path = b.dupePath(sub_path),
144 .options = options.dupe(b),
145 .generated_dir = .{ .step = &wf.step },
146 };
147 wf.directories.append(gpa, dir) catch @panic("OOM");
148
149 wf.maybeUpdateName();
150 source.addStepDependencies(&wf.step);
151 return dir.getPath();
152}
153
99/// A path relative to the package root.154/// A path relative to the package root.
100/// Be careful with this because it updates source files. This should not be155/// Be careful with this because it updates source files. This should not be
101/// used as part of the normal build process, but as a utility occasionally156/// used as part of the normal build process, but as a utility occasionally
...@@ -130,11 +185,16 @@ pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath {...@@ -130,11 +185,16 @@ pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath {
130}185}
131186
132fn maybeUpdateName(wf: *WriteFile) void {187fn maybeUpdateName(wf: *WriteFile) void {
133 if (wf.files.items.len == 1) {188 if (wf.files.items.len == 1 and wf.directories.items.len == 0) {
134 // First time adding a file; update name.189 // First time adding a file; update name.
135 if (std.mem.eql(u8, wf.step.name, "WriteFile")) {190 if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
136 wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path});191 wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path});
137 }192 }
193 } else if (wf.directories.items.len == 1 and wf.files.items.len == 0) {
194 // First time adding a directory; update name.
195 if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
196 wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.directories.items[0].sub_path});
197 }
138 }198 }
139}199}
140200
...@@ -209,6 +269,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -209,6 +269,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
209 },269 },
210 }270 }
211 }271 }
272 for (wf.directories.items) |dir| {
273 man.hash.addBytes(dir.source.getPath2(b, step));
274 man.hash.addBytes(dir.sub_path);
275 for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext);
276 if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc);
277 }
212278
213 if (try step.cacheHit(&man)) {279 if (try step.cacheHit(&man)) {
214 const digest = man.final();280 const digest = man.final();
...@@ -233,6 +299,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -233,6 +299,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
233 };299 };
234 defer cache_dir.close();300 defer cache_dir.close();
235301
302 const cwd = fs.cwd();
303
236 for (wf.files.items) |file| {304 for (wf.files.items) |file| {
237 if (fs.path.dirname(file.sub_path)) |dirname| {305 if (fs.path.dirname(file.sub_path)) |dirname| {
238 cache_dir.makePath(dirname) catch |err| {306 cache_dir.makePath(dirname) catch |err| {
...@@ -252,7 +320,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -252,7 +320,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
252 .copy => |file_source| {320 .copy => |file_source| {
253 const source_path = file_source.getPath(b);321 const source_path = file_source.getPath(b);
254 const prev_status = fs.Dir.updateFile(322 const prev_status = fs.Dir.updateFile(
255 fs.cwd(),323 cwd,
256 source_path,324 source_path,
257 cache_dir,325 cache_dir,
258 file.sub_path,326 file.sub_path,
...@@ -279,6 +347,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -279,6 +347,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
279 cache_path, file.sub_path,347 cache_path, file.sub_path,
280 });348 });
281 }349 }
350 for (wf.directories.items) |dir| {
351 const full_src_dir_path = dir.source.getPath2(b, step);
352 const dest_dirname = dir.sub_path;
353
354 if (dest_dirname.len != 0) {
355 cache_dir.makePath(dest_dirname) catch |err| {
356 return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{
357 b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err),
358 });
359 };
360 }
361
362 var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| {
363 return step.fail("unable to open source directory '{s}': {s}", .{
364 full_src_dir_path, @errorName(err),
365 });
366 };
367 defer src_dir.close();
368
369 var it = try src_dir.walk(b.allocator);
370 next_entry: while (try it.next()) |entry| {
371 for (dir.options.exclude_extensions) |ext| {
372 if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry;
373 }
374 if (dir.options.include_extensions) |incs| {
375 for (incs) |inc| {
376 if (std.mem.endsWith(u8, entry.path, inc)) break;
377 } else {
378 continue :next_entry;
379 }
380 }
381 const full_src_entry_path = b.pathJoin(&.{ full_src_dir_path, entry.path });
382 const dest_path = b.pathJoin(&.{ dest_dirname, entry.path });
383 switch (entry.kind) {
384 .directory => try cache_dir.makePath(dest_path),
385 .file => {
386 const prev_status = fs.Dir.updateFile(
387 cwd,
388 full_src_entry_path,
389 cache_dir,
390 dest_path,
391 .{},
392 ) catch |err| {
393 return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{
394 full_src_entry_path,
395 b.cache_root,
396 cache_path,
397 fs.path.sep,
398 dest_path,
399 @errorName(err),
400 });
401 };
402 _ = prev_status;
403 },
404 else => continue,
405 }
406 }
407 }
282408
283 try step.writeManifest(&man);409 try step.writeManifest(&man);
284}410}
test/standalone.zig+4
...@@ -266,6 +266,10 @@ pub const build_cases = [_]BuildCase{...@@ -266,6 +266,10 @@ pub const build_cases = [_]BuildCase{
266 .build_root = "test/standalone/depend_on_main_mod",266 .build_root = "test/standalone/depend_on_main_mod",
267 .import = @import("standalone/depend_on_main_mod/build.zig"),267 .import = @import("standalone/depend_on_main_mod/build.zig"),
268 },268 },
269 .{
270 .build_root = "test/standalone/install_headers",
271 .import = @import("standalone/install_headers/build.zig"),
272 },
269};273};
270274
271const std = @import("std");275const std = @import("std");
test/standalone/install_headers/build.zig created+99
...@@ -0,0 +1,99 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 b.default_step = test_step;
6
7 const empty_c = b.addWriteFiles().add("empty.c", "");
8
9 const libfoo = b.addStaticLibrary(.{
10 .name = "foo",
11 .target = b.resolveTargetQuery(.{}),
12 .optimize = .Debug,
13 });
14 libfoo.addCSourceFile(.{ .file = empty_c });
15
16 const exe = b.addExecutable(.{
17 .name = "exe",
18 .target = b.resolveTargetQuery(.{}),
19 .optimize = .Debug,
20 .link_libc = true,
21 });
22 exe.addCSourceFile(.{ .file = b.addWriteFiles().add("main.c",
23 \\#include <stdio.h>
24 \\#include <foo/a.h>
25 \\#include <foo/sub_dir/b.h>
26 \\#include <foo/d.h>
27 \\#include <foo/config.h>
28 \\#include <bar.h>
29 \\int main(void) {
30 \\ printf(FOO_A FOO_B FOO_D FOO_CONFIG_1 FOO_CONFIG_2 BAR_X);
31 \\ return 0;
32 \\}
33 ) });
34
35 libfoo.installHeadersDirectory(.{ .path = "include" }, "foo", .{ .exclude_extensions = &.{".ignore_me.h"} });
36 libfoo.installHeader(b.addWriteFiles().add("d.h",
37 \\#define FOO_D "D"
38 \\
39 ), "foo/d.h");
40
41 if (libfoo.installed_headers_include_tree != null) std.debug.panic("include tree step was created before linking", .{});
42
43 // Link before we have registered all headers for installation,
44 // to verify that the lazily created write files step is properly taken into account.
45 exe.linkLibrary(libfoo);
46
47 if (libfoo.installed_headers_include_tree == null) std.debug.panic("include tree step was not created after linking", .{});
48
49 libfoo.installConfigHeader(b.addConfigHeader(.{
50 .style = .blank,
51 .include_path = "foo/config.h",
52 }, .{
53 .FOO_CONFIG_1 = "1",
54 .FOO_CONFIG_2 = "2",
55 }));
56
57 const libbar = b.addStaticLibrary(.{
58 .name = "bar",
59 .target = b.resolveTargetQuery(.{}),
60 .optimize = .Debug,
61 });
62 libbar.addCSourceFile(.{ .file = empty_c });
63 libbar.installHeader(b.addWriteFiles().add("bar.h",
64 \\#define BAR_X "X"
65 \\
66 ), "bar.h");
67 libfoo.installLibraryHeaders(libbar);
68
69 const run_exe = b.addRunArtifact(exe);
70 run_exe.expectStdOutEqual("ABD12X");
71 test_step.dependOn(&run_exe.step);
72
73 const install_libfoo = b.addInstallArtifact(libfoo, .{
74 .dest_dir = .{ .override = .{ .custom = "custom" } },
75 .h_dir = .{ .override = .{ .custom = "custom/include" } },
76 .implib_dir = .disabled,
77 .pdb_dir = .disabled,
78 });
79 const check_exists = b.addExecutable(.{
80 .name = "check_exists",
81 .root_source_file = .{ .path = "check_exists.zig" },
82 .target = b.resolveTargetQuery(.{}),
83 .optimize = .Debug,
84 });
85 const run_check_exists = b.addRunArtifact(check_exists);
86 run_check_exists.addArgs(&.{
87 "custom/include/foo/a.h",
88 "!custom/include/foo/ignore_me.txt",
89 "custom/include/foo/sub_dir/b.h",
90 "!custom/include/foo/sub_dir/c.ignore_me.h",
91 "custom/include/foo/d.h",
92 "custom/include/foo/config.h",
93 "custom/include/bar.h",
94 });
95 run_check_exists.setCwd(.{ .cwd_relative = b.getInstallPath(.prefix, "") });
96 run_check_exists.expectExitCode(0);
97 run_check_exists.step.dependOn(&install_libfoo.step);
98 test_step.dependOn(&run_check_exists.step);
99}
test/standalone/install_headers/check_exists.zig created+37
...@@ -0,0 +1,37 @@
1const std = @import("std");
2
3/// Checks the existence of files relative to cwd.
4/// A path starting with ! should not exist.
5pub fn main() !void {
6 var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
7 defer arena_state.deinit();
8
9 const arena = arena_state.allocator();
10
11 var arg_it = try std.process.argsWithAllocator(arena);
12 _ = arg_it.next();
13
14 const cwd = std.fs.cwd();
15 const cwd_realpath = try cwd.realpathAlloc(arena, ".");
16
17 while (arg_it.next()) |file_path| {
18 if (file_path.len > 0 and file_path[0] == '!') {
19 errdefer std.log.err(
20 "exclusive file check '{s}{c}{s}' failed",
21 .{ cwd_realpath, std.fs.path.sep, file_path[1..] },
22 );
23 if (std.fs.cwd().statFile(file_path[1..])) |_| {
24 return error.FileFound;
25 } else |err| switch (err) {
26 error.FileNotFound => {},
27 else => return err,
28 }
29 } else {
30 errdefer std.log.err(
31 "inclusive file check '{s}{c}{s}' failed",
32 .{ cwd_realpath, std.fs.path.sep, file_path },
33 );
34 _ = try std.fs.cwd().statFile(file_path);
35 }
36 }
37}
test/standalone/install_headers/include/a.h created+1
...@@ -0,0 +1 @@
1#define FOO_A "A"
test/standalone/install_headers/include/ignore_me.txt created+1
...@@ -0,0 +1 @@
1ignore me
test/standalone/install_headers/include/sub_dir/b.h created+1
...@@ -0,0 +1 @@
1#define FOO_B "B"
test/standalone/install_headers/include/sub_dir/c.ignore_me.h created+1
...@@ -0,0 +1 @@
1#error "ignore me"