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 @@...@@ -1,5 +1,6 @@
1const Maker = @This();1const Maker = @This();
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const native_os = builtin.os.tag;
34
4const std = @import("std");5const std = @import("std");
5const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
...@@ -106,7 +107,7 @@ const MultilineErrors = enum { indent, newline, none };...@@ -106,7 +107,7 @@ const MultilineErrors = enum { indent, newline, none };
106const Summary = enum { all, new, failures, line, none };107const Summary = enum { all, new, failures, line, none };
107108
108/// Used to build the -M flags to pass to build-exe.109/// Used to build the -M flags to pass to build-exe.
109const CliModule = struct {110pub const CliModule = struct {
110 name: []const u8,111 name: []const u8,
111 root_path: []const u8,112 root_path: []const u8,
112 deps: Deps = .empty,113 deps: Deps = .empty,
...@@ -171,17 +172,17 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -171,17 +172,17 @@ pub fn main(init: process.Init.Minimal) !void {
171 };172 };
172173
173 const cmd = stringToEnum(enum { init, fetch, build }, cmd_name) orelse174 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});
175 switch (cmd) {176 switch (cmd) {
176 .init => return cmdInit( gpa, &graph, args[arg_i..]),177 .init => return cmdInit(gpa, &graph, args[arg_i..]),
177 .fetch => return cmdFetch( gpa, &graph, args[arg_i..]),178 .fetch => return cmdFetch(gpa, &graph, args[arg_i..]),
178 .build => {},179 .build => {},
179 }180 }
180181
181 var step_names: std.ArrayList([]const u8) = .empty;182 var step_names: std.ArrayList([]const u8) = .empty;
182 var help_menu = false;183 var help_menu = false;
183 var steps_menu = false;184 var steps_menu = false;
184 var print_configuration: enum {none, zon, path} = .none;185 var print_configuration: enum { none, zon, path } = .none;
185 var override_install_prefix: ?[]const u8 = null;186 var override_install_prefix: ?[]const u8 = null;
186 var override_lib_dir: ?[]const u8 = null;187 var override_lib_dir: ?[]const u8 = null;
187 var override_bin_dir: ?[]const u8 = null;188 var override_bin_dir: ?[]const u8 = null;
...@@ -409,6 +410,8 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -409,6 +410,8 @@ pub fn main(init: process.Init.Minimal) !void {
409 webui_listen = Io.net.IpAddress.parseLiteral(addr_str) catch |err| {410 webui_listen = Io.net.IpAddress.parseLiteral(addr_str) catch |err| {
410 fatal("invalid web UI address {q}: {t}", .{ addr_str, err });411 fatal("invalid web UI address {q}: {t}", .{ addr_str, err });
411 };412 };
413 } else if (mem.eql(u8, arg, "--debug-target")) {
414 debug_target = nextArgOrFatal(args, &arg_i);
412 } else if (mem.eql(u8, arg, "--debug-log")) {415 } else if (mem.eql(u8, arg, "--debug-log")) {
413 try graph.debug_log_scopes.append(arena, nextArgOrFatal(args, &arg_i));416 try graph.debug_log_scopes.append(arena, nextArgOrFatal(args, &arg_i));
414 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {417 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
...@@ -551,9 +554,9 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -551,9 +554,9 @@ pub fn main(init: process.Init.Minimal) !void {
551 io,554 io,
552 cwd_path,555 cwd_path,
553 unresolved_path,556 unresolved_path,
554 .@"local_cache",557 .@"local cache",
555 ) else .{558 ) 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 }),
557 .handle = try build_root.directory.handle.createDirPathOpen(io, default_local_zig_cache_basename, .{}),560 .handle = try build_root.directory.handle.createDirPathOpen(io, default_local_zig_cache_basename, .{}),
558 };561 };
559 graph.cache = .{562 graph.cache = .{
...@@ -583,10 +586,13 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -583,10 +586,13 @@ pub fn main(init: process.Init.Minimal) !void {
583 });586 });
584 defer main_progress_node.end();587 defer main_progress_node.end();
585588
586 {589 const scanned_config: ScannedConfig = sc: {
587 // Cache lookup for configure options. If we get a match, we can skip590 // Cache lookup for configure options. If we get a match, we can skip
588 // execution of the configure script. If not, we get the file path to pass591 // execution of the configure script. If not, we get the file path to pass
589 // to the configure process.592 // 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.
590 var config_man = graph.cache.obtain();596 var config_man = graph.cache.obtain();
591 defer config_man.deinit();597 defer config_man.deinit();
592598
...@@ -597,28 +603,6 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -597,28 +603,6 @@ pub fn main(init: process.Init.Minimal) !void {
597 // a `zig build --cache-poison=ignored`.603 // a `zig build --cache-poison=ignored`.
598 config_man.hash.add(cache_poison == .ignored);604 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
622 const pkg_root: Path = if (override_pkg_dir) |p|606 const pkg_root: Path = if (override_pkg_dir) |p|
623 .initCwd(p)607 .initCwd(p)
624 else if (system_pkg_dir_path) |p|608 else if (system_pkg_dir_path) |p|
...@@ -659,10 +643,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -659,10 +643,7 @@ pub fn main(init: process.Init.Minimal) !void {
659 }643 }
660 defer Fork.deinitList(forks.items);644 defer Fork.deinitList(forks.items);
661645
662 var file_system_inputs: std.ArrayList(u8) = .empty;646 var build_configurer_argv: std.ArrayList([]const u8) = .empty;
663 defer file_system_inputs.deinit(gpa);
664
665 var build_configurer_argv: std.ArrayList(u8) = .empty;
666 defer build_configurer_argv.deinit(gpa);647 defer build_configurer_argv.deinit(gpa);
667648
668 var dependencies_source: std.ArrayList(u8) = .empty;649 var dependencies_source: std.ArrayList(u8) = .empty;
...@@ -678,16 +659,28 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -678,16 +659,28 @@ pub fn main(init: process.Init.Minimal) !void {
678 .sub_path = build_root.build_zig_basename,659 .sub_path = build_root.build_zig_basename,
679 };660 };
680661
662 const configurer_exe_name = "configurer";
663
681 try build_configurer_argv.appendSlice(gpa, &.{664 try build_configurer_argv.appendSlice(gpa, &.{
682 graph.zig_exe, "build-exe", //665 graph.zig_exe, "build-exe", //
683 "--cache-dir", graph.local_cache_root.path orelse ".", //666 "--cache-dir", graph.local_cache_root.path orelse ".", //
684 "--global-cache-dir", graph.global_cache_root.path orelse ".", //667 "--global-cache-dir", graph.global_cache_root.path orelse ".", //
685 "--zig-lib-dir", graph.zig_lib_directory.path orelse ".", //668 "--zig-lib-dir", graph.zig_lib_directory.path orelse ".", //
686 "--name", "configurer", //669 "--name", configurer_exe_name, //
687 "-fsingle-threaded", //670 "-fsingle-threaded", //
688 });671 });
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
689 if (graph.libc_file) |libc_file| {682 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 });
691 }684 }
692 if (graph.reference_trace) |n| {685 if (graph.reference_trace) |n| {
693 try build_configurer_argv.append(gpa, try allocPrint(arena, "-freference-trace={d}", .{n}));686 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 {...@@ -737,9 +730,6 @@ pub fn main(init: process.Init.Minimal) !void {
737 // We want to release all the locks before executing the child process, so we make a nice730 // We want to release all the locks before executing the child process, so we make a nice
738 // big block here to ensure the cleanup gets run when we extract out our argv.731 // big block here to ensure the cleanup gets run when we extract out our argv.
739 {732 {
740
741
742
743 {733 {
744 const fetch_prog_node = main_progress_node.start("Fetch Packages", 0);734 const fetch_prog_node = main_progress_node.start("Fetch Packages", 0);
745 defer fetch_prog_node.end();735 defer fetch_prog_node.end();
...@@ -820,12 +810,12 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -820,12 +810,12 @@ pub fn main(init: process.Init.Minimal) !void {
820 var any_unused = false;810 var any_unused = false;
821 for (fork_set.keys()) |*fork| {811 for (fork_set.keys()) |*fork| {
822 if (fork.uses == 0) {812 if (fork.uses == 0) {
823 std.log.err("fork {f} matched no {s} packages", .{813 log.err("fork {f} matched no {s} packages", .{
824 fork.path, fork.manifest.name,814 fork.path, fork.manifest.name,
825 });815 });
826 any_unused = true;816 any_unused = true;
827 } else {817 } else {
828 std.log.info("fork {f} matched {d} {s} packages", .{818 log.info("fork {f} matched {d} {s} packages", .{
829 fork.path, fork.uses, fork.manifest.name,819 fork.path, fork.uses, fork.manifest.name,
830 });820 });
831 }821 }
...@@ -842,16 +832,16 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -842,16 +832,16 @@ pub fn main(init: process.Init.Minimal) !void {
842 process.exit(1);832 process.exit(1);
843 }833 }
844834
845 if (fetch_only) return cleanExit(io);835 if (fetch_only) return process.cleanExit(io);
846836
847 // Create the dependencies.zig file for configurer to837 // Create the dependencies.zig file for configurer to
848 // obtain via `@import("@dependencies")`.838 // obtain via `@import("@dependencies")`.
849 {839 {
850 {840 {
851 dependencies_source.clearRetainingCapacity();841 dependencies_source.clearRetainingCapacity();
852 var source_writer: Io.Writer.Allocating = .fromArrayList(&dependencies_source);842 var source_writer: Io.Writer.Allocating = .fromArrayList(gpa, &dependencies_source);
853 defer dependencies_source = source_writer.toArrayList();843 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) {
855 error.WriteFailed => return error.OutOfMemory,845 error.WriteFailed => return error.OutOfMemory,
856 };846 };
857 }847 }
...@@ -862,17 +852,18 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -862,17 +852,18 @@ pub fn main(init: process.Init.Minimal) !void {
862 const hex_digest = hh.final();852 const hex_digest = hh.final();
863 const dependencies_zig_path: Path = .{853 const dependencies_zig_path: Path = .{
864 .root_dir = graph.local_cache_root,854 .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}),
866 };856 };
867 var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic(857 var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic(
868 io,858 io,
869 dependencies_zig_path.sub_path, .{ .make_path = true, .replace = true },859 dependencies_zig_path.sub_path,
860 .{ .make_path = true, .replace = true },
870 );861 );
871 defer atomic_file.deinit(io);862 defer atomic_file.deinit(io);
872 atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err|863 atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err|
873 fatal("writing dependencies.zig contents: {t}", .{err});864 fatal("writing dependencies.zig contents: {t}", .{err});
874 atomic_file.replace(io) catch |err|865 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
877 deps_mod.root_path = try dependencies_zig_path.toString(arena);868 deps_mod.root_path = try dependencies_zig_path.toString(arena);
878 }869 }
...@@ -914,7 +905,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -914,7 +905,7 @@ pub fn main(init: process.Init.Minimal) !void {
914 global_cache_directory,905 global_cache_directory,
915 dep,906 dep,
916 ) orelse continue;907 ) 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;
918 const name_cloned = try arena.dupe(u8, name);909 const name_cloned = try arena.dupe(u8, name);
919 mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod);910 mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod);
920 }911 }
...@@ -948,23 +939,30 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -948,23 +939,30 @@ pub fn main(init: process.Init.Minimal) !void {
948939
949 try build_configurer_argv.append(gpa, "--listen=-");940 try build_configurer_argv.append(gpa, "--listen=-");
950941
951 file_system_inputs.clearRetainingCapacity();942 const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{
952 execute_child(build_configurer_argv, &file_system_inputs);943 .argv = build_configurer_argv.items,
953944 .cache_root = graph.local_cache_root,
954 const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?);945 .root_name = configurer_exe_name,
955 const exe_path: Path = .{946 .environ_map = &graph.environ_map,
956 .root_dir = dirs.local_cache,947 .cache_manifest = &config_man,
957 .sub_path = try allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }),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,
958 };955 };
959 _ = try config_man.addFilePath(exe_path, null);956 defer gpa.free(configure_exe_path.sub_path);
960 configure_argv.items[0] = try exe_path.toString(arena);957
958 configure_argv.items[0] = try configure_exe_path.toString(arena);
961959
962 switch (cache_poison) {960 switch (cache_poison) {
963 .pure, .disallowed, .ignored => if (try config_man.hit()) {961 .pure, .disallowed, .ignored => if (try config_man.hit()) {
964 const digest = config_man.final();962 const digest = config_man.final();
965 break :cp .{963 break :cp .{
966 .{964 .{
967 .root_dir = dirs.local_cache,965 .root_dir = graph.local_cache_root,
968 .sub_path = try allocPrint(arena, "c/{s}", .{&digest}),966 .sub_path = try allocPrint(arena, "c/{s}", .{&digest}),
969 },967 },
970 false,968 false,
...@@ -975,14 +973,15 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -975,14 +973,15 @@ pub fn main(init: process.Init.Minimal) !void {
975 }973 }
976974
977 if (!process.can_spawn) {975 if (!process.can_spawn) {
978 const cmd = try std.mem.join(arena, " ", configure_argv.items);976 fatal("cannot spawn command on {t}: {f}", .{ native_os, @as(std.zig.SubprocessCommand, .{
979 fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd });977 .argv = configure_argv.items,
978 }) });
980 }979 }
981980
982 const rand_int = randInt(io, u64);981 const rand_int = randInt(io, u64);
983 const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);982 const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
984 const config_tmp_path: Path = .{983 const config_tmp_path: Path = .{
985 .root_dir = dirs.local_cache,984 .root_dir = graph.local_cache_root,
986 .sub_path = tmp_dir_sub_path,985 .sub_path = tmp_dir_sub_path,
987 };986 };
988 const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile(987 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 {...@@ -995,7 +994,7 @@ pub fn main(init: process.Init.Minimal) !void {
995 const term = term: {994 const term = term: {
996 const child_node = main_progress_node.start("Run Configure Script", 0);995 const child_node = main_progress_node.start("Run Configure Script", 0);
997 defer child_node.end();996 defer child_node.end();
998 var child = std.process.spawn(io, .{997 var child = process.spawn(io, .{
999 .argv = configure_argv.items,998 .argv = configure_argv.items,
1000 .stdout = .{ .file = config_tmp_file },999 .stdout = .{ .file = config_tmp_file },
1001 .progress_node = child_node,1000 .progress_node = child_node,
...@@ -1006,8 +1005,9 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -1006,8 +1005,9 @@ pub fn main(init: process.Init.Minimal) !void {
1006 };1005 };
1007 if (!term.success()) {1006 if (!term.success()) {
1008 // Failure to produce the configuration file.1007 // Failure to produce the configuration file.
1009 const cmd = try std.mem.join(arena, " ", configure_argv.items);1008 fatal("configure command {f}: {f}", .{ term, @as(std.zig.SubprocessCommand, .{
1010 fatal("the following configure command {f}:\n{s}", .{ term, cmd });1009 .argv = configure_argv.items,
1010 }) });
1011 }1011 }
1012 // Even though the file is designed to be sent directly to make1012 // Even though the file is designed to be sent directly to make
1013 // runner, we must load it now because:1013 // runner, we must load it now because:
...@@ -1015,17 +1015,16 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -1015,17 +1015,16 @@ pub fn main(init: process.Init.Minimal) !void {
1015 // add them to `config_man` before obtaining the final digest.1015 // add them to `config_man` before obtaining the final digest.
1016 // * If it contains a set of lazy packages that need to be1016 // * If it contains a set of lazy packages that need to be
1017 // fetched, we need to fetch those now and re-run configure.1017 // 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|
1019 fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err });1019 fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err });
10201020
1021 if (configuration.unlazy_deps.len != 0) {1021 if (configuration.unlazy_deps.len != 0) {
1022 if (!dev.env.supports(.fetch_command)) process.exit(1);
1023 var any_errors = false;1022 var any_errors = false;
1024 for (configuration.unlazy_deps) |hash_string| {1023 for (configuration.unlazy_deps) |hash_string| {
1025 const hash = hash_string.slice(&configuration);1024 const hash = hash_string.slice(&configuration);
1026 assert(hash.len != 0);1025 assert(hash.len != 0);
1027 if (hash.len > Package.Hash.max_len) {1026 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 });
1029 any_errors = true;1028 any_errors = true;
1030 continue;1029 continue;
1031 }1030 }
...@@ -1037,33 +1036,17 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -1037,33 +1036,17 @@ pub fn main(init: process.Init.Minimal) !void {
1037 // cannot be fetched by Zig.1036 // cannot be fetched by Zig.
1038 const s = Dir.path.sep_str;1037 const s = Dir.path.sep_str;
1039 for (unlazy_set.keys()) |*hash| {1038 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() });
1041 }1040 }
1042 std.log.info("remote package fetching disabled due to --system mode", .{});1041 log.info("remote package fetching disabled due to --system mode", .{});
1043 std.log.info("dependencies might be avoidable depending on build configuration", .{});1042 log.info("dependencies might be avoidable depending on build configuration", .{});
1044 process.exit(1);1043 process.exit(1);
1045 }1044 }
1046 continue :cp;1045 continue :cp;
1047 }1046 }
10481047
1049 for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| {1048 for (configuration.path_deps) |path_dep| {
1050 const conf_path: std.Build.Configuration.Path = .{ .base = base, .sub = sub };1049 try config_man.addPathPost(path_dep.toCachePath(&configuration, arena));
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 }
1067 }1050 }
10681051
1069 // If it is poisoned, there is no point in moving it to cached1052 // 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 {...@@ -1073,7 +1056,7 @@ pub fn main(init: process.Init.Minimal) !void {
1073 } else {1056 } else {
1074 const digest = config_man.final();1057 const digest = config_man.final();
1075 const final_path: Path = .{1058 const final_path: Path = .{
1076 .root_dir = dirs.local_cache,1059 .root_dir = graph.local_cache_root,
1077 .sub_path = try allocPrint(arena, "c/{s}", .{&digest}),1060 .sub_path = try allocPrint(arena, "c/{s}", .{&digest}),
1078 };1061 };
1079 Io.Dir.rename(1062 Io.Dir.rename(
...@@ -1107,33 +1090,27 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -1107,33 +1090,27 @@ pub fn main(init: process.Init.Minimal) !void {
1107 }1090 }
1108 };1091 };
11091092
1110 {1093 // Hang on to the configuration file lock until we finish loading the configuration file.
1111 // Release all file system locks just before running the maker process.1094 var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null;
1112 var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null;1095 defer if (configuration_lock) |*l| l.release(io);
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});
11241096
1125 make_argv.items[0] = try make_runner.exe_path.toString(arena);1097 switch (print_configuration) {
1126 make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena);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 => {},
1127 }1106 }
1128 }
11291107
1130 const scanned_config: ScannedConfig = sc: {
1131 const configuration = c: {1108 const configuration = c: {
1132 var file = cwd.openFile(io, configure_path, .{}) catch |err|1109 var file = configuration_path.root_dir.handle.openFile(io, configuration_path.sub_path, .{}) catch |err|
1133 fatal("failed to open configuration file {s}: {t}", .{ configure_path, err });1110 fatal("failed to open configuration file {f}: {t}", .{ configuration_path, err });
1134 defer file.close(io);1111 defer file.close(io);
1135 break :c Configuration.loadFile(arena, io, file) catch |err|1112 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 });
1137 };1114 };
1138 // Technically if the configuration is marked as poisoned, we could1115 // Technically if the configuration is marked as poisoned, we could
1139 // already delete the file now, but we leave it around in case the1116 // 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 {...@@ -1159,37 +1136,32 @@ pub fn main(init: process.Init.Minimal) !void {
1159 break :sc .{1136 break :sc .{
1160 .configuration = configuration,1137 .configuration = configuration,
1161 .top_level_steps = top_level_steps,1138 .top_level_steps = top_level_steps,
1162 .path = configure_path,1139 .path = configuration_path,
1163 };1140 };
1164 };1141 };
11651142
1166 if (help_menu) {1143 if (help_menu) {
1167 const w = initStdoutWriter(io);1144 scanned_config.printUsage(&graph, initStdoutWriter(io)) catch |err| switch (err) {
1168 scanned_config.printUsage(&graph, w) catch |err| switch (err) {
1169 error.WriteFailed => return stdout_writer_allocation.err.?,1145 error.WriteFailed => return stdout_writer_allocation.err.?,
1170 else => |e| return e,1146 else => |e| return e,
1171 };1147 };
1172 w.flush() catch return stdout_writer_allocation.err.?;1148 try stdout_writer_allocation.flush();
1173 return cleanExit(io, &scanned_config);1149 return cleanExit(io, &scanned_config);
1174 } else if (steps_menu) {1150 } else if (steps_menu) {
1175 const w = initStdoutWriter(io);1151 scanned_config.printSteps(&graph, initStdoutWriter(io)) catch |err| switch (err) {
1176 scanned_config.printSteps(&graph, w) catch |err| switch (err) {
1177 error.WriteFailed => return stdout_writer_allocation.err.?,1152 error.WriteFailed => return stdout_writer_allocation.err.?,
1178 else => |e| return e,1153 else => |e| return e,
1179 };1154 };
1180 w.flush() catch return stdout_writer_allocation.err.?;1155 try stdout_writer_allocation.flush();
1181 return cleanExit(io, &scanned_config);1156 return cleanExit(io, &scanned_config);
1182 } else switch (print_configuration) {1157 } else switch (print_configuration) {
1183 .none => {},1158 .none => {},
1184 .zon => {1159 .zon => {
1185 const w = initStdoutWriter(io);1160 scanned_config.print(initStdoutWriter(io)) catch return stdout_writer_allocation.err.?;
1186 scanned_config.print(w) catch return stdout_writer_allocation.err.?;1161 try stdout_writer_allocation.flush();
1187 w.flush() catch return stdout_writer_allocation.err.?;
1188 return cleanExit(io, &scanned_config);1162 return cleanExit(io, &scanned_config);
1189 },1163 },
1190 .path => {1164 .path => unreachable,
1191 @panic("TODO");
1192 },
1193 }1165 }
11941166
1195 if (webui_listen != null) {1167 if (webui_listen != null) {
...@@ -1204,7 +1176,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -1204,7 +1176,7 @@ pub fn main(init: process.Init.Minimal) !void {
1204 .root_dir = .cwd(),1176 .root_dir = .cwd(),
1205 .sub_path = cwd_relative,1177 .sub_path = cwd_relative,
1206 } else .{1178 } else .{
1207 .root_dir = build_root_directory,1179 .root_dir = graph.build_root_directory,
1208 .sub_path = "zig-out",1180 .sub_path = "zig-out",
1209 };1181 };
12101182
...@@ -1274,7 +1246,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -1274,7 +1246,7 @@ pub fn main(init: process.Init.Minimal) !void {
12741246
1275 var w: Watch = w: {1247 var w: Watch = w: {
1276 if (!watch) break :w undefined;1248 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});
1278 break :w try .init(&maker);1250 break :w try .init(&maker);
1279 };1251 };
12801252
...@@ -1366,11 +1338,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -1366,11 +1338,7 @@ pub fn main(init: process.Init.Minimal) !void {
1366 }1338 }
1367}1339}
13681340
1369fn cmdFetch(1341fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
1370 gpa: Allocator,
1371 graph: *Graph,
1372 args: []const []const u8
1373) !void {
1374 const environ_map = &graph.environ_map;1342 const environ_map = &graph.environ_map;
1375 const io = graph.io;1343 const io = graph.io;
1376 const arena = graph.arena;1344 const arena = graph.arena;
...@@ -1392,7 +1360,7 @@ fn cmdFetch(...@@ -1392,7 +1360,7 @@ fn cmdFetch(
1392 if (mem.startsWith(u8, arg, "-")) {1360 if (mem.startsWith(u8, arg, "-")) {
1393 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {1361 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
1394 try Io.File.stdout().writeStreamingAll(io, usage_fetch);1362 try Io.File.stdout().writeStreamingAll(io, usage_fetch);
1395 return cleanExit(io);1363 return process.cleanExit(io);
1396 } else if (mem.eql(u8, arg, "--global-cache-dir")) {1364 } else if (mem.eql(u8, arg, "--global-cache-dir")) {
1397 override_global_cache_dir = nextArgOrFatal(args, &arg_i);1365 override_global_cache_dir = nextArgOrFatal(args, &arg_i);
1398 } else if (mem.eql(u8, arg, "--cache-dir")) {1366 } else if (mem.eql(u8, arg, "--cache-dir")) {
...@@ -1500,7 +1468,7 @@ fn cmdFetch(...@@ -1500,7 +1468,7 @@ fn cmdFetch(
1500 .oom_flag = false,1468 .oom_flag = false,
1501 .latest_commit = null,1469 .latest_commit = null,
15021470
1503 .module = null,1471 .cli_module = null,
1504 };1472 };
1505 defer fetch.deinit();1473 defer fetch.deinit();
15061474
...@@ -1526,10 +1494,10 @@ fn cmdFetch(...@@ -1526,10 +1494,10 @@ fn cmdFetch(
1526 const name = switch (save) {1494 const name = switch (save) {
1527 .no => {1495 .no => {
1528 var data: [2][]const u8 = .{ package_hash_slice, "\n" };1496 var data: [2][]const u8 = .{ package_hash_slice, "\n" };
1529 const w = initStdoutWriter();1497 const w = initStdoutWriter(io);
1530 try w.writeVecAll(&data);1498 w.writeVecAll(&data) catch return stdout_writer_allocation.err.?;
1531 try w.flush();1499 try stdout_writer_allocation.flush();
1532 return cleanExit(io);1500 return process.cleanExit(io);
1533 },1501 },
1534 .yes, .exact => |name| name: {1502 .yes, .exact => |name| name: {
1535 if (name) |n| break :name n;1503 if (name) |n| break :name n;
...@@ -1567,14 +1535,14 @@ fn cmdFetch(...@@ -1567,14 +1535,14 @@ fn cmdFetch(
1567 // the refspec may already be fully resolved1535 // the refspec may already be fully resolved
1568 if (std.mem.eql(u8, target_ref, latest_commit_hex)) break :resolved;1536 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
1572 // include the original refspec in a query parameter, could be used to check for updates1540 // include the original refspec in a query parameter, could be used to check for updates
1573 uri.query = .{ .percent_encoded = try allocPrint(arena, "ref={f}", .{1541 uri.query = .{ .percent_encoded = try allocPrint(arena, "ref={f}", .{
1574 std.fmt.alt(fragment, .formatEscaped),1542 std.fmt.alt(fragment, .formatEscaped),
1575 }) };1543 }) };
1576 } else {1544 } else {
1577 std.log.info("resolved to commit {s}", .{latest_commit_hex});1545 log.info("resolved to commit {s}", .{latest_commit_hex});
1578 }1546 }
15791547
1580 // replace the refspec with the resolved commit SHA1548 // replace the refspec with the resolved commit SHA
...@@ -1613,7 +1581,7 @@ fn cmdFetch(...@@ -1613,7 +1581,7 @@ fn cmdFetch(
1613 switch (dep.location) {1581 switch (dep.location) {
1614 .url => |u| {1582 .url => |u| {
1615 if (mem.eql(u8, h, package_hash_slice) and mem.eql(u8, u, saved_path_or_url)) {1583 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});
1617 process.exit(0);1585 process.exit(0);
1618 }1586 }
1619 },1587 },
...@@ -1661,7 +1629,7 @@ fn cmdFetch(...@@ -1661,7 +1629,7 @@ fn cmdFetch(
1661 fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err });1629 fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err });
1662 };1630 };
16631631
1664 return cleanExit(io);1632 return process.cleanExit(io);
1665}1633}
16661634
1667const usage_fetch =1635const usage_fetch =
...@@ -1707,13 +1675,10 @@ const usage_init =...@@ -1707,13 +1675,10 @@ const usage_init =
1707 \\1675 \\
1708;1676;
17091677
1710fn cmdInit(1678fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
1711 gpa: Allocator,
1712 graph: *Graph,
1713 args: []const []const u8
1714) !void {
1715 const arena = graph.arena;1679 const arena = graph.arena;
1716 const io = graph.io;1680 const io = graph.io;
1681 const default_build_zig_basename = std.zig.build_zig_basename;
17171682
1718 var template: enum { example, minimal } = .example;1683 var template: enum { example, minimal } = .example;
1719 {1684 {
...@@ -1725,7 +1690,7 @@ fn cmdInit(...@@ -1725,7 +1690,7 @@ fn cmdInit(
1725 template = .minimal;1690 template = .minimal;
1726 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {1691 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
1727 try Io.File.stdout().writeStreamingAll(io, usage_init);1692 try Io.File.stdout().writeStreamingAll(io, usage_init);
1728 return cleanExit(io);1693 return process.cleanExit(io);
1729 } else {1694 } else {
1730 fatal("unrecognized parameter: {q}", .{arg});1695 fatal("unrecognized parameter: {q}", .{arg});
1731 }1696 }
...@@ -1749,7 +1714,7 @@ fn cmdInit(...@@ -1749,7 +1714,7 @@ fn cmdInit(
17491714
1750 const s = Dir.path.sep_str;1715 const s = Dir.path.sep_str;
1751 const template_paths = [_][]const u8{1716 const template_paths = [_][]const u8{
1752 Package.build_zig_basename,1717 default_build_zig_basename,
1753 Package.Manifest.basename,1718 Package.Manifest.basename,
1754 "src" ++ s ++ "main.zig",1719 "src" ++ s ++ "main.zig",
1755 "src" ++ s ++ "root.zig",1720 "src" ++ s ++ "root.zig",
...@@ -1758,20 +1723,20 @@ fn cmdInit(...@@ -1758,20 +1723,20 @@ fn cmdInit(
17581723
1759 for (template_paths) |template_path| {1724 for (template_paths) |template_path| {
1760 if (templates.write(arena, io, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| {1725 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});
1762 ok_count += 1;1727 ok_count += 1;
1763 } else |err| switch (err) {1728 } 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}", .{
1765 template_path,1730 template_path,
1766 }),1731 }),
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 }),
1768 }1733 }
1769 }1734 }
17701735
1771 if (ok_count == template_paths.len) {1736 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", .{});
1773 }1738 }
1774 return cleanExit(io);1739 return process.cleanExit(io);
1775 },1740 },
1776 .minimal => {1741 .minimal => {
1777 writeSimpleTemplateFile(io, Package.Manifest.basename,1742 writeSimpleTemplateFile(io, Package.Manifest.basename,
...@@ -1791,7 +1756,7 @@ fn cmdInit(...@@ -1791,7 +1756,7 @@ fn cmdInit(
1791 else => fatal("failed to create {q}: {t}", .{ Package.Manifest.basename, err }),1756 else => fatal("failed to create {q}: {t}", .{ Package.Manifest.basename, err }),
1792 error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Package.Manifest.basename}),1757 error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Package.Manifest.basename}),
1793 };1758 };
1794 writeSimpleTemplateFile(io, Package.build_zig_basename,1759 writeSimpleTemplateFile(io, default_build_zig_basename,
1795 \\const std = @import("std");1760 \\const std = @import("std");
1796 \\1761 \\
1797 \\pub fn build(b: *std.Build) void {{1762 \\pub fn build(b: *std.Build) void {{
...@@ -1799,24 +1764,22 @@ fn cmdInit(...@@ -1799,24 +1764,22 @@ fn cmdInit(
1799 \\}}1764 \\}}
1800 \\1765 \\
1801 , .{}) catch |err| switch (err) {1766 , .{}) 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 }),
1803 // `build.zig` already existing is okay: the user has just used `zig init` to set up1768 // `build.zig` already existing is okay: the user has just used `zig init` to set up
1804 // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal.1769 // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal.
1805 error.PathAlreadyExists => {1770 error.PathAlreadyExists => {
1806 std.log.info("successfully populated {q}, preserving existing {q}", .{1771 log.info("successfully populated {q}, preserving existing {q}", .{
1807 Package.Manifest.basename, Package.build_zig_basename,1772 Package.Manifest.basename, default_build_zig_basename,
1808 });1773 });
1809 return cleanExit(io);1774 return process.cleanExit(io);
1810 },1775 },
1811 };1776 };
1812 std.log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, Package.build_zig_basename });1777 log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, default_build_zig_basename });
1813 return cleanExit(io);1778 return process.cleanExit(io);
1814 },1779 },
1815 }1780 }
1816}1781}
18171782
1818
1819
1820fn markFailedStepsDirty(maker: *Maker) void {1783fn markFailedStepsDirty(maker: *Maker) void {
1821 const all_steps = maker.step_stack.keys();1784 const all_steps = maker.step_stack.keys();
18221785
...@@ -1918,9 +1881,7 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void {...@@ -1918,9 +1881,7 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void {
1918 }1881 }
1919 if (any_problems) {1882 if (any_problems) {
1920 if (maker.max_rss_is_default) {1883 if (maker.max_rss_is_default) {
1921 std.log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{1884 log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{max_needed});
1922 max_needed,
1923 });
1924 }1885 }
1925 return error.InsufficientMemory;1886 return error.InsufficientMemory;
1926 }1887 }
...@@ -2012,12 +1973,12 @@ fn makeStepNames(...@@ -2012,12 +1973,12 @@ fn makeStepNames(
2012 }1973 }
20131974
2014 if (fuzz) |mode| blk: {1975 if (fuzz) |mode| blk: {
2015 switch (builtin.os.tag) {1976 switch (native_os) {
2016 // Current implementation depends on two things that need to be ported to Windows:1977 // Current implementation depends on two things that need to be ported to Windows:
2017 // * Memory-mapping to share data between the fuzzer and build runner.1978 // * Memory-mapping to share data between the fuzzer and build runner.
2018 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving1979 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving
2019 // many addresses to source locations).1980 // 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}),
2021 else => {},1982 else => {},
2022 }1983 }
2023 if (@bitSizeOf(usize) != 64) {1984 if (@bitSizeOf(usize) != 64) {
...@@ -2781,23 +2742,23 @@ pub fn printErrorMessages(...@@ -2781,23 +2742,23 @@ pub fn printErrorMessages(
2781 try writer.writeByte('\n');2742 try writer.writeByte('\n');
2782}2743}
27832744
2784fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {2745fn nextArg(args: []const []const u8, idx: *usize) ?[]const u8 {
2785 if (idx.* >= args.len) return null;2746 if (idx.* >= args.len) return null;
2786 defer idx.* += 1;2747 defer idx.* += 1;
2787 return args[idx.*];2748 return args[idx.*];
2788}2749}
27892750
2790fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {2751fn nextArgOrFatal(args: []const []const u8, idx: *usize) []const u8 {
2791 return nextArg(args, idx) orelse fatalWithHint("expected argument after {q}", .{args[idx.* - 1]});2752 return nextArg(args, idx) orelse fatalWithHint("expected argument after {q}", .{args[idx.* - 1]});
2792}2753}
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 {
2795 const arg = args[index_ptr.*];2756 const arg = args[index_ptr.*];
2796 if (mem.cutPrefix(u8, arg, prefix)) |rest| return rest;2757 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 });
2798}2759}
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 {
2801 if (idx >= args.len) return null;2762 if (idx >= args.len) return null;
2802 return args[idx..];2763 return args[idx..];
2803}2764}
...@@ -3158,8 +3119,8 @@ fn removePoisonedConfiguration(io: Io, scanned_config: *const ScannedConfig) voi...@@ -3158,8 +3119,8 @@ fn removePoisonedConfiguration(io: Io, scanned_config: *const ScannedConfig) voi
3158 if (scanned_config.configuration.poisoned) {3119 if (scanned_config.configuration.poisoned) {
3159 // This configuration file was good for only 1 invocation of the maker3120 // This configuration file was good for only 1 invocation of the maker
3160 // process. Delete it to save space on disk.3121 // process. Delete it to save space on disk.
3161 Io.Dir.cwd().deleteFile(io, scanned_config.path) catch |err|3122 scanned_config.path.root_dir.handle.deleteFile(io, scanned_config.path.sub_path) catch |err|
3162 log.warn("failed deleting poisoned configuration file {s}: {t}", .{ scanned_config.path, err });3123 log.warn("failed deleting poisoned configuration file {f}: {t}", .{ scanned_config.path, err });
3163 }3124 }
3164}3125}
31653126
...@@ -3228,8 +3189,8 @@ fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !Build...@@ -3228,8 +3189,8 @@ fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !Build
3228 } else |err| switch (err) {3189 } else |err| switch (err) {
3229 error.FileNotFound => {3190 error.FileNotFound => {
3230 dirname = Dir.path.dirname(dirname) orelse {3191 dirname = Dir.path.dirname(dirname) orelse {
3231 std.log.info("initialize {s} template file with \"zig init\"", .{ std.zig.build_zig_basename });3192 log.info("initialize {s} template file with \"zig init\"", .{std.zig.build_zig_basename});
3232 std.log.info("see \"zig --help\" for more options", .{});3193 log.info("see \"zig --help\" for more options", .{});
3233 fatal("no build.zig file found, in the current directory or any parent directories", .{});3194 fatal("no build.zig file found, in the current directory or any parent directories", .{});
3234 };3195 };
3235 continue;3196 continue;
...@@ -3266,7 +3227,7 @@ const Fork = struct {...@@ -3266,7 +3227,7 @@ const Fork = struct {
3266 error.Canceled => |e| return e,3227 error.Canceled => |e| return e,
3267 error.AlreadyReported => fork.failed = true,3228 error.AlreadyReported => fork.failed = true,
3268 else => |e| {3229 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 });
3270 fork.failed = true;3231 fork.failed = true;
3271 },3232 },
3272 };3233 };
...@@ -3299,7 +3260,7 @@ const Fork = struct {...@@ -3299,7 +3260,7 @@ const Fork = struct {
3299 return error.AlreadyReported;3260 return error.AlreadyReported;
3300 },3261 },
3301 else => |e| {3262 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 });
3303 return error.AlreadyReported;3264 return error.AlreadyReported;
3304 },3265 },
3305 };3266 };
...@@ -3527,4 +3488,3 @@ fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates {...@@ -3527,4 +3488,3 @@ fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates {
3527 .buffer = std.array_list.Managed(u8).init(gpa),3488 .buffer = std.array_list.Managed(u8).init(gpa),
3528 };3489 };
3529}3490}
3530
lib/compiler/Maker/Fetch.zig+7-7
...@@ -46,7 +46,7 @@ const ascii = std.ascii;...@@ -46,7 +46,7 @@ const ascii = std.ascii;
46const Allocator = std.mem.Allocator;46const Allocator = std.mem.Allocator;
47const Cache = std.Build.Cache;47const Cache = std.Build.Cache;
48const git = @import("Fetch/git.zig");48const git = @import("Fetch/git.zig");
49const Package = @import("../Package.zig");49const Package = @import("Package.zig");
50const Manifest = Package.Manifest;50const Manifest = Package.Manifest;
51const ErrorBundle = std.zig.ErrorBundle;51const ErrorBundle = std.zig.ErrorBundle;
5252
...@@ -341,10 +341,10 @@ pub const JobQueue = struct {...@@ -341,10 +341,10 @@ pub const JobQueue = struct {
341 .{ std.zig.fmtString(name), std.zig.fmtString(h.toSlice()) },341 .{ std.zig.fmtString(name), std.zig.fmtString(h.toSlice()) },
342 );342 );
343 }343 }
344 try w.appendSlice("};\n");344 try w.writeAll("};\n");
345 }345 }
346346
347 pub fn createEmptyDependenciesSource(w: *Io.Writer) Io.Writer!void {347 pub fn createEmptyDependenciesSource(w: *Io.Writer) Io.Writer.Error!void {
348 try w.writeAll(348 try w.writeAll(
349 \\pub const packages = struct {};349 \\pub const packages = struct {};
350 \\pub const root_deps: []const struct { []const u8, []const u8 } = &.{};350 \\pub const root_deps: []const struct { []const u8, []const u8 } = &.{};
...@@ -858,14 +858,14 @@ pub fn computedPackageHash(f: *const Fetch) Package.Hash {...@@ -858,14 +858,14 @@ pub fn computedPackageHash(f: *const Fetch) Package.Hash {
858fn checkBuildFileExistence(f: *Fetch) RunError!void {858fn checkBuildFileExistence(f: *Fetch) RunError!void {
859 const io = f.job_queue.io;859 const io = f.job_queue.io;
860 const eb = &f.error_bundle;860 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, .{})) |_| {
862 f.has_build_zig = true;862 f.has_build_zig = true;
863 } else |err| switch (err) {863 } else |err| switch (err) {
864 error.FileNotFound => {},864 error.FileNotFound => {},
865 else => |e| {865 else => |e| {
866 try eb.addRootErrorMessage(.{866 try eb.addRootErrorMessage(.{
867 .msg = try eb.printString("unable to access '{f}{s}': {t}", .{867 .msg = try eb.printString("unable to access {f}/{s}: {t}", .{
868 f.package_root, Package.build_zig_basename, e,868 f.package_root, std.zig.build_zig_basename, e,
869 }),869 }),
870 });870 });
871 return error.FetchFailed;871 return error.FetchFailed;
...@@ -1781,7 +1781,7 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute...@@ -1781,7 +1781,7 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute
1781 )),1781 )),
1782 };1782 };
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))
1785 f.has_build_zig = true;1785 f.has_build_zig = true;
17861786
1787 const fs_path = try arena.dupe(u8, entry.path);1787 const fs_path = try arena.dupe(u8, entry.path);
lib/compiler/Maker/Package.zig+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;2const assert = std.debug.assert;
33
4pub const Fetch = @import("Package/Fetch.zig");4pub const Fetch = @import("Fetch.zig");
5pub const Manifest = @import("Package/Manifest.zig");5pub const Manifest = @import("Package/Manifest.zig");
66
7pub const Fingerprint = packed struct(u64) {7pub const Fingerprint = packed struct(u64) {
lib/compiler/Maker/ScannedConfig.zig+1-1
...@@ -9,7 +9,7 @@ const Graph = @import("Graph.zig");...@@ -9,7 +9,7 @@ const Graph = @import("Graph.zig");
99
10configuration: Configuration,10configuration: Configuration,
11top_level_steps: std.array_hash_map.String(Configuration.Step.Index),11top_level_steps: std.array_hash_map.String(Configuration.Step.Index),
12path: []const u8,12path: std.Build.Cache.Path,
1313
14pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {14pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
15 std.log.err("TODO also print paths", .{});15 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...@@ -584,7 +584,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi
584 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {584 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
585 return s.fail(585 return s.fail(
586 maker,586 maker,
587 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",587 "zig version mismatch build runner vs compiler: {q} vs {q}",
588 .{ builtin.zig_version_string, body },588 .{ builtin.zig_version_string, body },
589 );589 );
590 }590 }
lib/compiler/Maker/WebServer.zig+4-142
...@@ -582,8 +582,8 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim...@@ -582,8 +582,8 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
582 const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";582 const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
583583
584 const maker = ws.maker;584 const maker = ws.maker;
585 const gpa = maker.gpa;
586 const graph = maker.graph;585 const graph = maker.graph;
586 const gpa = maker.gpa;
587 const io = graph.io;587 const io = graph.io;
588588
589 const main_src_path: Cache.Path = .{589 const main_src_path: Cache.Path = .{
...@@ -622,151 +622,13 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim...@@ -622,151 +622,13 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
622 "--listen=-",622 "--listen=-",
623 });623 });
624624
625 var child = try std.process.spawn(io, .{625 return std.zig.buildExeSubprocess(gpa, io, .{
626 .argv = argv.items,626 .argv = argv.items,
627 .environ_map = &graph.environ_map,627 .cache_root = graph.global_cache_root,
628 .stdin = .pipe,628 .root_name = root_name,
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(.{
750 .arch_os_abi = arch_os_abi,629 .arch_os_abi = arch_os_abi,
751 .cpu_features = cpu_features,630 .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,
760 });631 });
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 };
770}632}
771633
772pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {634pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
lib/std/Build/Configuration.zig+1-1
...@@ -1881,7 +1881,7 @@ pub const PathDep = extern struct {...@@ -1881,7 +1881,7 @@ pub const PathDep = extern struct {
1881 _ = c;1881 _ = c;
1882 _ = arena;1882 _ = arena;
1883 _ = path;1883 _ = path;
1884 std.log.err("TODO Configuration.PathDep.toCachePath", .{});1884 if (true) @panic("TODO Configuration.PathDep.toCachePath");
1885 }1885 }
1886};1886};
18871887
lib/std/zig.zig+282-58
...@@ -7,6 +7,7 @@ const builtin = @import("builtin");...@@ -7,6 +7,7 @@ const builtin = @import("builtin");
7const std = @import("std.zig");7const std = @import("std.zig");
8const assert = std.debug.assert;8const assert = std.debug.assert;
9const mem = std.mem;9const mem = std.mem;
10const log = std.log;
10const Allocator = std.mem.Allocator;11const Allocator = std.mem.Allocator;
11const Io = std.Io;12const Io = std.Io;
12const Writer = std.Io.Writer;13const Writer = std.Io.Writer;
...@@ -721,7 +722,7 @@ pub fn parseTargetQueryOrReportFatalError(...@@ -721,7 +722,7 @@ pub fn parseTargetQueryOrReportFatalError(
721 for (diags.arch.?.allCpuModels()) |cpu| {722 for (diags.arch.?.allCpuModels()) |cpu| {
722 help_text.print(" {s}\n", .{cpu.name}) catch break :help;723 help_text.print(" {s}\n", .{cpu.name}) catch break :help;
723 }724 }
724 std.log.info("available CPUs for architecture '{s}':\n{s}", .{725 log.info("available CPUs for architecture '{s}':\n{s}", .{
725 @tagName(diags.arch.?), help_text.items,726 @tagName(diags.arch.?), help_text.items,
726 });727 });
727 }728 }
...@@ -734,7 +735,7 @@ pub fn parseTargetQueryOrReportFatalError(...@@ -734,7 +735,7 @@ pub fn parseTargetQueryOrReportFatalError(
734 for (diags.arch.?.allFeaturesList()) |feature| {735 for (diags.arch.?.allFeaturesList()) |feature| {
735 help_text.print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;736 help_text.print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
736 }737 }
737 std.log.info("available CPU features for architecture '{s}':\n{s}", .{738 log.info("available CPU features for architecture '{s}':\n{s}", .{
738 @tagName(diags.arch.?), help_text.items,739 @tagName(diags.arch.?), help_text.items,
739 });740 });
740 }741 }
...@@ -747,7 +748,7 @@ pub fn parseTargetQueryOrReportFatalError(...@@ -747,7 +748,7 @@ pub fn parseTargetQueryOrReportFatalError(
747 inline for (@typeInfo(std.Target.ObjectFormat).@"enum".field_names) |field_name| {748 inline for (@typeInfo(std.Target.ObjectFormat).@"enum".field_names) |field_name| {
748 help_text.print(" {s}\n", .{field_name}) catch break :help;749 help_text.print(" {s}\n", .{field_name}) catch break :help;
749 }750 }
750 std.log.info("available object formats:\n{s}", .{help_text.items});751 log.info("available object formats:\n{s}", .{help_text.items});
751 }752 }
752 std.process.fatal("unknown object format: '{s}'", .{opts.object_format.?});753 std.process.fatal("unknown object format: '{s}'", .{opts.object_format.?});
753 },754 },
...@@ -758,7 +759,7 @@ pub fn parseTargetQueryOrReportFatalError(...@@ -758,7 +759,7 @@ pub fn parseTargetQueryOrReportFatalError(
758 inline for (@typeInfo(std.Target.Cpu.Arch).@"enum".field_names) |field_name| {759 inline for (@typeInfo(std.Target.Cpu.Arch).@"enum".field_names) |field_name| {
759 help_text.print(" {s}\n", .{field_name}) catch break :help;760 help_text.print(" {s}\n", .{field_name}) catch break :help;
760 }761 }
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});
762 }763 }
763 std.process.fatal("unknown architecture: '{s}'", .{diags.unknown_architecture_name.?});764 std.process.fatal("unknown architecture: '{s}'", .{diags.unknown_architecture_name.?});
764 },765 },
...@@ -1182,73 +1183,88 @@ pub const ClangCliParam = struct {...@@ -1182,73 +1183,88 @@ pub const ClangCliParam = struct {
1182 }1183 }
1183};1184};
11841185
1186/// Deprecated
1185pub const AllocPrintCmdOptions = struct {1187pub const AllocPrintCmdOptions = struct {
1186 cwd: ?[]const u8 = null,1188 cwd: ?[]const u8 = null,
1187 parent_env: ?*const std.process.Environ.Map = null,1189 parent_env: ?*const std.process.Environ.Map = null,
1188 child_env: ?*const std.process.Environ.Map = null,1190 child_env: ?*const std.process.Environ.Map = null,
1189};1191};
11901192
1193/// Deprecated
1191pub fn allocPrintCmd(gpa: Allocator, argv: []const []const u8, options: AllocPrintCmdOptions) Allocator.Error![]u8 {1194pub 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
1227 var aw: Io.Writer.Allocating = .init(gpa);1195 var aw: Io.Writer.Allocating = .init(gpa);
1228 defer aw.deinit();1196 defer aw.deinit();
1229 const writer = &aw.writer;1197 SubprocessCommand.format(.{
1230 if (options.cwd) |path| {1198 .argv = argv,
1231 writer.print("cd {s} && ", .{path}) catch return error.OutOfMemory;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 }
1232 }1235 }
1233 if (options.child_env) |child_env| {1236 try writer.writeByte('"');
1234 for (child_env.keys(), child_env.values()) |key, value| {1237}
1235 if (options.parent_env) |parent_env| {1238
1236 if (parent_env.get(key)) |process_value| {1239pub const SubprocessCommand = struct {
1237 if (std.mem.eql(u8, value, process_value)) continue;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 }
1238 }1255 }
1256 try w.print("{s}=", .{key});
1257 try shellEscape(w, value, false);
1258 try w.writeByte(' ');
1239 }1259 }
1240 writer.print("{s}=", .{key}) catch return error.OutOfMemory;1260 }
1241 shell.escape(writer, value, false) catch return error.OutOfMemory;1261 try shellEscape(w, sc.argv[0], true);
1242 writer.writeByte(' ') catch return error.OutOfMemory;1262 for (sc.argv[1..]) |arg| {
1263 try w.writeByte(' ');
1264 try shellEscape(w, arg, false);
1243 }1265 }
1244 }1266 }
1245 shell.escape(writer, argv[0], true) catch return error.OutOfMemory;1267};
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}
12521268
1253/// Like `std.process.currentPathAlloc`, but also resolves the path with `Dir.path.resolve`. This1269/// Like `std.process.currentPathAlloc`, but also resolves the path with `Dir.path.resolve`. This
1254/// means the path has no repeated separators, no "." or ".." components, and no trailing separator.1270/// means the path has no repeated separators, no "." or ".." components, and no trailing separator.
...@@ -1391,7 +1407,7 @@ pub const Directories = struct {...@@ -1391,7 +1407,7 @@ pub const Directories = struct {
1391 },1407 },
1392 };1408 };
1393 }1409 }
1394 fn openUnresolved(1410 pub fn openUnresolved(
1395 arena: Allocator,1411 arena: Allocator,
1396 io: Io,1412 io: Io,
1397 cwd: []const u8,1413 cwd: []const u8,
...@@ -1609,6 +1625,214 @@ pub fn isUpDir(p: []const u8) bool {...@@ -1609,6 +1625,214 @@ pub fn isUpDir(p: []const u8) bool {
1609 return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == Dir.path.sep);1625 return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == Dir.path.sep);
1610}1626}
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
1612test {1836test {
1613 _ = Ast;1837 _ = Ast;
1614 _ = AstRlAnnotate;1838 _ = AstRlAnnotate;
lib/std/zig/Server.zig+1-1
...@@ -264,7 +264,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {...@@ -264,7 +264,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
264 try s.out.flush();264 try s.out.flush();
265}265}
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 {
268 var r: Reader = .fixed(body);268 var r: Reader = .fixed(body);
269 const hdr = r.takeStruct(OutMessage.ErrorBundle, .little) catch |err| switch (err) {269 const hdr = r.takeStruct(OutMessage.ErrorBundle, .little) catch |err| switch (err) {
270 error.EndOfStream => |e| return e,270 error.EndOfStream => |e| return e,