const builtin = @import("builtin"); const std = @import("std"); const Io = std.Io; const assert = std.debug.assert; const fmt = std.fmt; const mem = std.mem; const process = std.process; const File = std.Io.File; const Step = std.Build.Step; const Allocator = std.mem.Allocator; const fatal = std.process.fatal; const Writer = std.Io.Writer; const Color = std.zig.Color; const Configuration = std.Build.Configuration; pub const root = @import("@build"); pub const dependencies = @import("@dependencies"); pub const std_options: std.Options = .{ .side_channels_mitigations = .none, .http_disable_tls = true, }; pub fn main(init: process.Init.Minimal) !void { // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not // always the case. So, we do need a true gpa for some things. var debug_gpa_state: std.heap.DebugAllocator(.{ // We'd rather have `zig build` run faster than catch harmless leaks in // the user's build.zig script. .stack_trace_frames = 0, }) = .init; defer _ = debug_gpa_state.deinit(); const gpa = debug_gpa_state.allocator(); var threaded: std.Io.Threaded = .init(gpa, .{ .environ = init.environ, .argv0 = .init(init.args), }); defer threaded.deinit(); const io = threaded.io(); // ...but we'll back our arena by `std.heap.page_allocator` for efficiency. var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); const args = try init.args.toSlice(arena); // skip my own exe name var arg_idx: usize = 1; const zig_exe = expectArgOrFatal(args, &arg_idx, "--zig"); const zig_lib_dir = expectArgOrFatal(args, &arg_idx, "--zig-lib-dir"); const build_root = expectArgOrFatal(args, &arg_idx, "--build-root"); const local_cache_root = expectArgOrFatal(args, &arg_idx, "--local-cache"); const global_cache_root = expectArgOrFatal(args, &arg_idx, "--global-cache"); const cwd: Io.Dir = .cwd(); const zig_lib_directory: std.Build.Cache.Directory = .{ .path = zig_lib_dir, .handle = try cwd.openDir(io, zig_lib_dir, .{}), }; const build_root_directory: std.Build.Cache.Directory = .{ .path = build_root, .handle = try cwd.openDir(io, build_root, .{}), }; const local_cache_directory: std.Build.Cache.Directory = .{ .path = local_cache_root, .handle = try cwd.createDirPathOpen(io, local_cache_root, .{}), }; const global_cache_directory: std.Build.Cache.Directory = .{ .path = global_cache_root, .handle = try cwd.createDirPathOpen(io, global_cache_root, .{}), }; var graph: std.Build.Graph = .{ .io = io, .arena = arena, .cache = .{ .io = io, .gpa = gpa, .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}), .cwd = try process.currentPathAlloc(io, arena), }, .zig_exe = zig_exe, .environ_map = try init.environ.createMap(arena), .global_cache_root = global_cache_directory, .zig_lib_directory = zig_lib_directory, .host = .{ .query = .{}, .result = try std.zig.system.resolveTargetQuery(io, .{}), }, .time_report = false, }; graph.cache.addPrefix(.{ .path = null, .handle = cwd }); graph.cache.addPrefix(build_root_directory); graph.cache.addPrefix(local_cache_directory); graph.cache.addPrefix(global_cache_directory); graph.cache.hash.addBytes(builtin.zig_version_string); const builder = try std.Build.create( &graph, build_root_directory, local_cache_directory, dependencies.root_deps, ); var error_style: ErrorStyle = .verbose; var multiline_errors: MultilineErrors = .indent; var color: Color = .auto; if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| { if (std.meta.stringToEnum(ErrorStyle, str)) |style| { error_style = style; } } if (std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(&graph.environ_map)) |str| { if (std.meta.stringToEnum(MultilineErrors, str)) |style| { multiline_errors = style; } } while (nextArg(args, &arg_idx)) |arg| { if (mem.cutPrefix(u8, arg, "-D")) |option_contents| { if (option_contents.len == 0) fatalWithHint("expected option name after '-D'", .{}); if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { const option_name = option_contents[0..name_end]; const option_value = option_contents[name_end + 1 ..]; if (try builder.addUserInputOption(option_name, option_value)) fatal(" access the help menu with 'zig build -h'", .{}); } else { if (try builder.addUserInputFlag(option_contents)) fatal(" access the help menu with 'zig build -h'", .{}); } } else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| { graph.system_library_options.put(arena, name, .user_enabled) catch @panic("OOM"); } else if (mem.cutPrefix(u8, arg, "-fno-sys=")) |name| { graph.system_library_options.put(arena, name, .user_disabled) catch @panic("OOM"); } else if (mem.eql(u8, arg, "--release")) { graph.release_mode = .any; } else if (mem.cutPrefix(u8, arg, "--release=")) |text| { graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse { fatalWithHint("expected [off|any|fast|safe|small] in '{s}', found '{s}'", .{ arg, text, }); }; } else if (mem.eql(u8, arg, "--color")) { const next_arg = nextArg(args, &arg_idx) orelse fatalWithHint("expected [auto|on|off] after '{s}'", .{arg}); color = std.meta.stringToEnum(Color, next_arg) orelse { fatalWithHint("expected [auto|on|off] after '{s}', found '{s}'", .{ arg, next_arg, }); }; } else if (mem.eql(u8, arg, "--error-style")) { const next_arg = nextArg(args, &arg_idx) orelse fatalWithHint("expected style after '{s}'", .{arg}); error_style = std.meta.stringToEnum(ErrorStyle, next_arg) orelse { fatalWithHint("expected style after '{s}', found '{s}'", .{ arg, next_arg }); }; } else if (mem.eql(u8, arg, "--multiline-errors")) { const next_arg = nextArg(args, &arg_idx) orelse fatalWithHint("expected style after '{s}'", .{arg}); multiline_errors = std.meta.stringToEnum(MultilineErrors, next_arg) orelse { fatalWithHint("expected style after '{s}', found '{s}'", .{ arg, next_arg }); }; } else if (mem.eql(u8, arg, "--build-id")) { builder.build_id = .fast; } else if (mem.cutPrefix(u8, arg, "--build-id=")) |style| { builder.build_id = std.zig.BuildId.parse(style) catch |err| fatal("unable to parse --build-id style '{s}': {t}", .{ style, err }); } else if (mem.eql(u8, arg, "--debug-rt")) { graph.debug_compiler_runtime_libs = true; } else if (mem.eql(u8, arg, "--debug-compile-errors")) { builder.debug_compile_errors = true; } else if (mem.eql(u8, arg, "--debug-incremental")) { builder.debug_incremental = true; } else if (mem.eql(u8, arg, "--system")) { // The usage text shows another argument after this parameter // but it is handled by the parent process. The build runner // only sees this flag. graph.system_package_mode = true; } else { fatalWithHint("unrecognized argument: '{s}'", .{arg}); } } const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(&graph.environ_map); const CLICOLOR_FORCE = std.zig.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, }; try builder.runBuild(root); var wc: Configuration.Wip = .init(gpa); defer wc.deinit(); assert(try wc.addString("") == .empty); var stdout_buffer: [1024]u8 = undefined; var file_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); serialize(builder, &wc, &file_writer.interface) catch |err| switch (err) { error.WriteFailed => fatal("failed to write configuration output: {t}", .{file_writer.err.?}), error.OutOfMemory => |e| return e, }; // This executable is short-lived and run in Debug mode, so we'd rather // have `zig build` run faster than catch resource leaks in the user's // build.zig script (or, frankly, this configure runner), therefore we call // exit directly here rather than cleanExit. process.exit(0); } fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { const graph = b.graph; const arena = graph.arena; const gpa = wc.gpa; var module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty; defer module_map.deinit(gpa); // Starting from all top-level steps in `b`, traverse the entire step graph // and add all step dependencies implied by module graphs. const top_level_steps = b.top_level_steps.values(); // Index corresponds to `Configuration.steps` index. var step_map: std.AutoArrayHashMapUnmanaged(*Step, void) = .empty; try step_map.ensureUnusedCapacity(arena, top_level_steps.len); for (top_level_steps) |tls| { step_map.putAssumeCapacityNoClobber(&tls.step, {}); } { while (wc.steps.items.len < step_map.count()) { const step = step_map.keys()[wc.steps.items.len]; // Set up any implied dependencies for this step. It's important that we do this first, so // that the loop below discovers steps implied by the module graph. try createModuleDependenciesForStep(step); try step_map.ensureUnusedCapacity(arena, step.dependencies.items.len); for (step.dependencies.items) |other_step| { step_map.putAssumeCapacity(other_step, {}); } // Add and then de-duplicate dependencies. const deps = d: { const deps: Configuration.Deps = @enumFromInt(wc.extra.items.len); for (try wc.prepareDeps(step.dependencies.items.len), step.dependencies.items) |*dep, dep_step| dep.* = @intCast(step_map.getIndex(dep_step).?); break :d try wc.dedupeDeps(deps); }; try wc.steps.ensureTotalCapacity(gpa, step_map.entries.capacity); wc.steps.appendAssumeCapacity(.{ .name = try wc.addString(step.name), .deps = deps, .max_rss = .fromBytes(step.max_rss), .extra_index = switch (step.tag) { .top_level => e: { const top_level: *Step.TopLevel = @fieldParentPtr("step", step); break :e try wc.addExtra(@as(Configuration.Step.TopLevel, .{ .description = try wc.addString(top_level.description), })); }, .compile => e: { const c: *Step.Compile = @fieldParentPtr("step", step); const extra_index = try wc.addExtra(@as(Configuration.Step.Compile, .{ .flags = .{ .filters_len = c.filters.len != 0, .exec_cmd_args_len = if (c.exec_cmd_args) |a| a.len != 0 else false, .installed_headers_len = c.installed_headers.items.len != 0, .force_undefined_symbols_len = c.force_undefined_symbols.entries.len != 0, .verbose_link = c.verbose_link, .verbose_cc = c.verbose_cc, .rdynamic = c.rdynamic, .import_memory = c.import_memory, .export_memory = c.export_memory, .import_symbols = c.import_symbols, .import_table = c.import_table, .export_table = c.export_table, .shared_memory = c.shared_memory, .link_eh_frame_hdr = c.link_eh_frame_hdr, .link_emit_relocs = c.link_emit_relocs, .link_function_sections = c.link_function_sections, .link_data_sections = c.link_data_sections, .linker_dynamicbase = c.linker_dynamicbase, .link_z_notext = c.link_z_notext, .link_z_relro = c.link_z_relro, .link_z_lazy = c.link_z_lazy, .link_z_defs = c.link_z_defs, .headerpad_max_install_names = c.headerpad_max_install_names, .dead_strip_dylibs = c.dead_strip_dylibs, .force_load_objc = c.force_load_objc, .discard_local_symbols = c.discard_local_symbols, .mingw_unicode_entry_point = c.mingw_unicode_entry_point, }, .flags2 = .{ .pie = .init(c.pie), .formatted_panics = .init(c.formatted_panics), .bundle_compiler_rt = .init(c.bundle_compiler_rt), .bundle_ubsan_rt = .init(c.bundle_ubsan_rt), .each_lib_rpath = .init(c.each_lib_rpath), .link_gc_sections = .init(c.link_gc_sections), .linker_allow_shlib_undefined = .init(c.linker_allow_shlib_undefined), .linker_allow_undefined_version = .init(c.linker_allow_undefined_version), .linker_enable_new_dtags = .init(c.linker_enable_new_dtags), .dll_export_fns = .init(c.dll_export_fns), .use_llvm = .init(c.use_llvm), .use_lld = .init(c.use_lld), .use_new_linker = .init(c.use_new_linker), .allow_so_scripts = .init(c.allow_so_scripts), .sanitize_coverage_trace_pc_guard = .init(c.sanitize_coverage_trace_pc_guard), .linkage = .init(c.linkage), }, .flags3 = .{ .is_linking_libc = c.is_linking_libc, .is_linking_libcpp = c.is_linking_libcpp, .version = c.version != null, .compress_debug_sections = c.compress_debug_sections, .initial_memory = c.initial_memory != null, .max_memory = c.max_memory != null, .kind = c.kind, .global_base = c.global_base != null, .test_runner_mode = if (c.test_runner) |tr| switch (tr.mode) { .simple => .simple, .server => .server, } else .default, .wasi_exec_model = .init(c.wasi_exec_model), .win32_manifest = c.win32_manifest != null, .win32_module_definition = c.win32_module_definition != null, .zig_lib_dir = c.zig_lib_dir != null, .rc_includes = c.rc_includes, .image_base = c.image_base != null, .build_id = .init(c.build_id), .entry = switch (c.entry) { .default => .default, .disabled => .disabled, .enabled => .enabled, .symbol_name => .symbol_name, }, .lto = .init(c.lto), .subsystem = .init(c.subsystem), }, .flags4 = .{ .libc_file = c.libc_file != null, .link_z_common_page_size = c.link_z_common_page_size != null, .link_z_max_page_size = c.link_z_max_page_size != null, .pagezero_size = c.pagezero_size != null, .stack_size = c.stack_size != null, .headerpad_size = c.headerpad_size != null, .error_limit = c.error_limit != null, .install_name = c.install_name != null, .entitlements = c.entitlements != null, }, .root_module = try addModule(wc, &module_map, c.root_module), .root_name = try wc.addString(c.name), })); std.log.err("TODO serialize the trailing Compile step data", .{}); break :e extra_index; }, .install_artifact => e: { const ia: *Step.InstallArtifact = @fieldParentPtr("step", step); break :e try wc.addExtra(@as(Configuration.Step.InstallArtifact, .{ .flags = .{ .dylib_symlinks = ia.dylib_symlinks != null, }, .dest_dir = try addInstallDir(wc, ia.dest_dir), .dest_sub_path = try wc.addString(ia.dest_sub_path), .emitted_bin = try addOptionalLazyPath(wc, ia.emitted_bin), .implib_dir = try addInstallDir(wc, ia.implib_dir), .emitted_implib = try addOptionalLazyPath(wc, ia.emitted_implib), .pdb_dir = try addInstallDir(wc, ia.pdb_dir), .emitted_pdb = try addOptionalLazyPath(wc, ia.emitted_pdb), .h_dir = try addInstallDir(wc, ia.h_dir), .emitted_h = try addOptionalLazyPath(wc, ia.emitted_h), .artifact = stepIndex(&step_map, &ia.artifact.step), })); }, .install_file => @panic("TODO"), .install_dir => @panic("TODO"), .remove_dir => @panic("TODO"), .fail => @panic("TODO"), .fmt => @panic("TODO"), .translate_c => @panic("TODO"), .write_file => @panic("TODO"), .update_source_files => @panic("TODO"), .run => e: { const run: *Step.Run = @fieldParentPtr("step", step); const captured_stdout: Configuration.OptionalString = if (run.captured_stdout) |cs| .init(try wc.addString(cs.output.basename)) else .none; const captured_stderr: Configuration.OptionalString = if (run.captured_stderr) |cs| .init(try wc.addString(cs.output.basename)) else .none; const extra_index = try wc.addExtra(@as(Configuration.Step.Run, .{ .flags = .{ .disable_zig_progress = run.disable_zig_progress, .skip_foreign_checks = run.skip_foreign_checks, .failing_to_execute_foreign_is_an_error = run.failing_to_execute_foreign_is_an_error, .has_side_effects = run.has_side_effects, .test_runner_mode = run.test_runner_mode, .color = run.color, .stdio = switch (run.stdio) { .infer_from_args => .infer_from_args, .inherit => .inherit, .check => .check, .zig_test => .zig_test, }, .stdin = switch (run.stdin) { .none => .none, .bytes => .bytes, .lazy_path => .lazy_path, }, .stdout_trim_whitespace = if (run.captured_stdout) |cs| cs.trim_whitespace else .none, .stderr_trim_whitespace = if (run.captured_stderr) |cs| cs.trim_whitespace else .none, .stdio_limit = run.stdio_limit != .unlimited, .producer = run.producer != null, }, .file_inputs_len = @intCast(run.file_inputs.items.len), .args_len = @intCast(run.argv.items.len), .cwd = try addOptionalLazyPath(wc, run.cwd), .captured_stdout = captured_stdout, .captured_stderr = captured_stderr, })); std.log.err("TODO serialize the trailing Run step data", .{}); break :e extra_index; }, .check_file => @panic("TODO"), .check_object => @panic("TODO"), .config_header => @panic("TODO"), .objcopy => @panic("TODO"), .options => @panic("TODO"), }, }); } } try wc.unlazy_deps.ensureUnusedCapacity(gpa, graph.needed_lazy_dependencies.keys().len); for (graph.needed_lazy_dependencies.keys()) |k| { wc.unlazy_deps.appendAssumeCapacity(try wc.addString(k)); } try wc.write(writer, .{ .default_step = stepIndex(&step_map, b.default_step), }); } fn addModule( wc: *Configuration.Wip, module_map: *std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index), m: *std.Build.Module, ) !Configuration.Module.Index { if (module_map.get(m)) |index| return index; const gpa = wc.gpa; const import_table: Configuration.ImportTable = @enumFromInt(wc.extra.items.len); const import_table_extra_len = 1 + 2 * m.import_table.entries.len; try wc.extra.ensureUnusedCapacity(gpa, import_table_extra_len); wc.extra.items.len += import_table_extra_len; wc.extra.appendAssumeCapacity(@intCast(m.import_table.entries.len)); wc.extra.items[@intFromEnum(import_table)] = @intCast(m.import_table.entries.len); for ( m.import_table.keys(), @intFromEnum(import_table) + 1.., ) |mod_name, extra_index| { wc.extra.items[extra_index] = @intFromEnum(try wc.addString(mod_name)); } for ( m.import_table.values(), @intFromEnum(import_table) + 1 + m.import_table.entries.len.., ) |dep, extra_index| { // TODO module dependencies can be cyclic wc.extra.items[extra_index] = @intFromEnum(try addModule(wc, module_map, dep)); } const module_index: Configuration.Module.Index = @enumFromInt(try wc.addExtra(@as(Configuration.Module, .{ .flags = .{ .optimize = .init(m.optimize), .strip = .init(m.strip), .unwind_tables = .init(m.unwind_tables), .dwarf_format = .init(m.dwarf_format), .single_threaded = .init(m.strip), .stack_protector = .init(m.strip), .stack_check = .init(m.strip), .sanitize_c = .init(m.sanitize_c), .sanitize_thread = .init(m.strip), .fuzz = .init(m.strip), .code_model = m.code_model, .c_macros = m.c_macros.items.len != 0, .include_dirs = m.include_dirs.items.len != 0, .lib_paths = m.lib_paths.items.len != 0, .rpaths = m.rpaths.items.len != 0, .frameworks = m.frameworks.entries.len != 0, .link_objects = m.link_objects.items.len != 0, .export_symbol_names = m.export_symbol_names.len != 0, }, .flags2 = .{ .valgrind = .init(m.strip), .pic = .init(m.strip), .red_zone = .init(m.strip), .omit_frame_pointer = .init(m.strip), .error_tracing = .init(m.strip), .link_libc = .init(m.strip), .link_libcpp = .init(m.strip), .no_builtin = .init(m.strip), }, .owner = builderToPackage(m.owner), .root_source_file = try addOptionalLazyPath(wc, m.root_source_file), .import_table = import_table, .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target), }))); std.log.err("TODO serialize the trailing Module data", .{}); try module_map.putNoClobber(gpa, m, module_index); return module_index; } fn addOptionalResolvedTarget( wc: *Configuration.Wip, optional_resolved_target: ?std.Build.ResolvedTarget, ) !Configuration.ResolvedTarget.OptionalIndex { const resolved_target = optional_resolved_target orelse return .none; // TODO dedupe return @enumFromInt(try wc.addExtra(@as(Configuration.ResolvedTarget, .{ .query = try wc.addTargetQuery(resolved_target.query), .result = try wc.addTarget(resolved_target.result), }))); } fn addOptionalLazyPath(wc: *Configuration.Wip, lp: ?std.Build.LazyPath) !Configuration.OptionalLazyPath { return @enumFromInt(switch (lp orelse return .none) { .src_path => |src_path| i: { const sub_path = try wc.addString(src_path.sub_path); break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{ .flags = .{}, .owner = builderToPackage(src_path.owner), .sub_path = sub_path, })); }, .generated => |generated| i: { const sub_path = try wc.addString(generated.sub_path); break :i try wc.addExtra(@as(Configuration.LazyPath.Generated, .{ .flags = .{ .up = @intCast(generated.up) }, .sub_path = sub_path, })); }, .cwd_relative => |cwd_relative_sub_path| i: { const sub_path = try wc.addString(cwd_relative_sub_path); break :i try wc.addExtra(@as(Configuration.LazyPath.Relative, .{ .flags = .{ .base = .cwd }, .sub_path = sub_path, })); }, .dependency => |dependency| i: { const sub_path = try wc.addString(dependency.sub_path); break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{ .flags = .{}, .owner = builderToPackage(dependency.dependency.builder), .sub_path = sub_path, })); }, }); } fn builderToPackage(b: *std.Build) Configuration.Package.Index { _ = b; @panic("TODO"); } fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDir { switch (install_dir orelse return .none) { .prefix => return .prefix, .lib => return .lib, .bin => return .bin, .header => return .header, .custom => |sub_path| return .initCustom(try wc.addString(sub_path)), } } fn stepIndex(step_map: *const std.AutoArrayHashMapUnmanaged(*Step, void), step: *Step) Configuration.Step.Index { return @enumFromInt(step_map.getIndex(step).?); } /// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which /// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`. fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void { const root_module = if (step.cast(Step.Compile)) |cs| root: { break :root cs.root_module; } else return; // not a compile step so no module dependencies // Starting from `root_module`, discover all modules in this graph. const modules = root_module.getGraph().modules; // For each of those modules, set up the implied step dependencies. for (modules) |mod| { if (mod.root_source_file) |lp| lp.addStepDependencies(step); for (mod.include_dirs.items) |include_dir| switch (include_dir) { .path, .path_system, .path_after, .framework_path, .framework_path_system, .embed_path, => |lp| lp.addStepDependencies(step), .other_step => |other| { other.getEmittedIncludeTree().addStepDependencies(step); step.dependOn(&other.step); }, .config_header_step => |other| step.dependOn(&other.step), }; for (mod.lib_paths.items) |lp| lp.addStepDependencies(step); for (mod.rpaths.items) |rpath| switch (rpath) { .lazy_path => |lp| lp.addStepDependencies(step), .special => {}, }; for (mod.link_objects.items) |link_object| switch (link_object) { .static_path, .assembly_file, => |lp| lp.addStepDependencies(step), .other_step => |other| step.dependOn(&other.step), .system_lib => {}, .c_source_file => |source| source.file.addStepDependencies(step), .c_source_files => |source_files| source_files.root.addStepDependencies(step), .win32_resource_file => |rc_source| { rc_source.file.addStepDependencies(step); for (rc_source.include_paths) |lp| lp.addStepDependencies(step); }, }; } } fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 { if (idx.* >= args.len) return null; defer idx.* += 1; return args[idx.*]; } fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 { return nextArg(args, idx) orelse { fatal("expected argument after {q}\n access the help menu with \"zig build -h\"", .{ args[idx.* - 1], }); }; } fn expectArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, first: []const u8) []const u8 { const next_arg = nextArg(args, index_ptr) orelse fatal("missing {q} argument", .{first}); if (!mem.eql(u8, first, next_arg)) fatal("expected {q} instead of {q}", .{ first, next_arg }); const arg = nextArg(args, index_ptr) orelse fatal("expected argument after {q}", .{first}); return arg; } const ErrorStyle = enum { verbose, minimal, verbose_clear, minimal_clear, fn verboseContext(s: ErrorStyle) bool { return switch (s) { .verbose, .verbose_clear => true, .minimal, .minimal_clear => false, }; } fn clearOnUpdate(s: ErrorStyle) bool { return switch (s) { .verbose, .minimal => false, .verbose_clear, .minimal_clear => true, }; } }; const MultilineErrors = enum { indent, newline, none }; const Summary = enum { all, new, failures, line, none }; fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn { fatal(f ++ "\n access the help menu with \"zig build -h\"", args); }