authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-06-13 12:01:59-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-06-19 11:45:06-04:00
log16d78bc0c024da307c7ab5f6b94622e6b4b37397
tree9eb0e671a4ac1cfaf3795384d4b4e7d050d017d2
parentdf4068cabd9af96d0aca1f435b985d1c103976da

Build: add install commands to `--verbose` output


11 files changed, 160 insertions(+), 97 deletions(-)

build.zig+4-4
...@@ -203,6 +203,10 @@ pub fn build(b: *std.Build) !void {...@@ -203,6 +203,10 @@ pub fn build(b: *std.Build) !void {
203 exe.pie = pie;203 exe.pie = pie;
204 exe.entitlements = entitlements;204 exe.entitlements = entitlements;
205205
206 const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
207 exe.use_llvm = use_llvm;
208 exe.use_lld = use_llvm;
209
206 if (no_bin) {210 if (no_bin) {
207 b.getInstallStep().dependOn(&exe.step);211 b.getInstallStep().dependOn(&exe.step);
208 } else {212 } else {
...@@ -214,10 +218,6 @@ pub fn build(b: *std.Build) !void {...@@ -214,10 +218,6 @@ pub fn build(b: *std.Build) !void {
214218
215 test_step.dependOn(&exe.step);219 test_step.dependOn(&exe.step);
216220
217 const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
218 exe.use_llvm = use_llvm;
219 exe.use_lld = use_llvm;
220
221 const exe_options = b.addOptions();221 const exe_options = b.addOptions();
222 exe.root_module.addOptions("build_options", exe_options);222 exe.root_module.addOptions("build_options", exe_options);
223223
lib/std/Build.zig+23
...@@ -2456,12 +2456,23 @@ pub const GeneratedFile = struct {...@@ -2456,12 +2456,23 @@ pub const GeneratedFile = struct {
2456 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.2456 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
2457 path: ?[]const u8 = null,2457 path: ?[]const u8 = null,
24582458
2459 /// Deprecated, see `getPath2`.
2459 pub fn getPath(gen: GeneratedFile) []const u8 {2460 pub fn getPath(gen: GeneratedFile) []const u8 {
2460 return gen.step.owner.pathFromCwd(gen.path orelse std.debug.panic(2461 return gen.step.owner.pathFromCwd(gen.path orelse std.debug.panic(
2461 "getPath() was called on a GeneratedFile that wasn't built yet. Is there a missing Step dependency on step '{s}'?",2462 "getPath() was called on a GeneratedFile that wasn't built yet. Is there a missing Step dependency on step '{s}'?",
2462 .{gen.step.name},2463 .{gen.step.name},
2463 ));2464 ));
2464 }2465 }
2466
2467 pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 {
2468 return gen.path orelse {
2469 std.debug.lockStdErr();
2470 const stderr = std.io.getStdErr();
2471 dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};
2472 std.debug.unlockStdErr();
2473 @panic("misconfigured build script");
2474 };
2475 }
2465};2476};
24662477
2467// dirnameAllowEmpty is a variant of fs.path.dirname2478// dirnameAllowEmpty is a variant of fs.path.dirname
...@@ -2712,6 +2723,18 @@ pub const LazyPath = union(enum) {...@@ -2712,6 +2723,18 @@ pub const LazyPath = union(enum) {
2712 }2723 }
2713 }2724 }
27142725
2726 pub fn basename(lazy_path: LazyPath, src_builder: *Build, asking_step: ?*Step) []const u8 {
2727 return fs.path.basename(switch (lazy_path) {
2728 .src_path => |sp| sp.sub_path,
2729 .cwd_relative => |sub_path| sub_path,
2730 .generated => |gen| if (gen.sub_path.len > 0)
2731 gen.sub_path
2732 else
2733 gen.file.getPath2(src_builder, asking_step),
2734 .dependency => |dep| dep.sub_path,
2735 });
2736 }
2737
2715 /// Copies the internal strings.2738 /// Copies the internal strings.
2716 ///2739 ///
2717 /// The `b` parameter is only used for its allocator. All *Build instances2740 /// The `b` parameter is only used for its allocator. All *Build instances
lib/std/Build/Step.zig+67-4
...@@ -478,6 +478,29 @@ pub fn evalZigProcess(...@@ -478,6 +478,29 @@ pub fn evalZigProcess(
478 return result;478 return result;
479}479}
480480
481/// Wrapper around `std.fs.Dir.updateFile` that handles verbose and error output.
482pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus {
483 const b = s.owner;
484 const src_path = src_lazy_path.getPath3(b, s);
485 try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{}", .{src_path}), dest_path });
486 return src_path.root_dir.handle.updateFile(src_path.sub_path, std.fs.cwd(), dest_path, .{}) catch |err| {
487 return s.fail("unable to update file from '{}' to '{s}': {s}", .{
488 src_path, dest_path, @errorName(err),
489 });
490 };
491}
492
493/// Wrapper around `std.fs.Dir.makePathStatus` that handles verbose and error output.
494pub fn installDir(s: *Step, dest_path: []const u8) !std.fs.Dir.MakePathStatus {
495 const b = s.owner;
496 try handleVerbose(b, null, &.{ "install", "-d", dest_path });
497 return std.fs.cwd().makePathStatus(dest_path) catch |err| {
498 return s.fail("unable to create dir '{s}': {s}", .{
499 dest_path, @errorName(err),
500 });
501 };
502}
503
481fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {504fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {
482 const b = s.owner;505 const b = s.owner;
483 const arena = b.allocator;506 const arena = b.allocator;
...@@ -714,8 +737,44 @@ pub fn allocPrintCmd2(...@@ -714,8 +737,44 @@ pub fn allocPrintCmd2(
714 opt_env: ?*const std.process.EnvMap,737 opt_env: ?*const std.process.EnvMap,
715 argv: []const []const u8,738 argv: []const []const u8,
716) Allocator.Error![]u8 {739) Allocator.Error![]u8 {
740 const shell = struct {
741 fn escape(writer: anytype, string: []const u8, is_argv0: bool) !void {
742 for (string) |c| {
743 if (switch (c) {
744 else => true,
745 '%', '+'...':', '@'...'Z', '_', 'a'...'z' => false,
746 '=' => is_argv0,
747 }) break;
748 } else return writer.writeAll(string);
749
750 try writer.writeByte('"');
751 for (string) |c| {
752 if (switch (c) {
753 std.ascii.control_code.nul => break,
754 '!', '"', '$', '\\', '`' => true,
755 else => !std.ascii.isPrint(c),
756 }) try writer.writeByte('\\');
757 switch (c) {
758 std.ascii.control_code.nul => unreachable,
759 std.ascii.control_code.bel => try writer.writeByte('a'),
760 std.ascii.control_code.bs => try writer.writeByte('b'),
761 std.ascii.control_code.ht => try writer.writeByte('t'),
762 std.ascii.control_code.lf => try writer.writeByte('n'),
763 std.ascii.control_code.vt => try writer.writeByte('v'),
764 std.ascii.control_code.ff => try writer.writeByte('f'),
765 std.ascii.control_code.cr => try writer.writeByte('r'),
766 std.ascii.control_code.esc => try writer.writeByte('E'),
767 ' '...'~' => try writer.writeByte(c),
768 else => try writer.print("{o:0>3}", .{c}),
769 }
770 }
771 try writer.writeByte('"');
772 }
773 };
774
717 var buf: std.ArrayListUnmanaged(u8) = .empty;775 var buf: std.ArrayListUnmanaged(u8) = .empty;
718 if (opt_cwd) |cwd| try buf.writer(arena).print("cd {s} && ", .{cwd});776 const writer = buf.writer(arena);
777 if (opt_cwd) |cwd| try writer.print("cd {s} && ", .{cwd});
719 if (opt_env) |env| {778 if (opt_env) |env| {
720 const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena);779 const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena);
721 var it = env.iterator();780 var it = env.iterator();
...@@ -725,11 +784,15 @@ pub fn allocPrintCmd2(...@@ -725,11 +784,15 @@ pub fn allocPrintCmd2(
725 if (process_env_map.get(key)) |process_value| {784 if (process_env_map.get(key)) |process_value| {
726 if (std.mem.eql(u8, value, process_value)) continue;785 if (std.mem.eql(u8, value, process_value)) continue;
727 }786 }
728 try buf.writer(arena).print("{s}={s} ", .{ key, value });787 try writer.print("{s}=", .{key});
788 try shell.escape(writer, value, false);
789 try writer.writeByte(' ');
729 }790 }
730 }791 }
731 for (argv) |arg| {792 try shell.escape(writer, argv[0], true);
732 try buf.writer(arena).print("{s} ", .{arg});793 for (argv[1..]) |arg| {
794 try writer.writeByte(' ');
795 try shell.escape(writer, arg, false);
733 }796 }
734 return buf.toOwnedSlice(arena);797 return buf.toOwnedSlice(arena);
735}798}
lib/std/Build/Step/Compile.zig+1
...@@ -668,6 +668,7 @@ pub fn producesPdbFile(compile: *Compile) bool {...@@ -668,6 +668,7 @@ pub fn producesPdbFile(compile: *Compile) bool {
668 else => return false,668 else => return false,
669 }669 }
670 if (target.ofmt == .c) return false;670 if (target.ofmt == .c) return false;
671 if (compile.use_llvm == false) return false;
671 if (compile.root_module.strip == true or672 if (compile.root_module.strip == true or
672 (compile.root_module.strip == null and compile.root_module.optimize == .ReleaseSmall))673 (compile.root_module.strip == null and compile.root_module.optimize == .ReleaseSmall))
673 {674 {
lib/std/Build/Step/InstallArtifact.zig+14-41
...@@ -119,18 +119,12 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -119,18 +119,12 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
119 _ = options;119 _ = options;
120 const install_artifact: *InstallArtifact = @fieldParentPtr("step", step);120 const install_artifact: *InstallArtifact = @fieldParentPtr("step", step);
121 const b = step.owner;121 const b = step.owner;
122 const cwd = fs.cwd();
123122
124 var all_cached = true;123 var all_cached = true;
125124
126 if (install_artifact.dest_dir) |dest_dir| {125 if (install_artifact.dest_dir) |dest_dir| {
127 const full_dest_path = b.getInstallPath(dest_dir, install_artifact.dest_sub_path);126 const full_dest_path = b.getInstallPath(dest_dir, install_artifact.dest_sub_path);
128 const src_path = install_artifact.emitted_bin.?.getPath3(b, step);127 const p = try step.installFile(install_artifact.emitted_bin.?, full_dest_path);
129 const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_dest_path, .{}) catch |err| {
130 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
131 src_path.sub_path, full_dest_path, @errorName(err),
132 });
133 };
134 all_cached = all_cached and p == .fresh;128 all_cached = all_cached and p == .fresh;
135129
136 if (install_artifact.dylib_symlinks) |dls| {130 if (install_artifact.dylib_symlinks) |dls| {
...@@ -141,48 +135,28 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -141,48 +135,28 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
141 }135 }
142136
143 if (install_artifact.implib_dir) |implib_dir| {137 if (install_artifact.implib_dir) |implib_dir| {
144 const src_path = install_artifact.emitted_implib.?.getPath3(b, step);138 const full_implib_path = b.getInstallPath(implib_dir, install_artifact.emitted_implib.?.basename(b, step));
145 const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(src_path.sub_path));139 const p = try step.installFile(install_artifact.emitted_implib.?, full_implib_path);
146 const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_implib_path, .{}) catch |err| {
147 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
148 src_path.sub_path, full_implib_path, @errorName(err),
149 });
150 };
151 all_cached = all_cached and p == .fresh;140 all_cached = all_cached and p == .fresh;
152 }141 }
153142
154 if (install_artifact.pdb_dir) |pdb_dir| {143 if (install_artifact.pdb_dir) |pdb_dir| {
155 const src_path = install_artifact.emitted_pdb.?.getPath3(b, step);144 const full_pdb_path = b.getInstallPath(pdb_dir, install_artifact.emitted_pdb.?.basename(b, step));
156 const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(src_path.sub_path));145 const p = try step.installFile(install_artifact.emitted_pdb.?, full_pdb_path);
157 const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_pdb_path, .{}) catch |err| {
158 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
159 src_path.sub_path, full_pdb_path, @errorName(err),
160 });
161 };
162 all_cached = all_cached and p == .fresh;146 all_cached = all_cached and p == .fresh;
163 }147 }
164148
165 if (install_artifact.h_dir) |h_dir| {149 if (install_artifact.h_dir) |h_dir| {
166 if (install_artifact.emitted_h) |emitted_h| {150 if (install_artifact.emitted_h) |emitted_h| {
167 const src_path = emitted_h.getPath3(b, step);151 const full_h_path = b.getInstallPath(h_dir, emitted_h.basename(b, step));
168 const full_h_path = b.getInstallPath(h_dir, fs.path.basename(src_path.sub_path));152 const p = try step.installFile(emitted_h, full_h_path);
169 const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_h_path, .{}) catch |err| {
170 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
171 src_path.sub_path, full_h_path, @errorName(err),
172 });
173 };
174 all_cached = all_cached and p == .fresh;153 all_cached = all_cached and p == .fresh;
175 }154 }
176155
177 for (install_artifact.artifact.installed_headers.items) |installation| switch (installation) {156 for (install_artifact.artifact.installed_headers.items) |installation| switch (installation) {
178 .file => |file| {157 .file => |file| {
179 const src_path = file.source.getPath3(b, step);
180 const full_h_path = b.getInstallPath(h_dir, file.dest_rel_path);158 const full_h_path = b.getInstallPath(h_dir, file.dest_rel_path);
181 const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_h_path, .{}) catch |err| {159 const p = try step.installFile(file.source, full_h_path);
182 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
183 src_path.sub_path, full_h_path, @errorName(err),
184 });
185 };
186 all_cached = all_cached and p == .fresh;160 all_cached = all_cached and p == .fresh;
187 },161 },
188 .directory => |dir| {162 .directory => |dir| {
...@@ -209,16 +183,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -209,16 +183,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
209 }183 }
210 }184 }
211185
212 const src_entry_path = src_dir_path.join(b.allocator, entry.path) catch @panic("OOM");
213 const full_dest_path = b.pathJoin(&.{ full_h_prefix, entry.path });186 const full_dest_path = b.pathJoin(&.{ full_h_prefix, entry.path });
214 switch (entry.kind) {187 switch (entry.kind) {
215 .directory => try cwd.makePath(full_dest_path),188 .directory => {
189 try Step.handleVerbose(b, null, &.{ "install", "-d", full_dest_path });
190 const p = try step.installDir(full_dest_path);
191 all_cached = all_cached and p == .existed;
192 },
216 .file => {193 .file => {
217 const p = fs.Dir.updateFile(src_entry_path.root_dir.handle, src_entry_path.sub_path, cwd, full_dest_path, .{}) catch |err| {194 const p = try step.installFile(try dir.source.join(b.allocator, entry.path), full_dest_path);
218 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
219 src_entry_path.sub_path, full_dest_path, @errorName(err),
220 });
221 };
222 all_cached = all_cached and p == .fresh;195 all_cached = all_cached and p == .fresh;
223 },196 },
224 else => continue,197 else => continue,
lib/std/Build/Step/InstallDir.zig+10-28
...@@ -74,31 +74,23 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -74,31 +74,23 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
74 var all_cached = true;74 var all_cached = true;
75 next_entry: while (try it.next()) |entry| {75 next_entry: while (try it.next()) |entry| {
76 for (install_dir.options.exclude_extensions) |ext| {76 for (install_dir.options.exclude_extensions) |ext| {
77 if (mem.endsWith(u8, entry.path, ext)) {77 if (mem.endsWith(u8, entry.path, ext)) continue :next_entry;
78 continue :next_entry;
79 }
80 }78 }
81 if (install_dir.options.include_extensions) |incs| {79 if (install_dir.options.include_extensions) |incs| {
82 var found = false;
83 for (incs) |inc| {80 for (incs) |inc| {
84 if (mem.endsWith(u8, entry.path, inc)) {81 if (mem.endsWith(u8, entry.path, inc)) break;
85 found = true;82 } else {
86 break;83 continue :next_entry;
87 }
88 }84 }
89 if (!found) continue :next_entry;
90 }85 }
9186
92 // relative to src build root87 const src_path = try install_dir.options.source_dir.join(b.allocator, entry.path);
93 const src_sub_path = try src_dir_path.join(arena, entry.path);
94 const dest_path = b.pathJoin(&.{ dest_prefix, entry.path });88 const dest_path = b.pathJoin(&.{ dest_prefix, entry.path });
95 const cwd = fs.cwd();
96
97 switch (entry.kind) {89 switch (entry.kind) {
98 .directory => {90 .directory => {
99 if (need_derived_inputs) try step.addDirectoryWatchInputFromPath(src_sub_path);91 if (need_derived_inputs) _ = try step.addDirectoryWatchInput(src_path);
100 try cwd.makePath(dest_path);92 const p = try step.installDir(dest_path);
101 // TODO: set result_cached=false if the directory did not already exist.93 all_cached = all_cached and p == .existed;
102 },94 },
103 .file => {95 .file => {
104 for (install_dir.options.blank_extensions) |ext| {96 for (install_dir.options.blank_extensions) |ext| {
...@@ -108,18 +100,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -108,18 +100,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
108 }100 }
109 }101 }
110102
111 const prev_status = fs.Dir.updateFile(103 const p = try step.installFile(src_path, dest_path);
112 src_sub_path.root_dir.handle,104 all_cached = all_cached and p == .fresh;
113 src_sub_path.sub_path,
114 cwd,
115 dest_path,
116 .{},
117 ) catch |err| {
118 return step.fail("unable to update file from '{}' to '{s}': {s}", .{
119 src_sub_path, dest_path, @errorName(err),
120 });
121 };
122 all_cached = all_cached and prev_status == .fresh;
123 },105 },
124 else => continue,106 else => continue,
125 }107 }
lib/std/Build/Step/InstallFile.zig+2-8
...@@ -41,13 +41,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -41,13 +41,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
41 const install_file: *InstallFile = @fieldParentPtr("step", step);41 const install_file: *InstallFile = @fieldParentPtr("step", step);
42 try step.singleUnchangingWatchInput(install_file.source);42 try step.singleUnchangingWatchInput(install_file.source);
4343
44 const full_src_path = install_file.source.getPath2(b, step);
45 const full_dest_path = b.getInstallPath(install_file.dir, install_file.dest_rel_path);44 const full_dest_path = b.getInstallPath(install_file.dir, install_file.dest_rel_path);
46 const cwd = std.fs.cwd();45 const p = try step.installFile(install_file.source, full_dest_path);
47 const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| {46 step.result_cached = p == .fresh;
48 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
49 full_src_path, full_dest_path, @errorName(err),
50 });
51 };
52 step.result_cached = prev == .fresh;
53}47}
lib/std/Build/Step/ObjCopy.zig+1-1
...@@ -209,7 +209,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -209,7 +209,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
209 }209 }
210 if (objcopy.add_section) |section| {210 if (objcopy.add_section) |section| {
211 try argv.append("--add-section");211 try argv.append("--add-section");
212 try argv.appendSlice(&.{b.fmt("{s}={s}", .{ section.section_name, section.file_path.getPath(b) })});212 try argv.appendSlice(&.{b.fmt("{s}={s}", .{ section.section_name, section.file_path.getPath2(b, step) })});
213 }213 }
214 if (objcopy.set_section_alignment) |set_align| {214 if (objcopy.set_section_alignment) |set_align| {
215 try argv.append("--set-section-alignment");215 try argv.append("--set-section-alignment");
lib/std/Build/Step/Run.zig+20-3
...@@ -456,11 +456,28 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void {...@@ -456,11 +456,28 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void {
456 const b = run.step.owner;456 const b = run.step.owner;
457 const env_map = getEnvMapInternal(run);457 const env_map = getEnvMapInternal(run);
458458
459 const key = "PATH";459 const use_wine = b.enable_wine and b.graph.host.result.os.tag != .windows and use_wine: switch (run.argv.items[0]) {
460 .artifact => |p| p.artifact.rootModuleTarget().os.tag == .windows,
461 .lazy_path => |p| {
462 switch (p.lazy_path) {
463 .generated => |g| if (g.file.step.cast(Step.Compile)) |cs| break :use_wine cs.rootModuleTarget().os.tag == .windows,
464 else => {},
465 }
466 break :use_wine std.mem.endsWith(u8, p.lazy_path.basename(b, &run.step), ".exe");
467 },
468 .decorated_directory => false,
469 .bytes => |bytes| std.mem.endsWith(u8, bytes, ".exe"),
470 .output_file, .output_directory => false,
471 };
472 const key = if (use_wine) "WINEPATH" else "PATH";
460 const prev_path = env_map.get(key);473 const prev_path = env_map.get(key);
461474
462 if (prev_path) |pp| {475 if (prev_path) |pp| {
463 const new_path = b.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });476 const new_path = b.fmt("{s}{c}{s}", .{
477 pp,
478 if (use_wine) fs.path.delimiter_windows else fs.path.delimiter,
479 search_path,
480 });
464 env_map.put(key, new_path) catch @panic("OOM");481 env_map.put(key, new_path) catch @panic("OOM");
465 } else {482 } else {
466 env_map.put(key, b.dupePath(search_path)) catch @panic("OOM");483 env_map.put(key, b.dupePath(search_path)) catch @panic("OOM");
...@@ -866,7 +883,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -866,7 +883,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
866 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, null);883 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, null);
867884
868 const dep_file_dir = std.fs.cwd();885 const dep_file_dir = std.fs.cwd();
869 const dep_file_basename = dep_output_file.generated_file.getPath();886 const dep_file_basename = dep_output_file.generated_file.getPath2(b, step);
870 if (has_side_effects)887 if (has_side_effects)
871 try man.addDepFile(dep_file_dir, dep_file_basename)888 try man.addDepFile(dep_file_dir, dep_file_basename)
872 else889 else
lib/std/fs/Dir.zig+14-4
...@@ -1146,6 +1146,7 @@ pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void {...@@ -1146,6 +1146,7 @@ pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void {
1146/// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).1146/// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1147/// On WASI, `sub_path` should be encoded as valid UTF-8.1147/// On WASI, `sub_path` should be encoded as valid UTF-8.
1148/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.1148/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
1149/// Fails on an empty path with `error.BadPathName` as that is not a path that can be created.
1149///1150///
1150/// Paths containing `..` components are handled differently depending on the platform:1151/// Paths containing `..` components are handled differently depending on the platform:
1151/// - On Windows, `..` are resolved before the path is passed to NtCreateFile, meaning1152/// - On Windows, `..` are resolved before the path is passed to NtCreateFile, meaning
...@@ -1155,10 +1156,19 @@ pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void {...@@ -1155,10 +1156,19 @@ pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void {
1155/// meaning a `sub_path` like "first/../second" will create both a `./first`1156/// meaning a `sub_path` like "first/../second" will create both a `./first`
1156/// and a `./second` directory.1157/// and a `./second` directory.
1157pub fn makePath(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!void {1158pub fn makePath(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!void {
1159 _ = try self.makePathStatus(sub_path);
1160}
1161
1162pub const MakePathStatus = enum { existed, created };
1163/// Same as `makePath` except returns whether the path already existed or was successfully created.
1164pub fn makePathStatus(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!MakePathStatus {
1158 var it = try fs.path.componentIterator(sub_path);1165 var it = try fs.path.componentIterator(sub_path);
1159 var component = it.last() orelse return;1166 var status: MakePathStatus = .existed;
1167 var component = it.last() orelse return error.BadPathName;
1160 while (true) {1168 while (true) {
1161 self.makeDir(component.path) catch |err| switch (err) {1169 if (self.makeDir(component.path)) |_| {
1170 status = .created;
1171 } else |err| switch (err) {
1162 error.PathAlreadyExists => {1172 error.PathAlreadyExists => {
1163 // stat the file and return an error if it's not a directory1173 // stat the file and return an error if it's not a directory
1164 // this is important because otherwise a dangling symlink1174 // this is important because otherwise a dangling symlink
...@@ -1177,8 +1187,8 @@ pub fn makePath(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!vo...@@ -1177,8 +1187,8 @@ pub fn makePath(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!vo
1177 continue;1187 continue;
1178 },1188 },
1179 else => |e| return e,1189 else => |e| return e,
1180 };1190 }
1181 component = it.next() orelse return;1191 component = it.next() orelse return status;
1182 }1192 }
1183}1193}
11841194
lib/std/fs/path.zig+4-4
...@@ -27,8 +27,8 @@ const fs = std.fs;...@@ -27,8 +27,8 @@ const fs = std.fs;
27const process = std.process;27const process = std.process;
28const native_os = builtin.target.os.tag;28const native_os = builtin.target.os.tag;
2929
30pub const sep_windows = '\\';30pub const sep_windows: u8 = '\\';
31pub const sep_posix = '/';31pub const sep_posix: u8 = '/';
32pub const sep = switch (native_os) {32pub const sep = switch (native_os) {
33 .windows, .uefi => sep_windows,33 .windows, .uefi => sep_windows,
34 else => sep_posix,34 else => sep_posix,
...@@ -41,8 +41,8 @@ pub const sep_str = switch (native_os) {...@@ -41,8 +41,8 @@ pub const sep_str = switch (native_os) {
41 else => sep_str_posix,41 else => sep_str_posix,
42};42};
4343
44pub const delimiter_windows = ';';44pub const delimiter_windows: u8 = ';';
45pub const delimiter_posix = ':';45pub const delimiter_posix: u8 = ':';
46pub const delimiter = if (native_os == .windows) delimiter_windows else delimiter_posix;46pub const delimiter = if (native_os == .windows) delimiter_windows else delimiter_posix;
4747
48/// Returns if the given byte is a valid path separator48/// Returns if the given byte is a valid path separator