authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-06-23 18:23:59-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-06-29 23:50:19-07:00
log98854a673e10bf0606bb720aed93a2b759e8842d
tree9043ad62703600af7117a2a3975e0444229914ce
parent27ae3b30add18fa1348c8e89cdace862e6b0bb09

Maker: it's compiling again


9 files changed, 436 insertions(+), 390 deletions(-)

lib/compiler/Maker.zig+138-178
......@@ -1,5 +1,6 @@
11const Maker = @This();
22const builtin = @import("builtin");
3const native_os = builtin.os.tag;
34
45const std = @import("std");
56const Allocator = std.mem.Allocator;
......@@ -106,7 +107,7 @@ const MultilineErrors = enum { indent, newline, none };
106107const Summary = enum { all, new, failures, line, none };
107108
108109/// Used to build the -M flags to pass to build-exe.
109const CliModule = struct {
110pub const CliModule = struct {
110111 name: []const u8,
111112 root_path: []const u8,
112113 deps: Deps = .empty,
......@@ -171,17 +172,17 @@ pub fn main(init: process.Init.Minimal) !void {
171172 };
172173
173174 const cmd = stringToEnum(enum { init, fetch, build }, cmd_name) orelse
174 fatal("bad command name: {q}", .{ cmd_name });
175 fatal("bad command name: {q}", .{cmd_name});
175176 switch (cmd) {
176 .init => return cmdInit( gpa, &graph, args[arg_i..]),
177 .fetch => return cmdFetch( gpa, &graph, args[arg_i..]),
177 .init => return cmdInit(gpa, &graph, args[arg_i..]),
178 .fetch => return cmdFetch(gpa, &graph, args[arg_i..]),
178179 .build => {},
179180 }
180181
181182 var step_names: std.ArrayList([]const u8) = .empty;
182183 var help_menu = false;
183184 var steps_menu = false;
184 var print_configuration: enum {none, zon, path} = .none;
185 var print_configuration: enum { none, zon, path } = .none;
185186 var override_install_prefix: ?[]const u8 = null;
186187 var override_lib_dir: ?[]const u8 = null;
187188 var override_bin_dir: ?[]const u8 = null;
......@@ -409,6 +410,8 @@ pub fn main(init: process.Init.Minimal) !void {
409410 webui_listen = Io.net.IpAddress.parseLiteral(addr_str) catch |err| {
410411 fatal("invalid web UI address {q}: {t}", .{ addr_str, err });
411412 };
413 } else if (mem.eql(u8, arg, "--debug-target")) {
414 debug_target = nextArgOrFatal(args, &arg_i);
412415 } else if (mem.eql(u8, arg, "--debug-log")) {
413416 try graph.debug_log_scopes.append(arena, nextArgOrFatal(args, &arg_i));
414417 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
......@@ -551,9 +554,9 @@ pub fn main(init: process.Init.Minimal) !void {
551554 io,
552555 cwd_path,
553556 unresolved_path,
554 .@"local_cache",
557 .@"local cache",
555558 ) else .{
556 .path = try Dir.path.join(arena, &.{build_root.directory.path orelse ".", default_local_zig_cache_basename}),
559 .path = try Dir.path.join(arena, &.{ build_root.directory.path orelse ".", default_local_zig_cache_basename }),
557560 .handle = try build_root.directory.handle.createDirPathOpen(io, default_local_zig_cache_basename, .{}),
558561 };
559562 graph.cache = .{
......@@ -583,10 +586,13 @@ pub fn main(init: process.Init.Minimal) !void {
583586 });
584587 defer main_progress_node.end();
585588
586 {
589 const scanned_config: ScannedConfig = sc: {
587590 // Cache lookup for configure options. If we get a match, we can skip
588591 // execution of the configure script. If not, we get the file path to pass
589592 // to the configure process.
593 //
594 // In the hot path, we only check this cache, which means that also
595 // configure source files need to go in here.
590596 var config_man = graph.cache.obtain();
591597 defer config_man.deinit();
592598
......@@ -597,28 +603,6 @@ pub fn main(init: process.Init.Minimal) !void {
597603 // a `zig build --cache-poison=ignored`.
598604 config_man.hash.add(cache_poison == .ignored);
599605
600 // Normally the build runner is compiled for the host target but here is
601 // some code to help when debugging edits to the build runner so that you
602 // can make sure it compiles successfully on other targets.
603 const resolved_target: Package.Module.ResolvedTarget = t: {
604 if (debug_target) |triple| {
605 const target_query = try std.Target.Query.parse(.{ .arch_os_abi = triple });
606 config_man.hash.addBytes(triple);
607 break :t .{
608 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
609 .is_native_os = false,
610 .is_native_abi = false,
611 .is_explicit_dynamic_linker = false,
612 };
613 }
614 break :t .{
615 .result = std.zig.resolveTargetQueryOrFatal(io, .{}),
616 .is_native_os = true,
617 .is_native_abi = true,
618 .is_explicit_dynamic_linker = false,
619 };
620 };
621
622606 const pkg_root: Path = if (override_pkg_dir) |p|
623607 .initCwd(p)
624608 else if (system_pkg_dir_path) |p|
......@@ -659,10 +643,7 @@ pub fn main(init: process.Init.Minimal) !void {
659643 }
660644 defer Fork.deinitList(forks.items);
661645
662 var file_system_inputs: std.ArrayList(u8) = .empty;
663 defer file_system_inputs.deinit(gpa);
664
665 var build_configurer_argv: std.ArrayList(u8) = .empty;
646 var build_configurer_argv: std.ArrayList([]const u8) = .empty;
666647 defer build_configurer_argv.deinit(gpa);
667648
668649 var dependencies_source: std.ArrayList(u8) = .empty;
......@@ -678,16 +659,28 @@ pub fn main(init: process.Init.Minimal) !void {
678659 .sub_path = build_root.build_zig_basename,
679660 };
680661
662 const configurer_exe_name = "configurer";
663
681664 try build_configurer_argv.appendSlice(gpa, &.{
682665 graph.zig_exe, "build-exe", //
683666 "--cache-dir", graph.local_cache_root.path orelse ".", //
684667 "--global-cache-dir", graph.global_cache_root.path orelse ".", //
685668 "--zig-lib-dir", graph.zig_lib_directory.path orelse ".", //
686 "--name", "configurer", //
669 "--name", configurer_exe_name, //
687670 "-fsingle-threaded", //
688671 });
672
673 // Normally the build runner is compiled for the host target but here is
674 // some code to help when debugging edits to the build runner so that you
675 // can make sure it compiles successfully on other targets.
676 const target_arch_os_abi: ?[]const u8 = if (debug_target) |triple| t: {
677 config_man.hash.addBytes(triple);
678 try build_configurer_argv.appendSlice(gpa, &.{ "-target", triple });
679 break :t triple;
680 } else null;
681
689682 if (graph.libc_file) |libc_file| {
690 try build_configurer_argv.appendSlice(gpa, &.{ "--libc", libc_file});
683 try build_configurer_argv.appendSlice(gpa, &.{ "--libc", libc_file });
691684 }
692685 if (graph.reference_trace) |n| {
693686 try build_configurer_argv.append(gpa, try allocPrint(arena, "-freference-trace={d}", .{n}));
......@@ -737,9 +730,6 @@ pub fn main(init: process.Init.Minimal) !void {
737730 // We want to release all the locks before executing the child process, so we make a nice
738731 // big block here to ensure the cleanup gets run when we extract out our argv.
739732 {
740
741
742
743733 {
744734 const fetch_prog_node = main_progress_node.start("Fetch Packages", 0);
745735 defer fetch_prog_node.end();
......@@ -820,12 +810,12 @@ pub fn main(init: process.Init.Minimal) !void {
820810 var any_unused = false;
821811 for (fork_set.keys()) |*fork| {
822812 if (fork.uses == 0) {
823 std.log.err("fork {f} matched no {s} packages", .{
813 log.err("fork {f} matched no {s} packages", .{
824814 fork.path, fork.manifest.name,
825815 });
826816 any_unused = true;
827817 } else {
828 std.log.info("fork {f} matched {d} {s} packages", .{
818 log.info("fork {f} matched {d} {s} packages", .{
829819 fork.path, fork.uses, fork.manifest.name,
830820 });
831821 }
......@@ -842,16 +832,16 @@ pub fn main(init: process.Init.Minimal) !void {
842832 process.exit(1);
843833 }
844834
845 if (fetch_only) return cleanExit(io);
835 if (fetch_only) return process.cleanExit(io);
846836
847837 // Create the dependencies.zig file for configurer to
848838 // obtain via `@import("@dependencies")`.
849839 {
850840 {
851841 dependencies_source.clearRetainingCapacity();
852 var source_writer: Io.Writer.Allocating = .fromArrayList(&dependencies_source);
842 var source_writer: Io.Writer.Allocating = .fromArrayList(gpa, &dependencies_source);
853843 defer dependencies_source = source_writer.toArrayList();
854 job_queue.createDependenciesSource(&dependencies_source) catch |err| switch (err) {
844 job_queue.createDependenciesSource(&source_writer.writer) catch |err| switch (err) {
855845 error.WriteFailed => return error.OutOfMemory,
856846 };
857847 }
......@@ -862,17 +852,18 @@ pub fn main(init: process.Init.Minimal) !void {
862852 const hex_digest = hh.final();
863853 const dependencies_zig_path: Path = .{
864854 .root_dir = graph.local_cache_root,
865 .sub_path = try allocPrint(arena, "o/{s}/dependencies.zig", .{ &hex_digest }),
855 .sub_path = try allocPrint(arena, "o/{s}/dependencies.zig", .{&hex_digest}),
866856 };
867857 var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic(
868858 io,
869 dependencies_zig_path.sub_path, .{ .make_path = true, .replace = true },
859 dependencies_zig_path.sub_path,
860 .{ .make_path = true, .replace = true },
870861 );
871862 defer atomic_file.deinit(io);
872863 atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err|
873864 fatal("writing dependencies.zig contents: {t}", .{err});
874865 atomic_file.replace(io) catch |err|
875 fatal("replacing {f}: {t}", .{dependencies_zig_path, err});
866 fatal("replacing {f}: {t}", .{ dependencies_zig_path, err });
876867
877868 deps_mod.root_path = try dependencies_zig_path.toString(arena);
878869 }
......@@ -914,7 +905,7 @@ pub fn main(init: process.Init.Minimal) !void {
914905 global_cache_directory,
915906 dep,
916907 ) orelse continue;
917 const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue;
908 const dep_mod = job_queue.table.get(dep_digest).?.cli_module orelse continue;
918909 const name_cloned = try arena.dupe(u8, name);
919910 mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod);
920911 }
......@@ -948,23 +939,30 @@ pub fn main(init: process.Init.Minimal) !void {
948939
949940 try build_configurer_argv.append(gpa, "--listen=-");
950941
951 file_system_inputs.clearRetainingCapacity();
952 execute_child(build_configurer_argv, &file_system_inputs);
953
954 const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?);
955 const exe_path: Path = .{
956 .root_dir = dirs.local_cache,
957 .sub_path = try allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }),
942 const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{
943 .argv = build_configurer_argv.items,
944 .cache_root = graph.local_cache_root,
945 .root_name = configurer_exe_name,
946 .environ_map = &graph.environ_map,
947 .cache_manifest = &config_man,
948 .arch_os_abi = target_arch_os_abi,
949 })) |p| p else |err| switch (err) {
950 error.AlreadyReported => process.exit(1),
951 // If the file system inputs are populated, we can
952 // still watch for changes and try again.
953 error.FailedButCacheIntact => @panic("TODO"),
954 error.Canceled, error.OutOfMemory => |e| return e,
958955 };
959 _ = try config_man.addFilePath(exe_path, null);
960 configure_argv.items[0] = try exe_path.toString(arena);
956 defer gpa.free(configure_exe_path.sub_path);
957
958 configure_argv.items[0] = try configure_exe_path.toString(arena);
961959
962960 switch (cache_poison) {
963961 .pure, .disallowed, .ignored => if (try config_man.hit()) {
964962 const digest = config_man.final();
965963 break :cp .{
966964 .{
967 .root_dir = dirs.local_cache,
965 .root_dir = graph.local_cache_root,
968966 .sub_path = try allocPrint(arena, "c/{s}", .{&digest}),
969967 },
970968 false,
......@@ -975,14 +973,15 @@ pub fn main(init: process.Init.Minimal) !void {
975973 }
976974
977975 if (!process.can_spawn) {
978 const cmd = try std.mem.join(arena, " ", configure_argv.items);
979 fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd });
976 fatal("cannot spawn command on {t}: {f}", .{ native_os, @as(std.zig.SubprocessCommand, .{
977 .argv = configure_argv.items,
978 }) });
980979 }
981980
982981 const rand_int = randInt(io, u64);
983982 const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
984983 const config_tmp_path: Path = .{
985 .root_dir = dirs.local_cache,
984 .root_dir = graph.local_cache_root,
986985 .sub_path = tmp_dir_sub_path,
987986 };
988987 const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile(
......@@ -995,7 +994,7 @@ pub fn main(init: process.Init.Minimal) !void {
995994 const term = term: {
996995 const child_node = main_progress_node.start("Run Configure Script", 0);
997996 defer child_node.end();
998 var child = std.process.spawn(io, .{
997 var child = process.spawn(io, .{
999998 .argv = configure_argv.items,
1000999 .stdout = .{ .file = config_tmp_file },
10011000 .progress_node = child_node,
......@@ -1006,8 +1005,9 @@ pub fn main(init: process.Init.Minimal) !void {
10061005 };
10071006 if (!term.success()) {
10081007 // Failure to produce the configuration file.
1009 const cmd = try std.mem.join(arena, " ", configure_argv.items);
1010 fatal("the following configure command {f}:\n{s}", .{ term, cmd });
1008 fatal("configure command {f}: {f}", .{ term, @as(std.zig.SubprocessCommand, .{
1009 .argv = configure_argv.items,
1010 }) });
10111011 }
10121012 // Even though the file is designed to be sent directly to make
10131013 // runner, we must load it now because:
......@@ -1015,17 +1015,16 @@ pub fn main(init: process.Init.Minimal) !void {
10151015 // add them to `config_man` before obtaining the final digest.
10161016 // * If it contains a set of lazy packages that need to be
10171017 // fetched, we need to fetch those now and re-run configure.
1018 var configuration = std.Build.Configuration.loadFile(arena, io, config_tmp_file) catch |err|
1018 var configuration = Configuration.loadFile(arena, io, config_tmp_file) catch |err|
10191019 fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err });
10201020
10211021 if (configuration.unlazy_deps.len != 0) {
1022 if (!dev.env.supports(.fetch_command)) process.exit(1);
10231022 var any_errors = false;
10241023 for (configuration.unlazy_deps) |hash_string| {
10251024 const hash = hash_string.slice(&configuration);
10261025 assert(hash.len != 0);
10271026 if (hash.len > Package.Hash.max_len) {
1028 std.log.err("invalid digest (length {d} exceeds maximum): {q}", .{ hash.len, hash });
1027 log.err("invalid digest (length {d} exceeds maximum): {q}", .{ hash.len, hash });
10291028 any_errors = true;
10301029 continue;
10311030 }
......@@ -1037,33 +1036,17 @@ pub fn main(init: process.Init.Minimal) !void {
10371036 // cannot be fetched by Zig.
10381037 const s = Dir.path.sep_str;
10391038 for (unlazy_set.keys()) |*hash| {
1040 std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() });
1039 log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() });
10411040 }
1042 std.log.info("remote package fetching disabled due to --system mode", .{});
1043 std.log.info("dependencies might be avoidable depending on build configuration", .{});
1041 log.info("remote package fetching disabled due to --system mode", .{});
1042 log.info("dependencies might be avoidable depending on build configuration", .{});
10441043 process.exit(1);
10451044 }
10461045 continue :cp;
10471046 }
10481047
1049 for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| {
1050 const conf_path: std.Build.Configuration.Path = .{ .base = base, .sub = sub };
1051 try config_man.addPathPost(conf_path.toCachePath(&configuration, arena));
1052 }
1053
1054 // We need to add to the configuration cache the source files of
1055 // configurer itself, so that the maker process can watch the file system
1056 // for those changes and restart itself. By doing this, we make it
1057 // possible to bypass creating a Compilation for configurer on
1058 // Configuration cache hit.
1059 {
1060 var it = mem.splitScalar(u8, file_system_inputs.items, 0);
1061 while (it.next()) |input| {
1062 _ = try config_man.addPrefixedPathPost(.{
1063 .prefix = input[0],
1064 .sub_path = input[1..],
1065 });
1066 }
1048 for (configuration.path_deps) |path_dep| {
1049 try config_man.addPathPost(path_dep.toCachePath(&configuration, arena));
10671050 }
10681051
10691052 // If it is poisoned, there is no point in moving it to cached
......@@ -1073,7 +1056,7 @@ pub fn main(init: process.Init.Minimal) !void {
10731056 } else {
10741057 const digest = config_man.final();
10751058 const final_path: Path = .{
1076 .root_dir = dirs.local_cache,
1059 .root_dir = graph.local_cache_root,
10771060 .sub_path = try allocPrint(arena, "c/{s}", .{&digest}),
10781061 };
10791062 Io.Dir.rename(
......@@ -1107,33 +1090,27 @@ pub fn main(init: process.Init.Minimal) !void {
11071090 }
11081091 };
11091092
1110 {
1111 // Release all file system locks just before running the maker process.
1112 var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null;
1113 defer if (configuration_lock) |*l| l.release(io);
1114
1115 if (print_configuration_path) {
1116 var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer);
1117 stdout_writer.interface.print("{f}\n", .{configuration_path}) catch
1118 fatal("failed printing cache file path: {t}", .{stdout_writer.err.?});
1119 stdout_writer.flush() catch |err|
1120 fatal("failed printing cache file path: {t}", .{err});
1121 return cleanExit(io);
1122 }
1123 const make_runner = make_runner_task.await(io) catch |err| fatal("failed compiling maker: {t}", .{err});
1093 // Hang on to the configuration file lock until we finish loading the configuration file.
1094 var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null;
1095 defer if (configuration_lock) |*l| l.release(io);
11241096
1125 make_argv.items[0] = try make_runner.exe_path.toString(arena);
1126 make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena);
1097 switch (print_configuration) {
1098 .path => {
1099 initStdoutWriter(io).print("{f}\n", .{configuration_path}) catch
1100 fatal("failed printing cache file path: {t}", .{stdout_writer_allocation.err.?});
1101 stdout_writer_allocation.flush() catch |err|
1102 fatal("failed printing cache file path: {t}", .{err});
1103 return process.cleanExit(io);
1104 },
1105 .none, .zon => {},
11271106 }
1128 }
11291107
1130 const scanned_config: ScannedConfig = sc: {
11311108 const configuration = c: {
1132 var file = cwd.openFile(io, configure_path, .{}) catch |err|
1133 fatal("failed to open configuration file {s}: {t}", .{ configure_path, err });
1109 var file = configuration_path.root_dir.handle.openFile(io, configuration_path.sub_path, .{}) catch |err|
1110 fatal("failed to open configuration file {f}: {t}", .{ configuration_path, err });
11341111 defer file.close(io);
11351112 break :c Configuration.loadFile(arena, io, file) catch |err|
1136 fatal("failed to load configuration file {s}: {t}", .{ configure_path, err });
1113 fatal("failed to load configuration file {f}: {t}", .{ configuration_path, err });
11371114 };
11381115 // Technically if the configuration is marked as poisoned, we could
11391116 // already delete the file now, but we leave it around in case the
......@@ -1159,37 +1136,32 @@ pub fn main(init: process.Init.Minimal) !void {
11591136 break :sc .{
11601137 .configuration = configuration,
11611138 .top_level_steps = top_level_steps,
1162 .path = configure_path,
1139 .path = configuration_path,
11631140 };
11641141 };
11651142
11661143 if (help_menu) {
1167 const w = initStdoutWriter(io);
1168 scanned_config.printUsage(&graph, w) catch |err| switch (err) {
1144 scanned_config.printUsage(&graph, initStdoutWriter(io)) catch |err| switch (err) {
11691145 error.WriteFailed => return stdout_writer_allocation.err.?,
11701146 else => |e| return e,
11711147 };
1172 w.flush() catch return stdout_writer_allocation.err.?;
1148 try stdout_writer_allocation.flush();
11731149 return cleanExit(io, &scanned_config);
11741150 } else if (steps_menu) {
1175 const w = initStdoutWriter(io);
1176 scanned_config.printSteps(&graph, w) catch |err| switch (err) {
1151 scanned_config.printSteps(&graph, initStdoutWriter(io)) catch |err| switch (err) {
11771152 error.WriteFailed => return stdout_writer_allocation.err.?,
11781153 else => |e| return e,
11791154 };
1180 w.flush() catch return stdout_writer_allocation.err.?;
1155 try stdout_writer_allocation.flush();
11811156 return cleanExit(io, &scanned_config);
11821157 } else switch (print_configuration) {
11831158 .none => {},
11841159 .zon => {
1185 const w = initStdoutWriter(io);
1186 scanned_config.print(w) catch return stdout_writer_allocation.err.?;
1187 w.flush() catch return stdout_writer_allocation.err.?;
1160 scanned_config.print(initStdoutWriter(io)) catch return stdout_writer_allocation.err.?;
1161 try stdout_writer_allocation.flush();
11881162 return cleanExit(io, &scanned_config);
11891163 },
1190 .path => {
1191 @panic("TODO");
1192 },
1164 .path => unreachable,
11931165 }
11941166
11951167 if (webui_listen != null) {
......@@ -1204,7 +1176,7 @@ pub fn main(init: process.Init.Minimal) !void {
12041176 .root_dir = .cwd(),
12051177 .sub_path = cwd_relative,
12061178 } else .{
1207 .root_dir = build_root_directory,
1179 .root_dir = graph.build_root_directory,
12081180 .sub_path = "zig-out",
12091181 };
12101182
......@@ -1274,7 +1246,7 @@ pub fn main(init: process.Init.Minimal) !void {
12741246
12751247 var w: Watch = w: {
12761248 if (!watch) break :w undefined;
1277 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag});
1249 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{native_os});
12781250 break :w try .init(&maker);
12791251 };
12801252
......@@ -1366,11 +1338,7 @@ pub fn main(init: process.Init.Minimal) !void {
13661338 }
13671339}
13681340
1369fn cmdFetch(
1370 gpa: Allocator,
1371 graph: *Graph,
1372 args: []const []const u8
1373) !void {
1341fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
13741342 const environ_map = &graph.environ_map;
13751343 const io = graph.io;
13761344 const arena = graph.arena;
......@@ -1392,7 +1360,7 @@ fn cmdFetch(
13921360 if (mem.startsWith(u8, arg, "-")) {
13931361 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
13941362 try Io.File.stdout().writeStreamingAll(io, usage_fetch);
1395 return cleanExit(io);
1363 return process.cleanExit(io);
13961364 } else if (mem.eql(u8, arg, "--global-cache-dir")) {
13971365 override_global_cache_dir = nextArgOrFatal(args, &arg_i);
13981366 } else if (mem.eql(u8, arg, "--cache-dir")) {
......@@ -1500,7 +1468,7 @@ fn cmdFetch(
15001468 .oom_flag = false,
15011469 .latest_commit = null,
15021470
1503 .module = null,
1471 .cli_module = null,
15041472 };
15051473 defer fetch.deinit();
15061474
......@@ -1526,10 +1494,10 @@ fn cmdFetch(
15261494 const name = switch (save) {
15271495 .no => {
15281496 var data: [2][]const u8 = .{ package_hash_slice, "\n" };
1529 const w = initStdoutWriter();
1530 try w.writeVecAll(&data);
1531 try w.flush();
1532 return cleanExit(io);
1497 const w = initStdoutWriter(io);
1498 w.writeVecAll(&data) catch return stdout_writer_allocation.err.?;
1499 try stdout_writer_allocation.flush();
1500 return process.cleanExit(io);
15331501 },
15341502 .yes, .exact => |name| name: {
15351503 if (name) |n| break :name n;
......@@ -1567,14 +1535,14 @@ fn cmdFetch(
15671535 // the refspec may already be fully resolved
15681536 if (std.mem.eql(u8, target_ref, latest_commit_hex)) break :resolved;
15691537
1570 std.log.info("resolved ref {q} to commit {s}", .{ target_ref, latest_commit_hex });
1538 log.info("resolved ref {q} to commit {s}", .{ target_ref, latest_commit_hex });
15711539
15721540 // include the original refspec in a query parameter, could be used to check for updates
15731541 uri.query = .{ .percent_encoded = try allocPrint(arena, "ref={f}", .{
15741542 std.fmt.alt(fragment, .formatEscaped),
15751543 }) };
15761544 } else {
1577 std.log.info("resolved to commit {s}", .{latest_commit_hex});
1545 log.info("resolved to commit {s}", .{latest_commit_hex});
15781546 }
15791547
15801548 // replace the refspec with the resolved commit SHA
......@@ -1613,7 +1581,7 @@ fn cmdFetch(
16131581 switch (dep.location) {
16141582 .url => |u| {
16151583 if (mem.eql(u8, h, package_hash_slice) and mem.eql(u8, u, saved_path_or_url)) {
1616 std.log.info("existing dependency named {q} is up-to-date", .{name});
1584 log.info("existing dependency named {q} is up-to-date", .{name});
16171585 process.exit(0);
16181586 }
16191587 },
......@@ -1661,7 +1629,7 @@ fn cmdFetch(
16611629 fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err });
16621630 };
16631631
1664 return cleanExit(io);
1632 return process.cleanExit(io);
16651633}
16661634
16671635const usage_fetch =
......@@ -1707,13 +1675,10 @@ const usage_init =
17071675 \\
17081676;
17091677
1710fn cmdInit(
1711 gpa: Allocator,
1712 graph: *Graph,
1713 args: []const []const u8
1714) !void {
1678fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
17151679 const arena = graph.arena;
17161680 const io = graph.io;
1681 const default_build_zig_basename = std.zig.build_zig_basename;
17171682
17181683 var template: enum { example, minimal } = .example;
17191684 {
......@@ -1725,7 +1690,7 @@ fn cmdInit(
17251690 template = .minimal;
17261691 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
17271692 try Io.File.stdout().writeStreamingAll(io, usage_init);
1728 return cleanExit(io);
1693 return process.cleanExit(io);
17291694 } else {
17301695 fatal("unrecognized parameter: {q}", .{arg});
17311696 }
......@@ -1749,7 +1714,7 @@ fn cmdInit(
17491714
17501715 const s = Dir.path.sep_str;
17511716 const template_paths = [_][]const u8{
1752 Package.build_zig_basename,
1717 default_build_zig_basename,
17531718 Package.Manifest.basename,
17541719 "src" ++ s ++ "main.zig",
17551720 "src" ++ s ++ "root.zig",
......@@ -1758,20 +1723,20 @@ fn cmdInit(
17581723
17591724 for (template_paths) |template_path| {
17601725 if (templates.write(arena, io, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| {
1761 std.log.info("created {s}", .{template_path});
1726 log.info("created {s}", .{template_path});
17621727 ok_count += 1;
17631728 } else |err| switch (err) {
1764 error.PathAlreadyExists => std.log.info("preserving already existing file: {s}", .{
1729 error.PathAlreadyExists => log.info("preserving already existing file: {s}", .{
17651730 template_path,
17661731 }),
1767 else => std.log.err("unable to write {s}: {s}\n", .{ template_path, @errorName(err) }),
1732 else => log.err("unable to write {s}: {t}", .{ template_path, err }),
17681733 }
17691734 }
17701735
17711736 if (ok_count == template_paths.len) {
1772 std.log.info("see `zig build --help` for a menu of options", .{});
1737 log.info("see `zig build --help` for a menu of options", .{});
17731738 }
1774 return cleanExit(io);
1739 return process.cleanExit(io);
17751740 },
17761741 .minimal => {
17771742 writeSimpleTemplateFile(io, Package.Manifest.basename,
......@@ -1791,7 +1756,7 @@ fn cmdInit(
17911756 else => fatal("failed to create {q}: {t}", .{ Package.Manifest.basename, err }),
17921757 error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Package.Manifest.basename}),
17931758 };
1794 writeSimpleTemplateFile(io, Package.build_zig_basename,
1759 writeSimpleTemplateFile(io, default_build_zig_basename,
17951760 \\const std = @import("std");
17961761 \\
17971762 \\pub fn build(b: *std.Build) void {{
......@@ -1799,24 +1764,22 @@ fn cmdInit(
17991764 \\}}
18001765 \\
18011766 , .{}) catch |err| switch (err) {
1802 else => fatal("failed to create {q}: {t}", .{ Package.build_zig_basename, err }),
1767 else => fatal("failed to create {q}: {t}", .{ default_build_zig_basename, err }),
18031768 // `build.zig` already existing is okay: the user has just used `zig init` to set up
18041769 // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal.
18051770 error.PathAlreadyExists => {
1806 std.log.info("successfully populated {q}, preserving existing {q}", .{
1807 Package.Manifest.basename, Package.build_zig_basename,
1771 log.info("successfully populated {q}, preserving existing {q}", .{
1772 Package.Manifest.basename, default_build_zig_basename,
18081773 });
1809 return cleanExit(io);
1774 return process.cleanExit(io);
18101775 },
18111776 };
1812 std.log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, Package.build_zig_basename });
1813 return cleanExit(io);
1777 log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, default_build_zig_basename });
1778 return process.cleanExit(io);
18141779 },
18151780 }
18161781}
18171782
1818
1819
18201783fn markFailedStepsDirty(maker: *Maker) void {
18211784 const all_steps = maker.step_stack.keys();
18221785
......@@ -1918,9 +1881,7 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void {
19181881 }
19191882 if (any_problems) {
19201883 if (maker.max_rss_is_default) {
1921 std.log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{
1922 max_needed,
1923 });
1884 log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{max_needed});
19241885 }
19251886 return error.InsufficientMemory;
19261887 }
......@@ -2012,12 +1973,12 @@ fn makeStepNames(
20121973 }
20131974
20141975 if (fuzz) |mode| blk: {
2015 switch (builtin.os.tag) {
1976 switch (native_os) {
20161977 // Current implementation depends on two things that need to be ported to Windows:
20171978 // * Memory-mapping to share data between the fuzzer and build runner.
20181979 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving
20191980 // many addresses to source locations).
2020 .windows => fatal("--fuzz not yet implemented for {t}", .{builtin.os.tag}),
1981 .windows => fatal("--fuzz not yet implemented for {t}", .{native_os}),
20211982 else => {},
20221983 }
20231984 if (@bitSizeOf(usize) != 64) {
......@@ -2781,23 +2742,23 @@ pub fn printErrorMessages(
27812742 try writer.writeByte('\n');
27822743}
27832744
2784fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
2745fn nextArg(args: []const []const u8, idx: *usize) ?[]const u8 {
27852746 if (idx.* >= args.len) return null;
27862747 defer idx.* += 1;
27872748 return args[idx.*];
27882749}
27892750
2790fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {
2751fn nextArgOrFatal(args: []const []const u8, idx: *usize) []const u8 {
27912752 return nextArg(args, idx) orelse fatalWithHint("expected argument after {q}", .{args[idx.* - 1]});
27922753}
27932754
2794fn prefixedArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, prefix: []const u8) []const u8 {
2755fn prefixedArgOrFatal(args: []const []const u8, index_ptr: *usize, prefix: []const u8) []const u8 {
27952756 const arg = args[index_ptr.*];
27962757 if (mem.cutPrefix(u8, arg, prefix)) |rest| return rest;
2797 fatal("expected {q} to begin with {q}", .{arg, prefix});
2758 fatal("expected {q} to begin with {q}", .{ arg, prefix });
27982759}
27992760
2800fn argsRest(args: []const [:0]const u8, idx: usize) ?[]const [:0]const u8 {
2761fn argsRest(args: []const []const u8, idx: usize) ?[]const []const u8 {
28012762 if (idx >= args.len) return null;
28022763 return args[idx..];
28032764}
......@@ -3158,8 +3119,8 @@ fn removePoisonedConfiguration(io: Io, scanned_config: *const ScannedConfig) voi
31583119 if (scanned_config.configuration.poisoned) {
31593120 // This configuration file was good for only 1 invocation of the maker
31603121 // process. Delete it to save space on disk.
3161 Io.Dir.cwd().deleteFile(io, scanned_config.path) catch |err|
3162 log.warn("failed deleting poisoned configuration file {s}: {t}", .{ scanned_config.path, err });
3122 scanned_config.path.root_dir.handle.deleteFile(io, scanned_config.path.sub_path) catch |err|
3123 log.warn("failed deleting poisoned configuration file {f}: {t}", .{ scanned_config.path, err });
31633124 }
31643125}
31653126
......@@ -3228,8 +3189,8 @@ fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !Build
32283189 } else |err| switch (err) {
32293190 error.FileNotFound => {
32303191 dirname = Dir.path.dirname(dirname) orelse {
3231 std.log.info("initialize {s} template file with \"zig init\"", .{ std.zig.build_zig_basename });
3232 std.log.info("see \"zig --help\" for more options", .{});
3192 log.info("initialize {s} template file with \"zig init\"", .{std.zig.build_zig_basename});
3193 log.info("see \"zig --help\" for more options", .{});
32333194 fatal("no build.zig file found, in the current directory or any parent directories", .{});
32343195 };
32353196 continue;
......@@ -3266,7 +3227,7 @@ const Fork = struct {
32663227 error.Canceled => |e| return e,
32673228 error.AlreadyReported => fork.failed = true,
32683229 else => |e| {
3269 std.log.err("failed to load fork at {f}: {t}", .{ fork.path, e });
3230 log.err("failed to load fork at {f}: {t}", .{ fork.path, e });
32703231 fork.failed = true;
32713232 },
32723233 };
......@@ -3299,7 +3260,7 @@ const Fork = struct {
32993260 return error.AlreadyReported;
33003261 },
33013262 else => |e| {
3302 std.log.err("failed to load package manifest {f}: {t}", .{ manifest_path, e });
3263 log.err("failed to load package manifest {f}: {t}", .{ manifest_path, e });
33033264 return error.AlreadyReported;
33043265 },
33053266 };
......@@ -3527,4 +3488,3 @@ fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates {
35273488 .buffer = std.array_list.Managed(u8).init(gpa),
35283489 };
35293490}
3530
lib/compiler/Maker/Fetch.zig+7-7
......@@ -46,7 +46,7 @@ const ascii = std.ascii;
4646const Allocator = std.mem.Allocator;
4747const Cache = std.Build.Cache;
4848const git = @import("Fetch/git.zig");
49const Package = @import("../Package.zig");
49const Package = @import("Package.zig");
5050const Manifest = Package.Manifest;
5151const ErrorBundle = std.zig.ErrorBundle;
5252
......@@ -341,10 +341,10 @@ pub const JobQueue = struct {
341341 .{ std.zig.fmtString(name), std.zig.fmtString(h.toSlice()) },
342342 );
343343 }
344 try w.appendSlice("};\n");
344 try w.writeAll("};\n");
345345 }
346346
347 pub fn createEmptyDependenciesSource(w: *Io.Writer) Io.Writer!void {
347 pub fn createEmptyDependenciesSource(w: *Io.Writer) Io.Writer.Error!void {
348348 try w.writeAll(
349349 \\pub const packages = struct {};
350350 \\pub const root_deps: []const struct { []const u8, []const u8 } = &.{};
......@@ -858,14 +858,14 @@ pub fn computedPackageHash(f: *const Fetch) Package.Hash {
858858fn checkBuildFileExistence(f: *Fetch) RunError!void {
859859 const io = f.job_queue.io;
860860 const eb = &f.error_bundle;
861 if (f.package_root.access(io, Package.build_zig_basename, .{})) |_| {
861 if (f.package_root.access(io, std.zig.build_zig_basename, .{})) |_| {
862862 f.has_build_zig = true;
863863 } else |err| switch (err) {
864864 error.FileNotFound => {},
865865 else => |e| {
866866 try eb.addRootErrorMessage(.{
867 .msg = try eb.printString("unable to access '{f}{s}': {t}", .{
868 f.package_root, Package.build_zig_basename, e,
867 .msg = try eb.printString("unable to access {f}/{s}: {t}", .{
868 f.package_root, std.zig.build_zig_basename, e,
869869 }),
870870 });
871871 return error.FetchFailed;
......@@ -1781,7 +1781,7 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute
17811781 )),
17821782 };
17831783
1784 if (std.mem.eql(u8, entry_pkg_path, Package.build_zig_basename))
1784 if (std.mem.eql(u8, entry_pkg_path, std.zig.build_zig_basename))
17851785 f.has_build_zig = true;
17861786
17871787 const fs_path = try arena.dupe(u8, entry.path);
lib/compiler/Maker/Package.zig+1-1
......@@ -1,7 +1,7 @@
11const std = @import("std");
22const assert = std.debug.assert;
33
4pub const Fetch = @import("Package/Fetch.zig");
4pub const Fetch = @import("Fetch.zig");
55pub const Manifest = @import("Package/Manifest.zig");
66
77pub const Fingerprint = packed struct(u64) {
lib/compiler/Maker/ScannedConfig.zig+1-1
......@@ -9,7 +9,7 @@ const Graph = @import("Graph.zig");
99
1010configuration: Configuration,
1111top_level_steps: std.array_hash_map.String(Configuration.Step.Index),
12path: []const u8,
12path: std.Build.Cache.Path,
1313
1414pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
1515 std.log.err("TODO also print paths", .{});
lib/compiler/Maker/Step.zig+1-1
......@@ -584,7 +584,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi
584584 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
585585 return s.fail(
586586 maker,
587 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
587 "zig version mismatch build runner vs compiler: {q} vs {q}",
588588 .{ builtin.zig_version_string, body },
589589 );
590590 }
lib/compiler/Maker/WebServer.zig+4-142
......@@ -582,8 +582,8 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
582582 const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
583583
584584 const maker = ws.maker;
585 const gpa = maker.gpa;
586585 const graph = maker.graph;
586 const gpa = maker.gpa;
587587 const io = graph.io;
588588
589589 const main_src_path: Cache.Path = .{
......@@ -622,151 +622,13 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
622622 "--listen=-",
623623 });
624624
625 var child = try std.process.spawn(io, .{
625 return std.zig.buildExeSubprocess(gpa, io, .{
626626 .argv = argv.items,
627 .environ_map = &graph.environ_map,
628 .stdin = .pipe,
629 .stdout = .pipe,
630 .stderr = .pipe,
631 });
632 defer child.kill(io);
633
634 var stderr_task = try io.concurrent(readStreamAlloc, .{ gpa, io, child.stderr.?, .unlimited });
635 defer if (stderr_task.cancel(io)) |slice| gpa.free(slice) else |_| {};
636
637 var stdout_buffer: [512]u8 = undefined;
638 var stdout_reader: Io.File.Reader = .initStreaming(child.stdout.?, io, &stdout_buffer);
639 const stdout = &stdout_reader.interface;
640
641 {
642 var w = child.stdin.?.writer(io, &.{});
643 w.interface.writeStruct(std.zig.Client.Message.Header{ .tag = .update, .bytes_len = 0 }, .little) catch |err| switch (err) {
644 error.WriteFailed => return w.err.?,
645 };
646 w.interface.writeStruct(std.zig.Client.Message.Header{ .tag = .exit, .bytes_len = 0 }, .little) catch |err| switch (err) {
647 error.WriteFailed => return w.err.?,
648 };
649 }
650
651 const Header = std.zig.Server.Message.Header;
652
653 var result: ?Cache.Path = null;
654 var result_error_bundle = std.zig.ErrorBundle.empty;
655 var body_buffer: std.ArrayList(u8) = .empty;
656 defer body_buffer.deinit(gpa);
657
658 while (true) {
659 const header = stdout.takeStruct(Header, .little) catch |err| switch (err) {
660 error.ReadFailed => |e| return e,
661 error.EndOfStream => break,
662 };
663 body_buffer.clearRetainingCapacity();
664 try stdout.appendExact(gpa, &body_buffer, header.bytes_len);
665 const body = body_buffer.items;
666
667 switch (header.tag) {
668 .zig_version => {
669 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
670 return error.ZigProtocolVersionMismatch;
671 }
672 },
673 .error_bundle => {
674 result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body);
675 },
676 .emit_digest => {
677 const EmitDigest = std.zig.Server.Message.EmitDigest;
678 const ebp_hdr: *align(1) const EmitDigest = @ptrCast(body);
679 if (!ebp_hdr.flags.cache_hit) {
680 log.info("source changes detected; rebuilt wasm component", .{});
681 }
682 const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];
683 result = .{
684 .root_dir = graph.global_cache_root,
685 .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)),
686 };
687 },
688 else => {}, // ignore other messages
689 }
690 }
691
692 const stderr_contents = try stderr_task.await(io);
693 if (stderr_contents.len > 0) {
694 std.debug.print("{s}", .{stderr_contents});
695 }
696
697 // Send EOF to stdin.
698 child.stdin.?.close(io);
699 child.stdin = null;
700
701 switch (try child.wait(io)) {
702 .exited => |code| {
703 if (code != 0) {
704 log.err(
705 "the following command exited with error code {d}:\n{s}",
706 .{ code, try std.zig.allocPrintCmd(arena, argv.items, .{}) },
707 );
708 return error.WasmCompilationFailed;
709 }
710 },
711 .signal => |sig| {
712 log.err(
713 "the following command terminated with signal {t}:\n{s}",
714 .{ sig, try std.zig.allocPrintCmd(arena, argv.items, .{}) },
715 );
716 return error.WasmCompilationFailed;
717 },
718 .stopped => |sig| {
719 log.err(
720 "the following command stopped unexpectedly with signal {t}:\n{s}",
721 .{ sig, try std.zig.allocPrintCmd(arena, argv.items, .{}) },
722 );
723 return error.WasmCompilationFailed;
724 },
725 .unknown => {
726 log.err(
727 "the following command terminated unexpectedly:\n{s}",
728 .{try std.zig.allocPrintCmd(arena, argv.items, .{})},
729 );
730 return error.WasmCompilationFailed;
731 },
732 }
733
734 if (result_error_bundle.errorMessageCount() > 0) {
735 try result_error_bundle.renderToStderr(io, .{}, .auto);
736 log.err("the following command failed with {d} compilation errors:\n{s}", .{
737 result_error_bundle.errorMessageCount(),
738 try std.zig.allocPrintCmd(arena, argv.items, .{}),
739 });
740 return error.WasmCompilationFailed;
741 }
742
743 const base_path = result orelse {
744 log.err("child process failed to report result\n{s}", .{
745 try std.zig.allocPrintCmd(arena, argv.items, .{}),
746 });
747 return error.WasmCompilationFailed;
748 };
749 const target = std.zig.system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{
627 .cache_root = graph.global_cache_root,
628 .root_name = root_name,
750629 .arch_os_abi = arch_os_abi,
751630 .cpu_features = cpu_features,
752 }) catch unreachable) catch unreachable;
753 const bin_name = try std.zig.binNameAlloc(arena, .{
754 .root_name = root_name,
755 .cpu_arch = target.cpu.arch,
756 .os_tag = target.os.tag,
757 .ofmt = target.ofmt,
758 .abi = target.abi,
759 .output_mode = .Exe,
760631 });
761 return base_path.join(arena, bin_name);
762}
763
764fn readStreamAlloc(gpa: Allocator, io: Io, file: Io.File, limit: Io.Limit) ![]u8 {
765 var file_reader: Io.File.Reader = .initStreaming(file, io, &.{});
766 return file_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) {
767 error.ReadFailed => return file_reader.err.?,
768 else => |e| return e,
769 };
770632}
771633
772634pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
lib/std/Build/Configuration.zig+1-1
......@@ -1881,7 +1881,7 @@ pub const PathDep = extern struct {
18811881 _ = c;
18821882 _ = arena;
18831883 _ = path;
1884 std.log.err("TODO Configuration.PathDep.toCachePath", .{});
1884 if (true) @panic("TODO Configuration.PathDep.toCachePath");
18851885 }
18861886};
18871887
lib/std/zig.zig+282-58
......@@ -7,6 +7,7 @@ const builtin = @import("builtin");
77const std = @import("std.zig");
88const assert = std.debug.assert;
99const mem = std.mem;
10const log = std.log;
1011const Allocator = std.mem.Allocator;
1112const Io = std.Io;
1213const Writer = std.Io.Writer;
......@@ -721,7 +722,7 @@ pub fn parseTargetQueryOrReportFatalError(
721722 for (diags.arch.?.allCpuModels()) |cpu| {
722723 help_text.print(" {s}\n", .{cpu.name}) catch break :help;
723724 }
724 std.log.info("available CPUs for architecture '{s}':\n{s}", .{
725 log.info("available CPUs for architecture '{s}':\n{s}", .{
725726 @tagName(diags.arch.?), help_text.items,
726727 });
727728 }
......@@ -734,7 +735,7 @@ pub fn parseTargetQueryOrReportFatalError(
734735 for (diags.arch.?.allFeaturesList()) |feature| {
735736 help_text.print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
736737 }
737 std.log.info("available CPU features for architecture '{s}':\n{s}", .{
738 log.info("available CPU features for architecture '{s}':\n{s}", .{
738739 @tagName(diags.arch.?), help_text.items,
739740 });
740741 }
......@@ -747,7 +748,7 @@ pub fn parseTargetQueryOrReportFatalError(
747748 inline for (@typeInfo(std.Target.ObjectFormat).@"enum".field_names) |field_name| {
748749 help_text.print(" {s}\n", .{field_name}) catch break :help;
749750 }
750 std.log.info("available object formats:\n{s}", .{help_text.items});
751 log.info("available object formats:\n{s}", .{help_text.items});
751752 }
752753 std.process.fatal("unknown object format: '{s}'", .{opts.object_format.?});
753754 },
......@@ -758,7 +759,7 @@ pub fn parseTargetQueryOrReportFatalError(
758759 inline for (@typeInfo(std.Target.Cpu.Arch).@"enum".field_names) |field_name| {
759760 help_text.print(" {s}\n", .{field_name}) catch break :help;
760761 }
761 std.log.info("available architectures:\n{s} native\n", .{help_text.items});
762 log.info("available architectures:\n{s} native\n", .{help_text.items});
762763 }
763764 std.process.fatal("unknown architecture: '{s}'", .{diags.unknown_architecture_name.?});
764765 },
......@@ -1182,73 +1183,88 @@ pub const ClangCliParam = struct {
11821183 }
11831184};
11841185
1186/// Deprecated
11851187pub const AllocPrintCmdOptions = struct {
11861188 cwd: ?[]const u8 = null,
11871189 parent_env: ?*const std.process.Environ.Map = null,
11881190 child_env: ?*const std.process.Environ.Map = null,
11891191};
11901192
1193/// Deprecated
11911194pub fn allocPrintCmd(gpa: Allocator, argv: []const []const u8, options: AllocPrintCmdOptions) Allocator.Error![]u8 {
1192 const shell = struct {
1193 fn escape(writer: *Io.Writer, string: []const u8, is_argv0: bool) !void {
1194 for (string) |c| {
1195 if (switch (c) {
1196 else => true,
1197 '%', '+'...':', '@'...'Z', '_', 'a'...'z' => false,
1198 '=' => is_argv0,
1199 }) break;
1200 } else return writer.writeAll(string);
1201
1202 try writer.writeByte('"');
1203 for (string) |c| {
1204 if (switch (c) {
1205 std.ascii.control_code.nul => break,
1206 '!', '"', '$', '\\', '`' => true,
1207 else => !std.ascii.isPrint(c),
1208 }) try writer.writeByte('\\');
1209 switch (c) {
1210 std.ascii.control_code.nul => unreachable,
1211 std.ascii.control_code.bel => try writer.writeByte('a'),
1212 std.ascii.control_code.bs => try writer.writeByte('b'),
1213 std.ascii.control_code.ht => try writer.writeByte('t'),
1214 std.ascii.control_code.lf => try writer.writeByte('n'),
1215 std.ascii.control_code.vt => try writer.writeByte('v'),
1216 std.ascii.control_code.ff => try writer.writeByte('f'),
1217 std.ascii.control_code.cr => try writer.writeByte('r'),
1218 std.ascii.control_code.esc => try writer.writeByte('E'),
1219 ' '...'~' => try writer.writeByte(c),
1220 else => try writer.print("{o:0>3}", .{c}),
1221 }
1222 }
1223 try writer.writeByte('"');
1224 }
1225 };
1226
12271195 var aw: Io.Writer.Allocating = .init(gpa);
12281196 defer aw.deinit();
1229 const writer = &aw.writer;
1230 if (options.cwd) |path| {
1231 writer.print("cd {s} && ", .{path}) catch return error.OutOfMemory;
1197 SubprocessCommand.format(.{
1198 .argv = argv,
1199 .cwd = options.cwd,
1200 .parent_env = options.parent_env,
1201 .child_env = options.child_env,
1202 }, &aw.writer) catch return error.OutOfMemory;
1203 return aw.toOwnedSlice();
1204}
1205
1206fn shellEscape(writer: *Io.Writer, string: []const u8, is_argv0: bool) !void {
1207 for (string) |c| {
1208 if (switch (c) {
1209 else => true,
1210 '%', '+'...':', '@'...'Z', '_', 'a'...'z' => false,
1211 '=' => is_argv0,
1212 }) break;
1213 } else return writer.writeAll(string);
1214
1215 try writer.writeByte('"');
1216 for (string) |c| {
1217 if (switch (c) {
1218 std.ascii.control_code.nul => break,
1219 '!', '"', '$', '\\', '`' => true,
1220 else => !std.ascii.isPrint(c),
1221 }) try writer.writeByte('\\');
1222 switch (c) {
1223 std.ascii.control_code.nul => unreachable,
1224 std.ascii.control_code.bel => try writer.writeByte('a'),
1225 std.ascii.control_code.bs => try writer.writeByte('b'),
1226 std.ascii.control_code.ht => try writer.writeByte('t'),
1227 std.ascii.control_code.lf => try writer.writeByte('n'),
1228 std.ascii.control_code.vt => try writer.writeByte('v'),
1229 std.ascii.control_code.ff => try writer.writeByte('f'),
1230 std.ascii.control_code.cr => try writer.writeByte('r'),
1231 std.ascii.control_code.esc => try writer.writeByte('E'),
1232 ' '...'~' => try writer.writeByte(c),
1233 else => try writer.print("{o:0>3}", .{c}),
1234 }
12321235 }
1233 if (options.child_env) |child_env| {
1234 for (child_env.keys(), child_env.values()) |key, value| {
1235 if (options.parent_env) |parent_env| {
1236 if (parent_env.get(key)) |process_value| {
1237 if (std.mem.eql(u8, value, process_value)) continue;
1236 try writer.writeByte('"');
1237}
1238
1239pub const SubprocessCommand = struct {
1240 argv: []const []const u8,
1241 cwd: ?[]const u8 = null,
1242 parent_env: ?*const std.process.Environ.Map = null,
1243 child_env: ?*const std.process.Environ.Map = null,
1244
1245 pub fn format(sc: SubprocessCommand, w: *Io.Writer) Io.Writer.Error!void {
1246 if (sc.cwd) |path| {
1247 try w.print("cd {s} && ", .{path});
1248 }
1249 if (sc.child_env) |child_env| {
1250 for (child_env.keys(), child_env.values()) |key, value| {
1251 if (sc.parent_env) |parent_env| {
1252 if (parent_env.get(key)) |process_value| {
1253 if (std.mem.eql(u8, value, process_value)) continue;
1254 }
12381255 }
1256 try w.print("{s}=", .{key});
1257 try shellEscape(w, value, false);
1258 try w.writeByte(' ');
12391259 }
1240 writer.print("{s}=", .{key}) catch return error.OutOfMemory;
1241 shell.escape(writer, value, false) catch return error.OutOfMemory;
1242 writer.writeByte(' ') catch return error.OutOfMemory;
1260 }
1261 try shellEscape(w, sc.argv[0], true);
1262 for (sc.argv[1..]) |arg| {
1263 try w.writeByte(' ');
1264 try shellEscape(w, arg, false);
12431265 }
12441266 }
1245 shell.escape(writer, argv[0], true) catch return error.OutOfMemory;
1246 for (argv[1..]) |arg| {
1247 writer.writeByte(' ') catch return error.OutOfMemory;
1248 shell.escape(writer, arg, false) catch return error.OutOfMemory;
1249 }
1250 return aw.toOwnedSlice();
1251}
1267};
12521268
12531269/// Like `std.process.currentPathAlloc`, but also resolves the path with `Dir.path.resolve`. This
12541270/// means the path has no repeated separators, no "." or ".." components, and no trailing separator.
......@@ -1391,7 +1407,7 @@ pub const Directories = struct {
13911407 },
13921408 };
13931409 }
1394 fn openUnresolved(
1410 pub fn openUnresolved(
13951411 arena: Allocator,
13961412 io: Io,
13971413 cwd: []const u8,
......@@ -1609,6 +1625,214 @@ pub fn isUpDir(p: []const u8) bool {
16091625 return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == Dir.path.sep);
16101626}
16111627
1628pub const BuildExeSubprocessOptions = struct {
1629 argv: []const []const u8,
1630 cache_root: Cache.Directory,
1631 root_name: []const u8,
1632
1633 environ_map: ?*std.process.Environ.Map = null,
1634 cache_manifest: ?*Cache.Manifest = null,
1635 arch_os_abi: ?[]const u8 = null,
1636 cpu_features: ?[]const u8 = null,
1637};
1638
1639pub const BuildExeSubprocessError = error{
1640 /// Error message has been logged.
1641 AlreadyReported,
1642 /// Error message has been logged, and source files added to the `Cache.Manifest`.
1643 FailedButCacheIntact,
1644} || Io.Cancelable || Allocator.Error;
1645
1646/// Assumes `argv` has `--listen=-` in it and the child process is `zig build-exe`.
1647///
1648/// Result path is allocated via gpa.
1649pub fn buildExeSubprocess(gpa: Allocator, io: Io, options: BuildExeSubprocessOptions) BuildExeSubprocessError!Cache.Path {
1650 const cmd: SubprocessCommand = .{ .argv = options.argv };
1651
1652 var child = std.process.spawn(io, .{
1653 .argv = options.argv,
1654 .environ_map = options.environ_map,
1655 .stdin = .pipe,
1656 .stdout = .pipe,
1657 .stderr = .pipe,
1658 }) catch |err| {
1659 log.err("spawning command {t}: {f}", .{ err, cmd });
1660 return error.AlreadyReported;
1661 };
1662 defer child.kill(io);
1663
1664 var stderr_task = io.concurrent(readStreamAlloc, .{ gpa, io, child.stderr.?, .unlimited }) catch
1665 @panic("TODO use multireader instead");
1666 defer if (stderr_task.cancel(io)) |slice| gpa.free(slice) else |_| {};
1667
1668 var stdout_buffer: [512]u8 = undefined;
1669 var stdout_reader: Io.File.Reader = .initStreaming(child.stdout.?, io, &stdout_buffer);
1670 const stdout = &stdout_reader.interface;
1671
1672 {
1673 var w = child.stdin.?.writer(io, &.{});
1674 w.interface.writeStruct(Client.Message.Header{ .tag = .update, .bytes_len = 0 }, .little) catch |err| switch (err) {
1675 error.WriteFailed => {
1676 log.err("{t} writing to command: {f}", .{ w.err.?, cmd });
1677 return error.AlreadyReported;
1678 },
1679 };
1680 w.interface.writeStruct(Client.Message.Header{ .tag = .exit, .bytes_len = 0 }, .little) catch |err| switch (err) {
1681 error.WriteFailed => {
1682 log.err("{t} writing to command: {f}", .{ w.err.?, cmd });
1683 return error.AlreadyReported;
1684 },
1685 };
1686 }
1687
1688 const Header = Server.Message.Header;
1689
1690 var result: ?Cache.Path = null;
1691 defer if (result) |r| gpa.free(r.sub_path);
1692
1693 var result_error_bundle: ErrorBundle = .empty;
1694 defer result_error_bundle.deinit(gpa);
1695
1696 var body_buffer: std.ArrayList(u8) = .empty;
1697 defer body_buffer.deinit(gpa);
1698
1699 var received_fs_inputs = false;
1700
1701 while (true) {
1702 const header = stdout.takeStruct(Header, .little) catch |err| switch (err) {
1703 error.ReadFailed => {
1704 log.err("{t} reading from command: {f}", .{ stdout_reader.err.?, cmd });
1705 return error.AlreadyReported;
1706 },
1707 error.EndOfStream => break,
1708 };
1709 body_buffer.clearRetainingCapacity();
1710 stdout.appendExact(gpa, &body_buffer, header.bytes_len) catch |err| switch (err) {
1711 error.ReadFailed => {
1712 log.err("{t} reading from command: {f}", .{ stdout_reader.err.?, cmd });
1713 return error.AlreadyReported;
1714 },
1715 error.OutOfMemory => |e| return e,
1716 error.EndOfStream => {
1717 log.err("unexpected end of stream from command: {f}", .{cmd});
1718 return error.AlreadyReported;
1719 },
1720 };
1721 const body = body_buffer.items;
1722
1723 switch (header.tag) {
1724 .zig_version => {
1725 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
1726 log.err("zig protocol version mismatch from command: {f}", .{cmd});
1727 return error.AlreadyReported;
1728 }
1729 },
1730 .error_bundle => {
1731 result_error_bundle.deinit(gpa);
1732 result_error_bundle = Server.allocErrorBundle(gpa, body) catch |err| switch (err) {
1733 error.EndOfStream => break,
1734 else => |e| return e,
1735 };
1736 },
1737 .emit_digest => {
1738 const EmitDigest = Server.Message.EmitDigest;
1739 const ebp_hdr: *align(1) const EmitDigest = @ptrCast(body);
1740 if (!ebp_hdr.flags.cache_hit) {
1741 log.info("source changes detected; rebuilt {s}", .{options.root_name});
1742 }
1743 const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];
1744 if (result) |r| gpa.free(r.sub_path);
1745 result = .{
1746 .root_dir = options.cache_root,
1747 .sub_path = try Dir.path.join(gpa, &.{ "o", &Cache.binToHex(digest.*) }),
1748 };
1749 },
1750 .file_system_inputs => {
1751 received_fs_inputs = true;
1752 @panic("TODO");
1753 //var it = mem.splitScalar(u8, file_system_inputs.items, 0);
1754 //while (it.next()) |input| {
1755 // _ = try config_man.addPrefixedPathPost(.{
1756 // .prefix = input[0],
1757 // .sub_path = input[1..],
1758 // });
1759 //}
1760 },
1761 else => {}, // ignore other messages
1762 }
1763 }
1764
1765 const stderr_contents = stderr_task.await(io) catch |err| switch (err) {
1766 error.Canceled, error.OutOfMemory => |e| return e,
1767 else => |e| c: {
1768 log.warn("{t} reading stderr from command: {f}", .{ e, cmd });
1769 break :c "";
1770 },
1771 };
1772 if (stderr_contents.len > 0)
1773 log.warn("unexpected stderr from {s} command:\n{s}", .{ options.argv[0], stderr_contents });
1774
1775 // Send EOF to stdin.
1776 child.stdin.?.close(io);
1777 child.stdin = null;
1778
1779 const term = child.wait(io) catch |err| switch (err) {
1780 error.Canceled => |e| return e,
1781 else => |e| {
1782 log.err("{t} waiting for command: {f}", .{ e, cmd });
1783 return error.AlreadyReported;
1784 },
1785 };
1786
1787 if (!term.success()) {
1788 log.err("command {f}: {f}", .{ term, cmd });
1789 if (received_fs_inputs) return error.FailedButCacheIntact;
1790 return error.AlreadyReported;
1791 }
1792
1793 if (result_error_bundle.errorMessageCount() > 0) {
1794 result_error_bundle.renderToStderr(io, .{}, .auto) catch |err| switch (err) {
1795 error.Canceled => |e| return e,
1796 else => |e| {
1797 log.err("failed rendering error bundle: {t}", .{e});
1798 return error.AlreadyReported;
1799 },
1800 };
1801 log.err("{s} command reported {d} compilation errors: {f}", .{
1802 options.argv[0], result_error_bundle.errorMessageCount(), cmd,
1803 });
1804 if (received_fs_inputs) return error.FailedButCacheIntact;
1805 return error.AlreadyReported;
1806 }
1807
1808 const base_path = result orelse {
1809 log.err("command failed to report result: {f}", .{cmd});
1810 return error.AlreadyReported;
1811 };
1812 const parsed_target = system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{
1813 .arch_os_abi = options.arch_os_abi orelse "native",
1814 .cpu_features = options.cpu_features,
1815 }) catch unreachable) catch unreachable;
1816 const bin_name = try binNameAlloc(gpa, .{
1817 .root_name = options.root_name,
1818 .cpu_arch = parsed_target.cpu.arch,
1819 .os_tag = parsed_target.os.tag,
1820 .ofmt = parsed_target.ofmt,
1821 .abi = parsed_target.abi,
1822 .output_mode = .Exe,
1823 });
1824 defer gpa.free(bin_name);
1825 return base_path.join(gpa, bin_name);
1826}
1827
1828fn readStreamAlloc(gpa: Allocator, io: Io, file: Io.File, limit: Io.Limit) ![]u8 {
1829 var file_reader: Io.File.Reader = .initStreaming(file, io, &.{});
1830 return file_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) {
1831 error.ReadFailed => return file_reader.err.?,
1832 else => |e| return e,
1833 };
1834}
1835
16121836test {
16131837 _ = Ast;
16141838 _ = AstRlAnnotate;
lib/std/zig/Server.zig+1-1
......@@ -264,7 +264,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
264264 try s.out.flush();
265265}
266266
267pub fn allocErrorBundle(gpa: std.mem.Allocator, body: []const u8) error{ OutOfMemory, EndOfStream }!std.zig.ErrorBundle {
267pub fn allocErrorBundle(gpa: Allocator, body: []const u8) error{ OutOfMemory, EndOfStream }!std.zig.ErrorBundle {
268268 var r: Reader = .fixed(body);
269269 const hdr = r.takeStruct(OutMessage.ErrorBundle, .little) catch |err| switch (err) {
270270 error.EndOfStream => |e| return e,