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 {
203203 exe.pie = pie;
204204 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
206210 if (no_bin) {
207211 b.getInstallStep().dependOn(&exe.step);
208212 } else {
......@@ -214,10 +218,6 @@ pub fn build(b: *std.Build) !void {
214218
215219 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
221221 const exe_options = b.addOptions();
222222 exe.root_module.addOptions("build_options", exe_options);
223223
lib/std/Build.zig+23
......@@ -2456,12 +2456,23 @@ pub const GeneratedFile = struct {
24562456 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
24572457 path: ?[]const u8 = null,
24582458
2459 /// Deprecated, see `getPath2`.
24592460 pub fn getPath(gen: GeneratedFile) []const u8 {
24602461 return gen.step.owner.pathFromCwd(gen.path orelse std.debug.panic(
24612462 "getPath() was called on a GeneratedFile that wasn't built yet. Is there a missing Step dependency on step '{s}'?",
24622463 .{gen.step.name},
24632464 ));
24642465 }
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 }
24652476};
24662477
24672478// dirnameAllowEmpty is a variant of fs.path.dirname
......@@ -2712,6 +2723,18 @@ pub const LazyPath = union(enum) {
27122723 }
27132724 }
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
27152738 /// Copies the internal strings.
27162739 ///
27172740 /// 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(
478478 return result;
479479}
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
481504fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {
482505 const b = s.owner;
483506 const arena = b.allocator;
......@@ -714,8 +737,44 @@ pub fn allocPrintCmd2(
714737 opt_env: ?*const std.process.EnvMap,
715738 argv: []const []const u8,
716739) 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
717775 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});
719778 if (opt_env) |env| {
720779 const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena);
721780 var it = env.iterator();
......@@ -725,11 +784,15 @@ pub fn allocPrintCmd2(
725784 if (process_env_map.get(key)) |process_value| {
726785 if (std.mem.eql(u8, value, process_value)) continue;
727786 }
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(' ');
729790 }
730791 }
731 for (argv) |arg| {
732 try buf.writer(arena).print("{s} ", .{arg});
792 try shell.escape(writer, argv[0], true);
793 for (argv[1..]) |arg| {
794 try writer.writeByte(' ');
795 try shell.escape(writer, arg, false);
733796 }
734797 return buf.toOwnedSlice(arena);
735798}
lib/std/Build/Step/Compile.zig+1
......@@ -668,6 +668,7 @@ pub fn producesPdbFile(compile: *Compile) bool {
668668 else => return false,
669669 }
670670 if (target.ofmt == .c) return false;
671 if (compile.use_llvm == false) return false;
671672 if (compile.root_module.strip == true or
672673 (compile.root_module.strip == null and compile.root_module.optimize == .ReleaseSmall))
673674 {
lib/std/Build/Step/InstallArtifact.zig+14-41
......@@ -119,18 +119,12 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
119119 _ = options;
120120 const install_artifact: *InstallArtifact = @fieldParentPtr("step", step);
121121 const b = step.owner;
122 const cwd = fs.cwd();
123122
124123 var all_cached = true;
125124
126125 if (install_artifact.dest_dir) |dest_dir| {
127126 const full_dest_path = b.getInstallPath(dest_dir, install_artifact.dest_sub_path);
128 const src_path = install_artifact.emitted_bin.?.getPath3(b, step);
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 };
127 const p = try step.installFile(install_artifact.emitted_bin.?, full_dest_path);
134128 all_cached = all_cached and p == .fresh;
135129
136130 if (install_artifact.dylib_symlinks) |dls| {
......@@ -141,48 +135,28 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
141135 }
142136
143137 if (install_artifact.implib_dir) |implib_dir| {
144 const src_path = install_artifact.emitted_implib.?.getPath3(b, step);
145 const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(src_path.sub_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 };
138 const full_implib_path = b.getInstallPath(implib_dir, install_artifact.emitted_implib.?.basename(b, step));
139 const p = try step.installFile(install_artifact.emitted_implib.?, full_implib_path);
151140 all_cached = all_cached and p == .fresh;
152141 }
153142
154143 if (install_artifact.pdb_dir) |pdb_dir| {
155 const src_path = install_artifact.emitted_pdb.?.getPath3(b, step);
156 const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(src_path.sub_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 };
144 const full_pdb_path = b.getInstallPath(pdb_dir, install_artifact.emitted_pdb.?.basename(b, step));
145 const p = try step.installFile(install_artifact.emitted_pdb.?, full_pdb_path);
162146 all_cached = all_cached and p == .fresh;
163147 }
164148
165149 if (install_artifact.h_dir) |h_dir| {
166150 if (install_artifact.emitted_h) |emitted_h| {
167 const src_path = emitted_h.getPath3(b, step);
168 const full_h_path = b.getInstallPath(h_dir, fs.path.basename(src_path.sub_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 };
151 const full_h_path = b.getInstallPath(h_dir, emitted_h.basename(b, step));
152 const p = try step.installFile(emitted_h, full_h_path);
174153 all_cached = all_cached and p == .fresh;
175154 }
176155
177156 for (install_artifact.artifact.installed_headers.items) |installation| switch (installation) {
178157 .file => |file| {
179 const src_path = file.source.getPath3(b, step);
180158 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| {
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 };
159 const p = try step.installFile(file.source, full_h_path);
186160 all_cached = all_cached and p == .fresh;
187161 },
188162 .directory => |dir| {
......@@ -209,16 +183,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
209183 }
210184 }
211185
212 const src_entry_path = src_dir_path.join(b.allocator, entry.path) catch @panic("OOM");
213186 const full_dest_path = b.pathJoin(&.{ full_h_prefix, entry.path });
214187 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 },
216193 .file => {
217 const p = fs.Dir.updateFile(src_entry_path.root_dir.handle, src_entry_path.sub_path, cwd, full_dest_path, .{}) catch |err| {
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 };
194 const p = try step.installFile(try dir.source.join(b.allocator, entry.path), full_dest_path);
222195 all_cached = all_cached and p == .fresh;
223196 },
224197 else => continue,
lib/std/Build/Step/InstallDir.zig+10-28
......@@ -74,31 +74,23 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
7474 var all_cached = true;
7575 next_entry: while (try it.next()) |entry| {
7676 for (install_dir.options.exclude_extensions) |ext| {
77 if (mem.endsWith(u8, entry.path, ext)) {
78 continue :next_entry;
79 }
77 if (mem.endsWith(u8, entry.path, ext)) continue :next_entry;
8078 }
8179 if (install_dir.options.include_extensions) |incs| {
82 var found = false;
8380 for (incs) |inc| {
84 if (mem.endsWith(u8, entry.path, inc)) {
85 found = true;
86 break;
87 }
81 if (mem.endsWith(u8, entry.path, inc)) break;
82 } else {
83 continue :next_entry;
8884 }
89 if (!found) continue :next_entry;
9085 }
9186
92 // relative to src build root
93 const src_sub_path = try src_dir_path.join(arena, entry.path);
87 const src_path = try install_dir.options.source_dir.join(b.allocator, entry.path);
9488 const dest_path = b.pathJoin(&.{ dest_prefix, entry.path });
95 const cwd = fs.cwd();
96
9789 switch (entry.kind) {
9890 .directory => {
99 if (need_derived_inputs) try step.addDirectoryWatchInputFromPath(src_sub_path);
100 try cwd.makePath(dest_path);
101 // TODO: set result_cached=false if the directory did not already exist.
91 if (need_derived_inputs) _ = try step.addDirectoryWatchInput(src_path);
92 const p = try step.installDir(dest_path);
93 all_cached = all_cached and p == .existed;
10294 },
10395 .file => {
10496 for (install_dir.options.blank_extensions) |ext| {
......@@ -108,18 +100,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
108100 }
109101 }
110102
111 const prev_status = fs.Dir.updateFile(
112 src_sub_path.root_dir.handle,
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;
103 const p = try step.installFile(src_path, dest_path);
104 all_cached = all_cached and p == .fresh;
123105 },
124106 else => continue,
125107 }
lib/std/Build/Step/InstallFile.zig+2-8
......@@ -41,13 +41,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
4141 const install_file: *InstallFile = @fieldParentPtr("step", step);
4242 try step.singleUnchangingWatchInput(install_file.source);
4343
44 const full_src_path = install_file.source.getPath2(b, step);
4544 const full_dest_path = b.getInstallPath(install_file.dir, install_file.dest_rel_path);
46 const cwd = std.fs.cwd();
47 const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| {
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;
45 const p = try step.installFile(install_file.source, full_dest_path);
46 step.result_cached = p == .fresh;
5347}
lib/std/Build/Step/ObjCopy.zig+1-1
......@@ -209,7 +209,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
209209 }
210210 if (objcopy.add_section) |section| {
211211 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) })});
213213 }
214214 if (objcopy.set_section_alignment) |set_align| {
215215 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 {
456456 const b = run.step.owner;
457457 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";
460473 const prev_path = env_map.get(key);
461474
462475 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 });
464481 env_map.put(key, new_path) catch @panic("OOM");
465482 } else {
466483 env_map.put(key, b.dupePath(search_path)) catch @panic("OOM");
......@@ -866,7 +883,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
866883 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, null);
867884
868885 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);
870887 if (has_side_effects)
871888 try man.addDepFile(dep_file_dir, dep_file_basename)
872889 else
lib/std/fs/Dir.zig+14-4
......@@ -1146,6 +1146,7 @@ pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void {
11461146/// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
11471147/// On WASI, `sub_path` should be encoded as valid UTF-8.
11481148/// 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.
11491150///
11501151/// Paths containing `..` components are handled differently depending on the platform:
11511152/// - 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 {
11551156/// meaning a `sub_path` like "first/../second" will create both a `./first`
11561157/// and a `./second` directory.
11571158pub 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 {
11581165 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;
11601168 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) {
11621172 error.PathAlreadyExists => {
11631173 // stat the file and return an error if it's not a directory
11641174 // this is important because otherwise a dangling symlink
......@@ -1177,8 +1187,8 @@ pub fn makePath(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!vo
11771187 continue;
11781188 },
11791189 else => |e| return e,
1180 };
1181 component = it.next() orelse return;
1190 }
1191 component = it.next() orelse return status;
11821192 }
11831193}
11841194
lib/std/fs/path.zig+4-4
......@@ -27,8 +27,8 @@ const fs = std.fs;
2727const process = std.process;
2828const native_os = builtin.target.os.tag;
2929
30pub const sep_windows = '\\';
31pub const sep_posix = '/';
30pub const sep_windows: u8 = '\\';
31pub const sep_posix: u8 = '/';
3232pub const sep = switch (native_os) {
3333 .windows, .uefi => sep_windows,
3434 else => sep_posix,
......@@ -41,8 +41,8 @@ pub const sep_str = switch (native_os) {
4141 else => sep_str_posix,
4242};
4343
44pub const delimiter_windows = ';';
45pub const delimiter_posix = ':';
44pub const delimiter_windows: u8 = ';';
45pub const delimiter_posix: u8 = ':';
4646pub const delimiter = if (native_os == .windows) delimiter_windows else delimiter_posix;
4747
4848/// Returns if the given byte is a valid path separator