feature. See also
. The project being documented here (as the example) is the Zig library itself.
Maker.main
pub fn main(init: process.Init.Minimal) !void
File
Code
pub fn main(init: process.Init.Minimal) !void {
// * `--watch` mode
// * `--webui` mode
// * `--fuzz` mode
// * A project that has a large, complex build graph.
const gpa = if (use_safe_allocator) safe_allocator_instance.allocator() else std.heap.smp_allocator;
defer if (use_safe_allocator) {
_ = safe_allocator_instance.deinit();
};
var threaded: std.Io.Threaded = .init(gpa, .{
.environ = init.environ,
.argv0 = .init(init.args),
});
defer threaded.deinit();
const io = threaded.io();
var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
defer arena_instance.deinit();
defer if (debugMakerLeaks()) log.debug("used {Bi} of arena", .{arena_instance.queryCapacity()});
const arena = arena_instance.allocator();
const args = try init.args.toSlice(arena);
var arg_i: usize = 1;
const cmd_name = nextArgOrFatal(args, &arg_i);
const zig_lib_arg = prefixedArgOrFatal(args, &arg_i, "--zig-lib=");
const zig_exe_arg = prefixedArgOrFatal(args, &arg_i, "--zig=");
const global_cache_arg = prefixedArgOrFatal(args, &arg_i, "--global-cache=");
const seed_arg = prefixedArgOrFatal(args, &arg_i, "--seed=");
const cwd: Dir = .cwd();
const zig_lib_directory: Cache.Directory = if (std.mem.eql(u8, zig_lib_arg, ".")) .cwd() else .{
.path = zig_lib_arg,
.handle = try cwd.openDir(io, zig_lib_arg, .{}),
};
const global_cache_directory: Cache.Directory = if (std.mem.eql(u8, global_cache_arg, ".")) .cwd() else .{
.path = global_cache_arg,
.handle = try cwd.createDirPathOpen(io, global_cache_arg, .{}),
};
var graph: Graph = .{
.io = io,
.arena = arena,
.cache = undefined,
.zig_exe = zig_exe_arg,
.environ_map = try init.environ.createMap(arena),
.global_cache_root = global_cache_directory,
.local_cache_root = undefined,
.zig_lib_directory = zig_lib_directory,
.build_root_directory = undefined,
.random_seed = parseRandomSeed(seed_arg),
};
const cmd = stringToEnum(enum { libc, init, fetch, build }, cmd_name) orelse
fatal("bad command name: {q}", .{cmd_name});
switch (cmd) {
.libc => return cmdLibC(gpa, &graph, args[arg_i..]),
.init => return cmdInit(gpa, &graph, args[arg_i..]),
.fetch => return cmdFetch(gpa, &graph, args[arg_i..]),
.build => {},
}
var step_names: std.ArrayList([]const u8) = .empty;
var help_menu = false;
var steps_menu = false;
var print_configuration: PrintConfiguration = .none;
var override_install_prefix: ?[]const u8 = null;
var override_lib_dir: ?[]const u8 = null;
var override_bin_dir: ?[]const u8 = null;
var override_include_dir: ?[]const u8 = null;
var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(&graph.environ_map);
var override_pkg_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_PKG_DIR.get(&graph.environ_map);
var error_style: ErrorStyle = .verbose;
var multiline_errors: MultilineErrors = .indent;
var summary: ?Summary = null;
var max_rss: u64 = 0;
var skip_oom_steps = false;
var test_timeout_ns: ?u64 = null;
var color: Color = .settingFromEnvironment(&graph.environ_map);
var watch = false;
var fuzz: ?Fuzz.Mode = null;
var debounce_interval_ms: u16 = 50;
var webui_listen: ?Io.net.IpAddress = null;
var debug_pkg_config = false;
var run_args: ?[]const []const u8 = null;
var build_file: ?[]const u8 = null;
var configure_argv: std.ArrayList([]const u8) = .empty;
var cached_passthru_configure: std.ArrayList(u32) = .empty;
var forks: std.ArrayList(Fork) = .empty;
var system_pkg_dir_path: ?[]const u8 = null;
var fetch_only = false;
var fetch_mode: Fetch.JobQueue.Mode = .needed;
var debug_target: ?[]const u8 = null;
var cache_poison: std.Build.Graph.CachePoison = .pure;
if (EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| {
if (stringToEnum(ErrorStyle, str)) |style| {
error_style = style;
}
}
if (EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(&graph.environ_map)) |str| {
if (stringToEnum(MultilineErrors, str)) |style| {
multiline_errors = style;
}
}
try configure_argv.ensureUnusedCapacity(arena, 16);
try cached_passthru_configure.ensureUnusedCapacity(arena, 16);
_ = configure_argv.addOneAssumeCapacity();
configure_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--zig", graph.zig_exe };
configure_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--build-root", undefined };
const conf_argv_index_build_root = configure_argv.items.len - 1;
while (nextArg(args, &arg_i)) |arg| {
if (mem.startsWith(u8, arg, "-")) {
try configure_argv.ensureUnusedCapacity(arena, 2);
if (mem.startsWith(u8, arg, "-D") or
mem.startsWith(u8, arg, "-fsys=") or
mem.startsWith(u8, arg, "-fno-sys=") or
mem.startsWith(u8, arg, "--release=") or
mem.eql(u8, arg, "--release"))
{
try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len));
configure_argv.appendAssumeCapacity(arg);
} else if (mem.eql(u8, arg, "--system")) {
system_pkg_dir_path = nextArgOrFatal(args, &arg_i);
try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len));
configure_argv.appendAssumeCapacity(arg);
} else if (mem.cutPrefix(u8, arg, "--color=")) |rest| {
color = stringToEnum(Color, rest) orelse
fatalWithHint("expected --color=[auto|on|off]; found {q}", .{arg});
try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len));
configure_argv.appendAssumeCapacity(arg);
} else if (mem.eql(u8, arg, "--color")) {
const next_arg = nextArgOrFatal(args, &arg_i);
color = stringToEnum(Color, next_arg) orelse
fatalWithHint("expected [auto|on|off] found {q}", .{next_arg});
try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len));
configure_argv.appendAssumeCapacity(try arena.print("--color={t}", .{color}));
} else if (mem.eql(u8, arg, "--cache-poison")) {
cache_poison = .poisoned;
configure_argv.appendAssumeCapacity("--cache-poison=poisoned");
} else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| {
// potentially get false positive cache hits for misspellings.
cache_poison = stringToEnum(std.Build.Graph.CachePoison, rest) orelse
fatalWithHint("expected --cache-poison=[pure|poisoned|disallowed|ignored]; found: {s}", .{arg});
if (cache_poison != .pure) configure_argv.appendAssumeCapacity(arg);
} else if (mem.eql(u8, arg, "--verbose")) {
// does not go into the cache hash.
configure_argv.appendAssumeCapacity(arg);
graph.verbose = true;
} else if (mem.eql(u8, arg, "--search-prefix")) {
const prefix = nextArgOrFatal(args, &arg_i);
// the cache and configurer must set the poison bit when
// choosing to observe it.
configure_argv.addManyAsArrayAssumeCapacity(2).* = .{ arg, prefix };
try graph.search_prefixes.append(arena, prefix);
} else if (mem.eql(u8, arg, "--cache-dir")) {
override_local_cache_dir = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--pkg-dir")) {
override_pkg_dir = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--fetch")) {
fetch_only = true;
} else if (mem.cutPrefix(u8, arg, "--fetch=")) |rest| {
fetch_only = true;
fetch_mode = stringToEnum(Fetch.JobQueue.Mode, rest) orelse
fatal("expected [needed|all] after \"--fetch=\", found {q}", .{rest});
} else if (mem.cutPrefix(u8, arg, "--fork=")) |rest| {
try forks.append(arena, .init(rest));
} else if (mem.eql(u8, arg, "--fork")) {
try forks.append(arena, .init(nextArgOrFatal(args, &arg_i)));
} else if (mem.startsWith(u8, arg, "--zig-lib=")) {
fatal("--zig-lib= argument is special and must be first", .{});
} else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
help_menu = true;
} else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) {
steps_menu = true;
} else if (mem.eql(u8, arg, "--print-configuration")) {
print_configuration = .zon;
} else if (mem.eql(u8, arg, "--print-configuration-path")) {
print_configuration = .path;
} else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) {
override_install_prefix = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--build-file")) {
build_file = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--prefix-lib-dir")) {
override_lib_dir = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--prefix-exe-dir")) {
override_bin_dir = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--prefix-include-dir")) {
override_include_dir = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--sysroot")) {
graph.sysroot = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--maxrss")) {
const max_rss_text = nextArgOrFatal(args, &arg_i);
max_rss = std.fmt.parseIntSizeSuffix(max_rss_text, 10) catch |err|
fatal("invalid byte size {q}: {t}", .{ max_rss_text, err });
} else if (mem.eql(u8, arg, "--skip-oom-steps")) {
skip_oom_steps = true;
} else if (mem.eql(u8, arg, "--test-timeout")) {
const units: []const struct { []const u8, u64 } = &.{
.{ "ns", 1 },
.{ "nanosecond", 1 },
.{ "us", std.time.ns_per_us },
.{ "microsecond", std.time.ns_per_us },
.{ "ms", std.time.ns_per_ms },
.{ "millisecond", std.time.ns_per_ms },
.{ "s", std.time.ns_per_s },
.{ "second", std.time.ns_per_s },
.{ "m", std.time.ns_per_min },
.{ "minute", std.time.ns_per_min },
.{ "h", std.time.ns_per_hour },
.{ "hour", std.time.ns_per_hour },
};
const timeout_str = nextArgOrFatal(args, &arg_i);
const num_end_idx = std.mem.findLastNone(u8, timeout_str, "abcdefghijklmnopqrstuvwxyz") orelse fatal(
"invalid timeout {q}: expected unit (ns, us, ms, s, m, h)",
.{timeout_str},
);
const num_str = timeout_str[0 .. num_end_idx + 1];
const unit_str = timeout_str[num_end_idx + 1 ..];
const unit_factor: f64 = for (units) |unit_and_factor| {
if (std.mem.eql(u8, unit_str, unit_and_factor[0])) {
break @floatFromInt(unit_and_factor[1]);
}
} else fatal(
"invalid timeout {q}: invalid unit {q} (expected ns, us, ms, s, m, h)",
.{ timeout_str, unit_str },
);
const num_parsed = std.fmt.parseFloat(f64, num_str) catch |err| fatal(
"invalid timeout {q}: invalid number {q} ({t})",
.{ timeout_str, num_str, err },
);
test_timeout_ns = std.math.lossyCast(u64, unit_factor * num_parsed);
} else if (mem.eql(u8, arg, "--libc")) {
graph.libc_file = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--error-style")) {
const next_arg = nextArg(args, &arg_i) orelse
fatalWithHint("expected style after {q}", .{arg});
error_style = stringToEnum(ErrorStyle, next_arg) orelse {
fatalWithHint("expected style after {q}, found {q}", .{ arg, next_arg });
};
} else if (mem.eql(u8, arg, "--multiline-errors")) {
const next_arg = nextArg(args, &arg_i) orelse
fatalWithHint("expected style after {q}", .{arg});
multiline_errors = stringToEnum(MultilineErrors, next_arg) orelse {
fatalWithHint("expected style after {q}, found {q}", .{ arg, next_arg });
};
} else if (mem.eql(u8, arg, "--summary")) {
const next_arg = nextArg(args, &arg_i) orelse
fatalWithHint("expected [all|new|failures|line|none] after {q}", .{arg});
summary = stringToEnum(Summary, next_arg) orelse {
fatalWithHint("expected [all|new|failures|line|none] after {q}, found {q}", .{
arg, next_arg,
});
};
} else if (mem.cutPrefix(u8, arg, "--seed=")) |rest| {
graph.random_seed = parseRandomSeed(rest);
} else if (mem.eql(u8, arg, "--build-id")) {
graph.build_id = .fast;
} else if (mem.cutPrefix(u8, arg, "--build-id=")) |style| {
graph.build_id = std.zig.BuildId.parse(style) catch |err|
fatal("unable to parse --build-id style {q}: {t}", .{ style, err });
} else if (mem.eql(u8, arg, "--debounce")) {
const next_arg = nextArg(args, &arg_i) orelse
fatalWithHint("expected u16 after {q}", .{arg});
debounce_interval_ms = std.fmt.parseUnsigned(u16, next_arg, 0) catch |err| {
fatal("unable to parse debounce interval {q} as unsigned 16-bit integer: {t}", .{
next_arg, err,
});
};
} else if (mem.eql(u8, arg, "--webui")) {
if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) };
} else if (mem.startsWith(u8, arg, "--webui=")) {
const addr_str = arg["--webui=".len..];
if (std.mem.eql(u8, addr_str, "-")) fatal("web interface cannot listen on stdio", .{});
webui_listen = Io.net.IpAddress.parseLiteral(addr_str) catch |err| {
fatal("invalid web UI address {q}: {t}", .{ addr_str, err });
};
} else if (mem.eql(u8, arg, "--debug-target")) {
debug_target = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--debug-log")) {
try graph.debug_log_scopes.append(arena, nextArgOrFatal(args, &arg_i));
} else if (mem.eql(u8, arg, "--debug-compile-errors")) {
graph.debug_compile_errors = true;
} else if (mem.eql(u8, arg, "--debug-incremental")) {
graph.debug_incremental = true;
} else if (mem.eql(u8, arg, "--debug-pkg-config")) {
debug_pkg_config = true;
} else if (mem.eql(u8, arg, "--debug-rt")) {
graph.debug_compiler_runtime_libs = .Debug;
} else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| {
graph.debug_compiler_runtime_libs = stringToEnum(std.lang.OptimizeMode, rest) orelse
fatal("unrecognized optimization mode: {s}", .{rest});
} else if (is_debug_mode and mem.eql(u8, arg, "--debug-maker-leaks")) {
debug_maker_leaks = true;
} else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {
graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--verbose-air")) {
graph.verbose_air = true;
} else if (mem.eql(u8, arg, "--verbose-cc")) {
graph.verbose_cc = true;
} else if (mem.eql(u8, arg, "--verbose-link")) {
graph.verbose_link = true;
} else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
graph.verbose_llvm_ir = true;
} else if (mem.eql(u8, arg, "--watch")) {
watch = true;
} else if (mem.eql(u8, arg, "--time-report")) {
graph.time_report = true;
if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) };
} else if (mem.eql(u8, arg, "--fuzz")) {
fuzz = .{ .forever = undefined };
graph.fuzzing = true;
if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) };
} else if (mem.startsWith(u8, arg, "--fuzz=")) {
const value = arg["--fuzz=".len..];
if (value.len == 0) fatal("missing argument to --fuzz", .{});
const unit: u8 = value[value.len - 1];
const digits = switch (unit) {
'0'...'9' => value,
'K', 'M', 'G' => value[0 .. value.len - 1],
else => fatal(
"invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]",
.{},
),
};
const amount = std.fmt.parseInt(u64, digits, 10) catch {
fatal(
"invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]",
.{},
);
};
const normalized_amount = std.math.mul(u64, amount, switch (unit) {
else => unreachable,
'0'...'9' => 1,
'K' => 1000,
'M' => 1_000_000,
'G' => 1_000_000_000,
}) catch fatal("fuzzing limit amount overflows u64", .{});
fuzz = .{
.limit = .{
.amount = normalized_amount,
},
};
graph.fuzzing = true;
} else if (mem.eql(u8, arg, "-fincremental")) {
graph.incremental = true;
} else if (mem.eql(u8, arg, "-fno-incremental")) {
graph.incremental = false;
} else if (mem.eql(u8, arg, "-fwine")) {
graph.enable_wine = true;
} else if (mem.eql(u8, arg, "-fno-wine")) {
graph.enable_wine = false;
} else if (mem.eql(u8, arg, "-fqemu")) {
graph.enable_qemu = true;
} else if (mem.eql(u8, arg, "-fno-qemu")) {
graph.enable_qemu = false;
} else if (mem.eql(u8, arg, "-fwasmtime")) {
graph.enable_wasmtime = true;
} else if (mem.eql(u8, arg, "-fno-wasmtime")) {
graph.enable_wasmtime = false;
} else if (mem.eql(u8, arg, "-frosetta")) {
graph.enable_rosetta = true;
} else if (mem.eql(u8, arg, "-fno-rosetta")) {
graph.enable_rosetta = false;
} else if (mem.eql(u8, arg, "-fdarling")) {
graph.enable_darling = true;
} else if (mem.eql(u8, arg, "-fno-darling")) {
graph.enable_darling = false;
} else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
graph.allow_so_scripts = true;
} else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
graph.allow_so_scripts = false;
} else if (mem.eql(u8, arg, "-freference-trace")) {
graph.reference_trace = 256;
} else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {
graph.reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err|
fatal("unable to parse reference_trace count {q}: {t}", .{ num, err });
} else if (mem.eql(u8, arg, "-fno-reference-trace")) {
graph.reference_trace = null;
} else if (mem.eql(u8, arg, "--error-limit")) {
const next_arg = nextArgOrFatal(args, &arg_i);
graph.error_limit = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err|
fatal("unable to parse error limit {q}: {t}", .{ next_arg, err });
} else if (mem.cutPrefix(u8, arg, "-j")) |text| {
const n = std.fmt.parseUnsigned(u32, text, 10) catch |err|
fatal("unable to parse jobs count {q}: {t}", .{ text, err });
if (n < 1) fatal("number of jobs must be at least 1", .{});
threaded.setAsyncLimit(.limited(n));
graph.max_jobs = n;
} else if (mem.eql(u8, arg, "--")) {
run_args = argsRest(args, arg_i);
break;
} else {
fatalWithHint("unrecognized argument: {s}", .{arg});
}
} else {
try step_names.append(arena, arg);
}
}
const early_exit_mode = fetch_only or help_menu or steps_menu or print_configuration != .none;
const server_mode = !early_exit_mode and (watch or webui_listen != null or fuzz != null);
process.raiseFileDescriptorLimit();
const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err|
fatal("resolving current directory path failed: {t}", .{err});
var build_root = try findBuildRoot(arena, io, .{
.cwd_path = cwd_path,
.build_file = build_file,
});
defer build_root.deinit(io);
graph.build_root_directory = build_root.directory;
graph.local_cache_root = if (override_local_cache_dir) |unresolved_path| std.zig.Directories.openUnresolved(
arena,
io,
cwd_path,
unresolved_path,
.@"local cache",
) else .{
.path = try build_root.directory.join(arena, &.{default_local_zig_cache_basename}),
.handle = try build_root.directory.handle.createDirPathOpen(io, default_local_zig_cache_basename, .{}),
};
graph.cache = .{
.io = io,
.gpa = gpa,
.manifest_dir = try graph.local_cache_root.handle.createDirPathOpen(io, "h", .{}),
.cwd = cwd_path,
};
graph.cache.addPrefix(.{ .path = null, .handle = cwd });
graph.cache.addPrefix(zig_lib_directory);
graph.cache.addPrefix(graph.local_cache_root);
graph.cache.addPrefix(global_cache_directory);
graph.cache.addPrefix(graph.build_root_directory);
comptime assert(0 == @backingInt(std.zig.Server.Message.PathPrefix.cwd));
comptime assert(1 == @backingInt(std.zig.Server.Message.PathPrefix.zig_lib));
comptime assert(2 == @backingInt(std.zig.Server.Message.PathPrefix.local_cache));
comptime assert(3 == @backingInt(std.zig.Server.Message.PathPrefix.global_cache));
graph.cache.hash.addBytes(builtin.zig_version_string);
const NO_COLOR = EnvVar.NO_COLOR.isSet(&graph.environ_map);
const CLICOLOR_FORCE = EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map);
graph.stderr_mode = switch (color) {
.auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE),
.on => .escape_codes,
.off => .no_color,
};
const pkg_root: Path = if (override_pkg_dir) |p|
.initCwd(p)
else if (system_pkg_dir_path) |p|
.initCwd(p)
else
.{
.root_dir = build_root.directory,
.sub_path = "zig-pkg",
};
const main_progress_node = std.Progress.start(io, .{
.disable_printing = (graph.stderr_mode.? == .no_color),
});
defer main_progress_node.end();
const install_prefix_path: Path = if (graph.environ_map.get("DESTDIR")) |dest_dir| .{
.root_dir = .cwd(),
.sub_path = try Dir.path.join(arena, &.{ dest_dir, override_install_prefix orelse "/usr" }),
} else if (override_install_prefix) |cwd_relative| .{
.root_dir = .cwd(),
.sub_path = cwd_relative,
} else .{
.root_dir = graph.build_root_directory,
.sub_path = "zig-out",
};
const install_lib_path: Path = if (override_lib_dir) |cwd_relative| .{
.root_dir = .cwd(),
.sub_path = cwd_relative,
} else try install_prefix_path.join(arena, "lib");
const install_bin_path: Path = if (override_bin_dir) |cwd_relative| .{
.root_dir = .cwd(),
.sub_path = cwd_relative,
} else try install_prefix_path.join(arena, "bin");
const install_include_path: Path = if (override_include_dir) |cwd_relative| .{
.root_dir = .cwd(),
.sub_path = cwd_relative,
} else try install_prefix_path.join(arena, "include");
const now = Io.Clock.Timestamp.now(io, .awake);
var web_server_allocation: AvoidableWebServer = undefined;
const web_server: ?*AvoidableWebServer = if (webui_listen) |listen_address| ws: {
if (builtin.single_threaded) fatal("--webui is not yet supported on single-threaded hosts", .{});
web_server_allocation = .init(.{
.graph = &graph,
.root_prog_node = main_progress_node,
.listen_address = listen_address,
.base_timestamp = now,
});
web_server_allocation.start() catch |err| fatal("failed to start web server: {t}", .{err});
break :ws &web_server_allocation;
} else null;
while (true) {
// to request a rebuild. If it returns error.FailedButCacheIntact
// we can even still do file system watching and automatically
// rebuild on source changes.
if (configure(&graph, .{
.configure_argv = configure_argv.items,
.conf_argv_index_build_root = conf_argv_index_build_root,
.cached_passthru_configure = cached_passthru_configure.items,
.cache_poison = cache_poison,
.pkg_root = pkg_root,
.build_root = build_root,
.cwd_path = cwd_path,
.color = color,
.debug_target = debug_target,
.parent_progress_node = main_progress_node,
.fetch_mode = fetch_mode,
.system_pkg_dir_path = system_pkg_dir_path,
.fetch_only = fetch_only,
.print_configuration = print_configuration,
.forks = forks.items,
})) |scanned_config| {
if (help_menu) {
scanned_config.printUsage(&graph, initStdoutWriter(io)) catch |err| switch (err) {
error.WriteFailed => return stdout_writer_allocation.err.?,
else => |e| return e,
};
try stdout_writer_allocation.flush();
return cleanExit(io, &scanned_config);
} else if (steps_menu) {
scanned_config.printSteps(&graph, initStdoutWriter(io)) catch |err| switch (err) {
error.WriteFailed => return stdout_writer_allocation.err.?,
else => |e| return e,
};
try stdout_writer_allocation.flush();
return cleanExit(io, &scanned_config);
} else switch (print_configuration) {
.none => {},
.zon => {
scanned_config.print(initStdoutWriter(io)) catch return stdout_writer_allocation.err.?;
try stdout_writer_allocation.flush();
return cleanExit(io, &scanned_config);
},
.path => unreachable,
}
var maker: Maker = .{
.gpa = gpa,
.graph = &graph,
.scanned_config = &scanned_config,
.install_paths = .{
.prefix = install_prefix_path,
.lib = install_lib_path,
.bin = install_bin_path,
.include = install_include_path,
},
.steps = try arena.alloc(Step, scanned_config.configuration.steps.len),
.generated_files = try arena.alloc(Path, scanned_config.configuration.generated_files_len),
.run_args = run_args,
.available_rss = max_rss,
.max_rss_is_default = false,
.max_rss_mutex = .init,
.skip_oom_steps = skip_oom_steps,
.unit_test_timeout_ns = test_timeout_ns,
.watch = watch,
.web_server = web_server,
.memory_blocked_steps = .empty,
.step_stack = .empty,
.pkg_config = .{ .debug = debug_pkg_config },
.error_style = error_style,
.multiline_errors = multiline_errors,
.summary = summary orelse if (watch or webui_listen != null) .new else .failures,
};
defer {
maker.memory_blocked_steps.deinit(gpa);
maker.step_stack.deinit(gpa);
}
if (maker.available_rss == 0) {
maker.available_rss = process.totalSystemMemory() catch std.math.maxInt(u64);
maker.max_rss_is_default = true;
}
maker.prepare(step_names.items) catch |err| switch (err) {
error.DependencyLoopDetected, error.InsufficientMemory => {
// and handle InsufficientMemory as error.AlreadyReported
_ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
process.exit(1);
},
else => |e| return e,
};
var w: Watch = w: {
if (!watch) break :w undefined;
if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{native_os});
break :w try .init(&maker);
};
if (web_server) |ws| try ws.updateConfiguration(&maker);
rebuild: while (true) : (if (maker.error_style.clearOnUpdate()) {
const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
defer io.unlockStderr();
stderr.file_writer.interface.writeAll("\x1B[2J\x1B[3J\x1B[H") catch |err| switch (err) {
error.WriteFailed => return stderr.file_writer.err.?,
};
}) {
if (web_server) |ws| ws.startBuild();
try maker.makeStepNames(step_names.items, main_progress_node, fuzz);
if (web_server) |ws| {
if (fuzz) |mode| if (mode != .forever) fatal(
"error: limited fuzzing is not implemented yet for --webui",
.{},
);
ws.finishBuild(.{ .fuzz = fuzz != null });
}
if (web_server) |ws| {
const c = &scanned_config.configuration;
assert(!watch);
while (true) switch (try ws.wait()) {
.rebuild => {
for (maker.step_stack.keys()) |step_index| {
const step = maker.stepByIndex(step_index);
step.state = .precheck_done;
const deps = step_index.ptr(c).deps.slice(c);
step.pending_deps = @intCast(deps.len);
step.reset(&maker);
}
continue :rebuild;
},
};
}
if (!maker.watch) return;
if (!Watch.have_impl) unreachable;
try w.update(maker.step_stack.keys());
// until the buffer is empty. Then wait for a debounce interval, resetting
// if any more events come in. After the debounce interval has passed,
// trigger a rebuild on all steps with modified inputs, as well as their
// recursive dependants.
var caption_buf: [std.Progress.Node.max_name_len]u8 = undefined;
const caption = std.fmt.bufPrint(&caption_buf, "watching {d} directories, {d} processes", .{
w.dir_count, countSubProcesses(&maker),
}) catch &caption_buf;
var debouncing_node = main_progress_node.start(caption, 0);
var in_debounce = false;
while (true) switch (try w.wait(if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) {
.timeout => {
assert(in_debounce);
debouncing_node.end();
markFailedStepsDirty(&maker);
continue :rebuild;
},
.dirty => if (!in_debounce) {
in_debounce = true;
debouncing_node.end();
debouncing_node = main_progress_node.start("Debouncing (Change Detected)", 0);
},
.clean => {},
};
}
} else |err| {
const can_fs_watch = switch (err) {
error.AlreadyReported => false,
error.FailedButCacheIntact => true,
else => |e| w: {
log.err("configuration failed: {t}", .{e});
break :w false;
},
};
if (!server_mode) {
_ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
process.exit(1);
}
if (watch and can_fs_watch) {
fatal("(zig build system) TODO set up fs watching even when build.zig compilation fails", .{});
} else {
fatal("(zig build system) TODO stay running and wait for user to request rebuild even when build.zig compilation fails", .{});
}
}
}
}